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]