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]

Reply via email to