You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kie.apache.org by tz...@apache.org on 2025/04/22 05:03:27 UTC

(incubator-kie-drools) branch main updated: [kie-issues#1923] includes in kmodule.xml doesn't work for Quarkus and Spring Boot projects (#6307)

This is an automated email from the ASF dual-hosted git repository.

tzimanyi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/incubator-kie-drools.git


The following commit(s) were added to refs/heads/main by this push:
     new a382abc5c9 [kie-issues#1923] includes in kmodule.xml doesn't work for Quarkus and Spring Boot projects (#6307)
a382abc5c9 is described below

commit a382abc5c9ca4199edc69fbe1d00693dd4d167dc
Author: Tibor Zimányi <ti...@ibm.com>
AuthorDate: Tue Apr 22 07:03:23 2025 +0200

    [kie-issues#1923] includes in kmodule.xml doesn't work for Quarkus and Spring Boot projects (#6307)
---
 .../compiler/kie/builder/impl/KieBuilderImpl.java  | 27 +++++++
 .../codegen/project/KieSessionModelBuilder.java    | 62 ++++++++++++++--
 .../codegen/project/ProjectRuntimeGenerator.java   |  3 +-
 .../rules/ProjectRuntimeJavaTemplate.java          |  2 +-
 .../rules/ProjectRuntimeQuarkusTemplate.java       |  2 +-
 .../rules/ProjectRuntimeSpringTemplate.java        |  2 +-
 .../org/drools/modelcompiler/KieBaseBuilder.java   | 15 +++-
 .../drools/modelcompiler/KieBaseBuilderTest.java   | 86 ++++++++++++++++++++++
 8 files changed, 184 insertions(+), 15 deletions(-)

diff --git a/drools-compiler/src/main/java/org/drools/compiler/kie/builder/impl/KieBuilderImpl.java b/drools-compiler/src/main/java/org/drools/compiler/kie/builder/impl/KieBuilderImpl.java
index 61e1796506..27740ad6b9 100644
--- a/drools-compiler/src/main/java/org/drools/compiler/kie/builder/impl/KieBuilderImpl.java
+++ b/drools-compiler/src/main/java/org/drools/compiler/kie/builder/impl/KieBuilderImpl.java
@@ -24,7 +24,9 @@ import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.Enumeration;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.function.BiFunction;
 import java.util.function.Predicate;
 import java.util.function.Supplier;
@@ -491,6 +493,31 @@ public class KieBuilderImpl
         return false;
     }
 
+    public static boolean isPackageInKieBaseOrIncludedKieBases(final String pkgName, final KieBaseModel kieBaseModel, final KieModuleModel kieModuleModel) {
+        final Set<String> alreadyProcessesKieBaseModels = new HashSet<>();
+        return isPackageInKieBaseOrIncludedKieBases(pkgName, kieBaseModel, kieModuleModel, alreadyProcessesKieBaseModels);
+    }
+
+    private static boolean isPackageInKieBaseOrIncludedKieBases(final String pkgName, final KieBaseModel kieBaseModel,
+            final KieModuleModel kieModuleModel, final Set<String> alreadyProcessesKieBaseModels) {
+        boolean isPackageInKieBaseResult = isPackageInKieBase(kieBaseModel, pkgName);
+        if (!isPackageInKieBaseResult && kieBaseModel.getIncludes() != null && !kieBaseModel.getIncludes().isEmpty()) {
+            for (String includedKieBaseName : kieBaseModel.getIncludes()) {
+                if (!alreadyProcessesKieBaseModels.contains(includedKieBaseName)) {
+                    KieBaseModel includedKieBaseModel = kieModuleModel.getKieBaseModels().get(includedKieBaseName);
+                    if (includedKieBaseModel != null) {
+                        isPackageInKieBaseResult = isPackageInKieBaseOrIncludedKieBases(pkgName, includedKieBaseModel, kieModuleModel);
+                        if (isPackageInKieBaseResult) {
+                            return true;
+                        }
+                    }
+                    alreadyProcessesKieBaseModels.add(includedKieBaseName);
+                }
+            }
+        }
+        return isPackageInKieBaseResult;
+    }
+
     private static String getRelativePackageName( String pkgNameForFile ) {
         for ( String root : SUPPORTED_RESOURCES_ROOTS ) {
             if ( pkgNameForFile.startsWith( root ) ) {
diff --git a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/project/KieSessionModelBuilder.java b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/project/KieSessionModelBuilder.java
index b8a7fb0eba..45e523b223 100644
--- a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/project/KieSessionModelBuilder.java
+++ b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/project/KieSessionModelBuilder.java
@@ -23,9 +23,11 @@ import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Objects;
+import java.util.Set;
 
 import org.drools.codegen.common.DroolsModelBuildContext;
 import org.drools.codegen.common.GeneratedFile;
@@ -69,18 +71,62 @@ public class KieSessionModelBuilder {
 
     }
 
-    private Map<String, List<String>> getModelByKBase(Collection<CodegenPackageSources> packageSources, Map<String, KieBaseModel> kieBaseModels) {
-        Map<String, String> modelsByPackage = getModelsByPackage(packageSources);
-        Map<String, List<String>> modelsByKBase = new HashMap<>();
-        for (Map.Entry<String, KieBaseModel> entry : kieBaseModels.entrySet()) {
-            List<String> kieBasePackages = entry.getValue().getPackages();
-            boolean isAllPackages = kieBasePackages.isEmpty() || (kieBasePackages.size() == 1 && kieBasePackages.get(0).equals("*"));
-            modelsByKBase.put(entry.getKey(),
-                    isAllPackages ? new ArrayList<>(modelsByPackage.values()) : kieBasePackages.stream().map(modelsByPackage::get).filter(Objects::nonNull).collect(toList()));
+    /**
+     * Provides rules model names by KieBase.
+     *
+     * @param packageSources Package code sources for code generation.
+     * @param kieBaseModels Map of KieBase models, where key is the KieBase name.
+     * @return Map of rules models, where key is KieBase name and value is a list of rules model names that belong to that KieBase.
+     */
+    private Map<String, List<String>> getModelByKBase(final Collection<CodegenPackageSources> packageSources,
+            final Map<String, KieBaseModel> kieBaseModels) {
+        final Map<String, String> modelsByPackage = getModelsByPackage(packageSources);
+        final Map<String, List<String>> modelsByKBase = new HashMap<>();
+        // Helper set of already processed KieBases that is used to avoid infinite loop, when a user
+        // can potentially create a loop with includes definition, e.g.
+        // a includes b and b includes a
+        final Set<String> alreadyProcessesKieBases = new HashSet<>();
+        for (final Map.Entry<String, KieBaseModel> entry : kieBaseModels.entrySet()) {
+            modelsByKBase.put(entry.getKey(), getModelsForKieBaseWithIncludes(entry.getValue(), modelsByPackage, alreadyProcessesKieBases));
+            alreadyProcessesKieBases.clear();
         }
         return modelsByKBase;
     }
 
+    /**
+     * Provides rules model names that are part of a KieBase. Checks which packages should be included in a KieBase,
+     * based on package names and "includes" configuration.
+     *
+     * @param kieBaseModel KieBase model for which the rule models are returned.
+     * @param modelsByPackage Map of rules models, where key is the package name where rules model is placed and value is the whole rules model class name with path.
+     * @param alreadyProcessesKieBases Set containing names of already processed KieBases as part of this method. This method is recursive, because KieBase can include other KieBases.
+     */
+    private List<String> getModelsForKieBaseWithIncludes(final KieBaseModel kieBaseModel,
+            final Map<String, String> modelsByPackage, final Set<String> alreadyProcessesKieBases) {
+        final List<String> modelsForKieBase = getModelsForKieBaseWithoutIncludes(kieBaseModel, modelsByPackage);
+        if (kieBaseModel.getIncludes() != null && !kieBaseModel.getIncludes().isEmpty()) {
+            for (final String includedKieBaseName : kieBaseModel.getIncludes()) {
+                if (!alreadyProcessesKieBases.contains(includedKieBaseName)) {
+                    modelsForKieBase.addAll(getModelsForKieBaseWithIncludes(kieBaseModels.get(includedKieBaseName), modelsByPackage, alreadyProcessesKieBases));
+                    alreadyProcessesKieBases.add(includedKieBaseName);
+                }
+            }
+        }
+        return modelsForKieBase;
+    }
+
+    /**
+     * Provides rules model names that are part of a KieBase.
+     *
+     * @param kieBaseModel KieBase model for which the rule models are returned.
+     * @param modelsByPackage Map of rules models, where key is the package name where rules model is placed and value is the whole rules model class name with path.
+     */
+    private List<String> getModelsForKieBaseWithoutIncludes(final KieBaseModel kieBaseModel, final Map<String, String> modelsByPackage) {
+        final List<String> kieBasePackages = kieBaseModel.getPackages();
+        boolean isAllPackages = kieBasePackages.isEmpty() || (kieBasePackages.size() == 1 && kieBasePackages.get(0).equals("*"));
+        return isAllPackages ? new ArrayList<>(modelsByPackage.values()) : kieBasePackages.stream().map(modelsByPackage::get).filter(Objects::nonNull).collect(toList());
+    }
+
     private Map<String, String> getModelsByPackage(Collection<CodegenPackageSources> packageSources) {
         Map<String, String> modelsByPackage = new HashMap<>();
         for (CodegenPackageSources pkgSources : packageSources) {
diff --git a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/project/ProjectRuntimeGenerator.java b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/project/ProjectRuntimeGenerator.java
index 5bb12d525e..e81700a36d 100644
--- a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/project/ProjectRuntimeGenerator.java
+++ b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/project/ProjectRuntimeGenerator.java
@@ -85,7 +85,8 @@ public class ProjectRuntimeGenerator {
         for (String kbaseName : modelMethod.getKieBaseNames()) {
             ifBlock.addStatement("kbaseMap.put( \"" + kbaseName + "\", " +
                     "KieBaseBuilder.createKieBaseFromModel( model.getModelsForKieBase( \"" + kbaseName + "\" ), " +
-                    "model.getKieModuleModel().getKieBaseModels().get( \"" + kbaseName + "\" ) ) );\n");
+                    "model.getKieModuleModel().getKieBaseModels().get( \"" + kbaseName + "\" ), " +
+                    "model.getKieModuleModel() ) );\n");
         }
     }
 
diff --git a/drools-model/drools-model-codegen/src/main/resources/class-templates/rules/ProjectRuntimeJavaTemplate.java b/drools-model/drools-model-codegen/src/main/resources/class-templates/rules/ProjectRuntimeJavaTemplate.java
index 79c9cdb135..9d798240df 100644
--- a/drools-model/drools-model-codegen/src/main/resources/class-templates/rules/ProjectRuntimeJavaTemplate.java
+++ b/drools-model/drools-model-codegen/src/main/resources/class-templates/rules/ProjectRuntimeJavaTemplate.java
@@ -51,7 +51,7 @@ public class ProjectRuntime implements KieRuntimeBuilder {
 
     @Override
     public KieBase getKieBase(String name) {
-        return kbases.computeIfAbsent( name, n -> KieBaseBuilder.createKieBaseFromModel(model.getModelsForKieBase(n), model.getKieModuleModel().getKieBaseModels().get(n)) );
+        return kbases.computeIfAbsent( name, n -> KieBaseBuilder.createKieBaseFromModel(model.getModelsForKieBase(n), model.getKieModuleModel().getKieBaseModels().get(n), model.getKieModuleModel()) );
     }
 
     @Override
diff --git a/drools-model/drools-model-codegen/src/main/resources/class-templates/rules/ProjectRuntimeQuarkusTemplate.java b/drools-model/drools-model-codegen/src/main/resources/class-templates/rules/ProjectRuntimeQuarkusTemplate.java
index 8efae48eda..5797508b51 100644
--- a/drools-model/drools-model-codegen/src/main/resources/class-templates/rules/ProjectRuntimeQuarkusTemplate.java
+++ b/drools-model/drools-model-codegen/src/main/resources/class-templates/rules/ProjectRuntimeQuarkusTemplate.java
@@ -52,7 +52,7 @@ public class ProjectRuntime implements KieRuntimeBuilder {
 
     @Override
     public KieBase getKieBase(String name) {
-        return kbases.computeIfAbsent( name, n -> KieBaseBuilder.createKieBaseFromModel(model.getModelsForKieBase(n), model.getKieModuleModel().getKieBaseModels().get(n)) );
+        return kbases.computeIfAbsent( name, n -> KieBaseBuilder.createKieBaseFromModel(model.getModelsForKieBase(n), model.getKieModuleModel().getKieBaseModels().get(n), model.getKieModuleModel()) );
     }
 
     @Override
diff --git a/drools-model/drools-model-codegen/src/main/resources/class-templates/rules/ProjectRuntimeSpringTemplate.java b/drools-model/drools-model-codegen/src/main/resources/class-templates/rules/ProjectRuntimeSpringTemplate.java
index c83245ff03..cef7842ff1 100644
--- a/drools-model/drools-model-codegen/src/main/resources/class-templates/rules/ProjectRuntimeSpringTemplate.java
+++ b/drools-model/drools-model-codegen/src/main/resources/class-templates/rules/ProjectRuntimeSpringTemplate.java
@@ -52,7 +52,7 @@ public class ProjectRuntime implements KieRuntimeBuilder {
 
     @Override
     public KieBase getKieBase(String name) {
-        return kbases.computeIfAbsent( name, n -> KieBaseBuilder.createKieBaseFromModel(model.getModelsForKieBase(n), model.getKieModuleModel().getKieBaseModels().get(n)) );
+        return kbases.computeIfAbsent( name, n -> KieBaseBuilder.createKieBaseFromModel(model.getModelsForKieBase(n), model.getKieModuleModel().getKieBaseModels().get(n), model.getKieModuleModel()) );
     }
 
     @Override
diff --git a/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/KieBaseBuilder.java b/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/KieBaseBuilder.java
index d81005b3ba..8d0d829e30 100644
--- a/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/KieBaseBuilder.java
+++ b/drools-model/drools-model-compiler/src/main/java/org/drools/modelcompiler/KieBaseBuilder.java
@@ -29,10 +29,11 @@ import org.drools.model.Model;
 import org.kie.api.KieBaseConfiguration;
 import org.kie.api.KieServices;
 import org.kie.api.builder.model.KieBaseModel;
+import org.kie.api.builder.model.KieModuleModel;
 import org.kie.api.conf.KieBaseOption;
 import org.kie.internal.builder.KnowledgeBuilderConfiguration;
 
-import static org.drools.compiler.kie.builder.impl.KieBuilderImpl.isPackageInKieBase;
+import static org.drools.compiler.kie.builder.impl.KieBuilderImpl.isPackageInKieBaseOrIncludedKieBases;
 
 public class KieBaseBuilder {
 
@@ -92,14 +93,22 @@ public class KieBaseBuilder {
         return new KieBaseBuilder(kieBaseConf).createKieBase(builder.build());
     }
 
-    public static InternalKnowledgeBase createKieBaseFromModel(Collection<Model> models, KieBaseModel kieBaseModel) {
+    /**
+     * Creates a KieBase instance from rules models.
+     *
+     * @param models A collection of rules models, which is filtered based on the required KieBase model.
+     * @param kieBaseModel A KieBase model for which the KieBase instance is created.
+     * @param kieModuleModel KieModule model, which contains the KieBase.
+     * @return An instance of KieBase.
+     */
+    public static InternalKnowledgeBase createKieBaseFromModel(Collection<Model> models, KieBaseModel kieBaseModel, final KieModuleModel kieModuleModel) {
         KieBaseConfiguration conf = KieServices.get().newKieBaseConfiguration();
         RuleBaseConfiguration kieBaseConf = conf.as(RuleBaseConfiguration.KEY);
         kieBaseConf.setEventProcessingMode(kieBaseModel.getEventProcessingMode());
         kieBaseConf.setSessionPoolSize(kieBaseModel.getSessionsPool().getSize());
 
         KiePackagesBuilder builder = new KiePackagesBuilder(conf);
-        models.stream().filter( m -> isPackageInKieBase(kieBaseModel, m.getPackageName()) ).forEach( builder::addModel );
+        models.stream().filter( m -> isPackageInKieBaseOrIncludedKieBases(m.getPackageName(), kieBaseModel, kieModuleModel)).forEach(builder::addModel);
         return new KieBaseBuilder(kieBaseModel, conf).createKieBase(builder.build());
     }
 }
diff --git a/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/KieBaseBuilderTest.java b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/KieBaseBuilderTest.java
new file mode 100644
index 0000000000..13138e9ffc
--- /dev/null
+++ b/drools-model/drools-model-compiler/src/test/java/org/drools/modelcompiler/KieBaseBuilderTest.java
@@ -0,0 +1,86 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements.  See the NOTICE file distributed with this work for additional information regarding copyright
+ * ownership.  The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.  You may obtain a copy of the
+ * License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ * implied.  See the License for the specific language governing permissions and limitations under the License.
+ */
+
+package org.drools.modelcompiler;
+
+import org.drools.compiler.kproject.models.KieModuleModelImpl;
+import org.drools.model.Model;
+import org.drools.model.Rule;
+import org.drools.model.Variable;
+import org.drools.model.impl.ModelImpl;
+import org.drools.modelcompiler.domain.Person;
+import org.junit.jupiter.api.Test;
+import org.kie.api.KieBase;
+import org.kie.api.builder.model.KieBaseModel;
+import org.kie.api.builder.model.KieModuleModel;
+import org.kie.api.runtime.KieSession;
+
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.drools.model.DSL.declarationOf;
+import static org.drools.model.DSL.on;
+import static org.drools.model.PatternDSL.pattern;
+import static org.drools.model.PatternDSL.rule;
+
+public class KieBaseBuilderTest {
+
+    private static final Variable<Person> PERSON_VARIABLE = declarationOf(Person.class);
+
+    private static final Rule RULE_1 = rule("org.drools.test.included", "rule1")
+            .build(
+                    pattern(PERSON_VARIABLE)
+                            .expr("exprA", p -> p.getName().equals("Frantisek"))
+                            .expr("exprB", p -> p.getAge() > 20),
+                    on(PERSON_VARIABLE).execute((p) -> System.out.println(p.getName() + " is older than " + 20)));
+
+    private static final Rule RULE_2 = rule("org.drools.test.main", "rule2")
+            .build(
+                    pattern(PERSON_VARIABLE)
+                            .expr("exprA", p -> p.getName().equals("Frantisek"))
+                            .expr("exprB", p -> p.getAge() > 30),
+                    on(PERSON_VARIABLE).execute((p) -> System.out.println(p.getName() + " is older than " + 30)));
+
+    private static final Rule RULE_3 = rule("org.drools.test.includedinincluded", "rule3")
+            .build(
+                    pattern(PERSON_VARIABLE)
+                            .expr("exprA", p -> p.getName().equals("Frantisek"))
+                            .expr("exprB", p -> p.getAge() > 10),
+                    on(PERSON_VARIABLE).execute((p) -> System.out.println(p.getName() + " is older than " + 10)));
+
+    @Test
+    public void createKieBaseFromModelWithKieBaseIncludes() {
+        final Model model1 = new ModelImpl("org.drools.test.included").addRule(RULE_1);
+        final Model model2 = new ModelImpl("org.drools.test.main").addRule(RULE_2);
+        final Model model3 = new ModelImpl("org.drools.test.includedinincluded").addRule(RULE_3);
+
+        final KieModuleModel kieModuleModel = new KieModuleModelImpl();
+
+        final KieBaseModel kieBaseModelIncludedInIncluded = kieModuleModel.newKieBaseModel("IncludedInIncluded");
+        kieBaseModelIncludedInIncluded.addPackage("org.drools.test.includedinincluded");
+
+        final KieBaseModel kieBaseModelIncluded = kieModuleModel.newKieBaseModel("IncludedModel");
+        kieBaseModelIncluded.addPackage("org.drools.test.included");
+        kieBaseModelIncluded.addInclude("IncludedInIncluded");
+
+        final KieBaseModel kieBaseModelThatIncludes = kieModuleModel.newKieBaseModel("ModelThatIncludesTheOther");
+        kieBaseModelThatIncludes.addPackage("org.drools.test.main");
+        kieBaseModelThatIncludes.addInclude("IncludedModel");
+
+        final KieBase kieBase = KieBaseBuilder.createKieBaseFromModel(List.of(model1, model2, model3), kieBaseModelThatIncludes, kieModuleModel);
+        try (KieSession ksession = kieBase.newKieSession()) {
+            final Person personToBeInserted = new Person("Frantisek", 38);
+            ksession.insert(personToBeInserted);
+            // This assert checks that all three rules fire. If not, the includes don't work properly.
+            assertThat(ksession.fireAllRules()).isEqualTo(3);
+        }
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org