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 <[email protected]>
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: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to