You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kie.apache.org by tk...@apache.org on 2026/07/09 06:36:45 UTC
(incubator-kie-drools) branch main updated: [incubator-kie-drools-6781] Resolve kbase-wide globals referenced from another package (#6786)
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 <da...@gmail.com>
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) <no...@anthropic.com>
* 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) <no...@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <no...@anthropic.com>
Co-authored-by: Toshiya Kobayashi <to...@gmail.com>
---
.../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: commits-unsubscribe@kie.apache.org
For additional commands, e-mail: commits-help@kie.apache.org