This is an automated email from the ASF dual-hosted git repository.
tkobayas 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 20c6a66a34e [incubator-kie-drools-6781] Resolve kbase-wide globals
referenced from another package (#6786)
20c6a66a34e is described below
commit 20c6a66a34e48bd0bb53d1e15342e523553df855
Author: Daniel Clark <[email protected]>
AuthorDate: Thu Jul 9 08:36:41 2026 +0200
[incubator-kie-drools-6781] Resolve kbase-wide globals referenced from
another package (#6786)
* [incubator-kie-drools-6781] Resolve kbase-wide globals referenced from a
constraint in another package
addGlobalDeclarations only collected globals from the current package and
from
packages imported with a wildcard. A constraint compiled from a different
package
in the same KieBase therefore could not resolve a global declared elsewhere,
unlike the classic compiler where globals are KieBase-wide. Collect globals
from
every package registry in the KieBase.
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
* move test to better home
* prevent global map mutation but register them in all package models
* preserve global ownership per-package while allowing cross-package
resolution
RuleContext.getGlobals() now uses the existing KieBase-wide globals from
typeDeclarationContext.getGlobals() instead of scanning package registries.
PackageModel.globals stays per-package for correct ownership in code
generation and package removal. Cross-package globals referenced by rules
get var_ field declarations via a separate externalGlobals map that
preserves the originating package name, without being added to the globals
list that drives runtime package registration.
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Co-authored-by: Toshiya Kobayashi <[email protected]>
---
.../model/codegen/execmodel/PackageModel.java | 18 ++++++-
.../codegen/execmodel/generator/RuleContext.java | 47 +++++++++-------
.../drools/model/codegen/execmodel/GlobalTest.java | 63 ++++++++++++++++++++++
3 files changed, 106 insertions(+), 22 deletions(-)
diff --git
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/PackageModel.java
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/PackageModel.java
index 8740af2858b..d4648cac3bf 100644
---
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/PackageModel.java
+++
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/PackageModel.java
@@ -140,6 +140,7 @@ public class PackageModel {
private Map<String, Method> staticMethods;
private final Map<String, java.lang.reflect.Type> globals = new
HashMap<>();
+ private final Map<String, Map.Entry<String, java.lang.reflect.Type>>
externalGlobals = new ConcurrentHashMap<>();
private final Map<String, RuleUnitMembers> ruleUnitMembers = new
ConcurrentHashMap<>();
@@ -389,6 +390,12 @@ public class PackageModel {
globals.putAll( pkg.getGlobals() );
}
+ public void addExternalGlobal(String globalName, String
originatingPackage, java.lang.reflect.Type type) {
+ if (!globals.containsKey(globalName)) {
+ externalGlobals.putIfAbsent(globalName,
Map.entry(originatingPackage, type));
+ }
+ }
+
public Map<String, java.lang.reflect.Type> getGlobals() {
return globals;
}
@@ -618,6 +625,10 @@ public class PackageModel {
addGlobalField(rulesClass, rulesListInitializerBody, getName(),
g.getKey(), g.getValue());
}
+ for ( Map.Entry<String, Map.Entry<String, java.lang.reflect.Type>> ext
: externalGlobals.entrySet() ) {
+ addGlobalFieldDeclaration(rulesClass, ext.getValue().getKey(),
ext.getKey(), ext.getValue().getValue());
+ }
+
rulesClass.addMember( generateListField("org.drools.model.Global",
"globals", globals.isEmpty() && !hasRuleUnit) );
customOperatorSpec.getOperatorDeclarations().forEach( op ->
rulesClass.addMember( parseBodyDeclaration( op ) ) );
@@ -903,6 +914,11 @@ public class PackageModel {
}
private static void addGlobalField(ClassOrInterfaceDeclaration
classDeclaration, BlockStmt rulesListInitializerBody, String packageName,
String globalName, java.lang.reflect.Type globalType) {
+ addGlobalFieldDeclaration(classDeclaration, packageName, globalName,
globalType);
+ addInitStatement( rulesListInitializerBody, new NameExpr(
toVar(globalName) ), "globals" );
+ }
+
+ private static void addGlobalFieldDeclaration(ClassOrInterfaceDeclaration
classDeclaration, String packageName, String globalName, java.lang.reflect.Type
globalType) {
ClassOrInterfaceType varType = toClassOrInterfaceType(Global.class);
MethodCallExpr declarationOfCall =
createDslTopLevelMethod(GLOBAL_OF_CALL);
@@ -925,8 +941,6 @@ public class PackageModel {
FieldDeclaration field = classDeclaration.addField(varType,
toVar(globalName), publicModifier().getKeyword(),
staticModifier().getKeyword(), finalModifier().getKeyword());
field.getVariables().get(0).setInitializer(declarationOfCall);
-
- addInitStatement( rulesListInitializerBody, new NameExpr(
toVar(globalName) ), "globals" );
}
public void setAccumulateFunctions(Map<String, AccumulateFunction>
accumulateFunctions) {
diff --git
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/RuleContext.java
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/RuleContext.java
index ff69bd90a8b..41c2b57a8c8 100644
---
a/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/RuleContext.java
+++
b/drools-model/drools-model-codegen/src/main/java/org/drools/model/codegen/execmodel/generator/RuleContext.java
@@ -31,7 +31,6 @@ import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
@@ -47,6 +46,7 @@ import com.github.javaparser.ast.type.UnknownType;
import org.drools.compiler.builder.impl.BuildResultCollector;
import org.drools.compiler.builder.impl.KnowledgeBuilderRulesConfigurationImpl;
import org.drools.compiler.builder.impl.TypeDeclarationContext;
+import org.drools.compiler.compiler.PackageRegistry;
import org.drools.compiler.rule.builder.EvaluatorDefinition;
import org.drools.base.ruleunit.RuleUnitDescriptionLoader;
import org.drools.core.util.Bag;
@@ -315,34 +315,41 @@ public class RuleContext {
public void addGlobalDeclarations() {
Map<String, java.lang.reflect.Type> globals = getGlobals();
-
- // also takes globals defined in different packages imported with a
wildcard
- packageModel.getImports().stream()
- .filter( imp -> imp.endsWith(".*") )
- .map( imp -> imp.substring(0, imp.length()-2) )
- .map( imp -> typeDeclarationContext.getPackageRegistry(imp) )
- .filter( Objects::nonNull )
- .map( pkgRegistry -> pkgRegistry.getPackage().getGlobals() )
- .forEach( globals::putAll );
-
for (Map.Entry<String, java.lang.reflect.Type> ks :
globals.entrySet()) {
definedVars.put(ks.getKey(), ks.getKey());
addDeclaration(new TypedDeclarationSpec(ks.getKey(),
ks.getValue(), true));
}
+ registerExternalGlobals(globals);
}
- public Map<String, java.lang.reflect.Type> getGlobals() {
+ private void registerExternalGlobals(Map<String, java.lang.reflect.Type>
globals) {
Map<String, java.lang.reflect.Type> pkgGlobals =
packageModel.getGlobals();
- if (ruleUnitDescr == null) {
- return pkgGlobals;
+ for (Map.Entry<String, java.lang.reflect.Type> entry :
globals.entrySet()) {
+ if (!pkgGlobals.containsKey(entry.getKey())) {
+ String originatingPackage = findGlobalPackage(entry.getKey());
+ if (originatingPackage != null) {
+ packageModel.addExternalGlobal(entry.getKey(),
originatingPackage, entry.getValue());
+ }
+ }
+ }
+ }
+
+ private String findGlobalPackage(String globalName) {
+ for (Map.Entry<String, PackageRegistry> entry :
typeDeclarationContext.getPackageRegistry().entrySet()) {
+ PackageRegistry reg = entry.getValue();
+ if (reg != null && reg.getPackage() != null &&
reg.getPackage().getGlobals().containsKey(globalName)) {
+ return entry.getKey();
+ }
}
- Map<String, java.lang.reflect.Type> ruleUnitGlobals =
packageModel.getGlobalsForUnit(ruleUnitDescr.getSimpleName());
- if (pkgGlobals.isEmpty()) {
- return ruleUnitGlobals;
+ return null;
+ }
+
+ public Map<String, java.lang.reflect.Type> getGlobals() {
+ // KieBase-wide globals (declared in any package of the build); the
current rule unit's globals, if any, are layered on top
+ Map<String, java.lang.reflect.Type> globals = new
HashMap<>(typeDeclarationContext.getGlobals());
+ if (ruleUnitDescr != null) {
+
globals.putAll(packageModel.getGlobalsForUnit(ruleUnitDescr.getSimpleName()));
}
- Map<String, java.lang.reflect.Type> globals = new HashMap<>();
- globals.putAll(pkgGlobals);
- globals.putAll(ruleUnitGlobals);
return globals;
}
diff --git
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/GlobalTest.java
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/GlobalTest.java
index 019ee178411..8eaed50262f 100644
---
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/GlobalTest.java
+++
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/GlobalTest.java
@@ -19,6 +19,7 @@
package org.drools.model.codegen.execmodel;
import java.lang.reflect.Method;
+import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
@@ -30,6 +31,8 @@ import org.junit.jupiter.params.provider.MethodSource;
import org.kie.api.KieBase;
import org.kie.api.builder.Message;
import org.kie.api.builder.Results;
+import org.kie.api.definition.KiePackage;
+import org.kie.api.definition.rule.Global;
import org.kie.api.definition.type.FactType;
import org.kie.api.runtime.KieSession;
@@ -96,6 +99,66 @@ public class GlobalTest extends BaseModelTest {
assertThat(result.getValue()).isEqualTo("Mark is 37");
}
+ @ParameterizedTest
+ @MethodSource("parameters")
+ public void testGlobalReferencedInConstraintFromDifferentPackage(RUN_TYPE
runType) {
+ // A global declared in one DRL package must be resolvable from a
pattern constraint compiled from
+ // a different package in the same KieBase (globals are KieBase-wide,
matching the classic compiler)
+ // and usable at runtime. Here 'threshold' is declared in
com.test.globals and referenced from a
+ // Person constraint in com.test.rules, with no wildcard import.
+ String globalDrl =
+ "package com.test.globals;\n" +
+ "global java.math.BigDecimal threshold;\n";
+ String ruleDrl =
+ "package com.test.rules;\n" +
+ "import " + Person.class.getCanonicalName() + ";\n" +
+ "rule R when\n" +
+ " Person( money.compareTo(threshold) < 0 )\n" +
+ "then end";
+
+ KieSession ksession = getKieSession(runType, globalDrl, ruleDrl);
+ ksession.setGlobal("threshold", new BigDecimal("100"));
+
+ ksession.insert(new Person("Poor", new BigDecimal("50"))); // 50 <
100 -> matches
+ ksession.insert(new Person("Rich", new BigDecimal("150"))); // 150 <
100 -> no match
+
+ assertThat(ksession.fireAllRules()).isEqualTo(1);
+ }
+
+ @ParameterizedTest
+ @MethodSource("parameters")
+ public void testRemovingDeclaringPackageCleansUpGlobal(RUN_TYPE runType) {
+ // A global declared in com.test.globals and referenced from
com.test.rules must be
+ // removed from the KieBase when com.test.globals is removed, because
no remaining
+ // package declares it. If the executable model incorrectly seeds all
globals into every
+ // PackageModel, the global survives removal because the non-declaring
package still
+ // "owns" it — breaking the package-removal contract.
+ String globalDrl =
+ "package com.test.globals;\n" +
+ "global java.math.BigDecimal threshold;\n";
+ String ruleDrl =
+ "package com.test.rules;\n" +
+ "import " + Person.class.getCanonicalName() + ";\n" +
+ "rule R when\n" +
+ " Person( money.compareTo(threshold) < 0 )\n" +
+ "then end";
+
+ KieSession ksession = getKieSession(runType, globalDrl, ruleDrl);
+ KieBase kieBase = ksession.getKieBase();
+
+
assertThat(kieBase.getKiePackage("com.test.globals").getGlobalVariables())
+ .extracting(Global::getName).contains("threshold");
+
+ kieBase.removeKiePackage("com.test.globals");
+
+ assertThat(kieBase.getKiePackage("com.test.globals")).isNull();
+ KiePackage rulesPkg = kieBase.getKiePackage("com.test.rules");
+ assertThat(rulesPkg).isNotNull();
+ assertThat(rulesPkg.getGlobalVariables())
+ .as("Global must be removed after its declaring package is
removed")
+ .extracting(Global::getName).doesNotContain("threshold");
+ }
+
public static class Functions {
public boolean lengthIs4(String s) {
return s.length() == 4;
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]