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 d569ab93e2 Fix issue where activation-group is not applied after rule 
fires initially. fixes #6509 (#6517)
d569ab93e2 is described below

commit d569ab93e2673d56120cb1f4ed6d52024e5a35cc
Author: Aner Perez <[email protected]>
AuthorDate: Thu Nov 13 19:57:01 2025 -0500

    Fix issue where activation-group is not applied after rule fires initially. 
fixes #6509 (#6517)
    
    * Fix issue where activation-group is not applied after rule fires initially
    
    * Use TrackingAgendaEventListener
---
 .../drools/core/phreak/PhreakRuleTerminalNode.java |   1 +
 .../execmodel/ActivationGroupReactivateTest.java   | 178 +++++++++++++++++++++
 2 files changed, 179 insertions(+)

diff --git 
a/drools-core/src/main/java/org/drools/core/phreak/PhreakRuleTerminalNode.java 
b/drools-core/src/main/java/org/drools/core/phreak/PhreakRuleTerminalNode.java
index 74a5e67242..703f23e7c7 100644
--- 
a/drools-core/src/main/java/org/drools/core/phreak/PhreakRuleTerminalNode.java
+++ 
b/drools-core/src/main/java/org/drools/core/phreak/PhreakRuleTerminalNode.java
@@ -232,6 +232,7 @@ public class PhreakRuleTerminalNode {
 
                     leftTuple.update( salienceInt, pctx );
                     executor.modifyActiveTuple(leftTuple );
+                    activationsManager.addItemToActivationGroup( leftTuple );
                     reteEvaluator.getRuleEventSupport().onUpdateMatch( 
leftTuple );
                 }
             }
diff --git 
a/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/ActivationGroupReactivateTest.java
 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/ActivationGroupReactivateTest.java
new file mode 100644
index 0000000000..bd31aa19de
--- /dev/null
+++ 
b/drools-model/drools-model-codegen/src/test/java/org/drools/model/codegen/execmodel/ActivationGroupReactivateTest.java
@@ -0,0 +1,178 @@
+/*
+ * 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.model.codegen.execmodel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.kie.api.event.rule.AfterMatchFiredEvent;
+import org.drools.core.event.TrackingAgendaEventListener;
+import org.kie.api.runtime.KieSession;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+public class ActivationGroupReactivateTest extends BaseModelTest {
+
+       public static class StringFact {
+               private String value;
+               private int count = 0;
+
+               public StringFact(String value) {
+                       this.value = value;
+               }
+
+               /**
+                * @return the value
+                */
+               public String getValue() {
+                       return value;
+               }
+
+               /**
+                * @param value the value to set
+                */
+               public void setValue(String value) {
+                       this.value = value;
+               }
+
+               /**
+                * @return the count
+                */
+               public int getCount() {
+                       return count;
+               }
+
+               /**
+                * @param count the count to set
+                */
+               public void setCount(int count) {
+                       this.count = count;
+               }
+
+               public void incrementCount() {
+                       this.count++;
+               }
+
+               @Override
+               public String toString() {
+                       return "StringFact: value=" + value + ", count=" + 
count;
+               }
+       }
+
+    /**
+     * Drools Rule Language (DRL) definition containing three interconnected 
rules
+     * that demonstrate activation group behavior on reactivation due to fact 
change.
+     * 
+     * <p>
+     * Rule Definitions:
+     * <p>
+     * "First rule in first activation group":
+     * - Fires for pre-asserted StringFact
+     * - In activation-group "first-group"
+     * - salience 10: Should fire first
+     * <p>
+     * "Second rule in first activation group":
+     * - Activated by pre-asserted StringFact
+     * - In activation-group "first-group"
+     * - salience 5: Should activate after first rule
+     * - Should NEVER fire due to activation group mutual exclusion
+     * <p>
+     * "Rule without activation group":
+     * - salience 2: fires last
+     * - No activation group
+     * - Fires second, triggering a StringFact update
+     *   Activates first and second rules a second time
+     * <p>
+     * Execution Flow:
+     * 1. First rule fires, cancels second rule activation
+     * 2. Third rule fires, triggers activation for first 2 rules
+     * 3. First rule fires, cancels second rule activation
+     */
+    private static final String DRL = """
+            package com.example
+            
+            import %s.StringFact;
+            
+                       rule "First rule in first activation group"
+                           activation-group "first-group"
+                               salience 10
+                       when
+                               $strf : StringFact(value == "force", count < 5)
+                       then
+                       end
+
+                       rule "Second rule in first activation group"
+                           activation-group "first-group"
+                               salience 5
+                       when
+                               $strf : StringFact(value == "force", count < 5)
+                       then
+                               modify($strf) { setValue("fail") }
+                       end
+
+                       rule "Rule without activation group"
+                               salience 2
+                       when
+                               $strf : StringFact(value == "force", count < 1)
+                       then
+                               modify($strf) { incrementCount() }
+                       end
+            """.formatted(ActivationGroupReactivateTest.class.getName());
+
+    @ParameterizedTest
+    @MethodSource("parameters")
+    void testReactivatedRulesInSingleActivationGroup(RUN_TYPE runType) {
+        // === PHASE 1: Rule Compilation and Knowledge Base Setup ===
+
+        final KieSession kSession = getKieSession(runType, DRL);
+
+        // === PHASE 2: Event Listener Setup for Rule Execution Tracking ===
+
+        // Track which rules fire during execution for validation
+        TrackingAgendaEventListener eventListener = new 
TrackingAgendaEventListener();
+
+        // Add event listener to capture rule firing events
+        kSession.addEventListener(eventListener);
+
+        // === PHASE 3: Fact Insertion - Setting Up the Test Scenario ===
+
+               kSession.insert(new StringFact("force"));
+
+        // === PHASE 4: Rule Execution - The Core Test Logic ===
+
+        // Execute all rules
+        kSession.fireAllRules();
+
+        // === PHASE 5: Validation - Verify Expected Behavior ===
+
+        final long firstRuleFireCount = 
eventListener.getAfterMatchFired().stream().filter(r -> r.equals("First rule in 
first activation group")).count();
+               final boolean secondRuleFired = 
eventListener.getAfterMatchFired().contains("Second rule in first activation 
group");
+        final long thirdRuleFireCount = 
eventListener.getAfterMatchFired().stream().filter(r -> r.equals("Rule without 
activation group")).count();
+
+        // Validate the expected execution pattern
+        assertThat(firstRuleFireCount).as("First rule should fire 
twice").isEqualTo(2);
+        assertThat(secondRuleFired).as("Second rule should not 
fire").isFalse();
+        assertThat(thirdRuleFireCount).as("Third rule should fire 
once").isEqualTo(1);
+
+        // Clean up resources
+        kSession.dispose();
+    }
+}


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to