Description of problem: A problem occurs when switching from Rete to Phreak. We get a NPE in the following code: // @TODO (mdp) is this really necessary? won't the entire FH and RightTuple chaines just et GC'd? unlinkCreatedHandles(leftTuple); On line 268 : leftTuple.getObject It seems like the 'match' is null at t+1 but it was not at t. An extract of our rules is: rule A when ... then context.delete("D1"); end rule B when Object(...) from context.get("D1") then ... end Unfortunately, we haven't reproduced the bug yet in a unit test. Version-Release number of selected component (if applicable): How reproducible: Steps to Reproduce: 1. 2. 3. Actual results: Expected results: Additional info: java.lang.NullPointerException at org.drools.core.phreak.PhreakFromNode.unlinkCreatedHandles(PhreakFromNode.java:268) at org.drools.core.phreak.PhreakFromNode.doLeftDeletes(PhreakFromNode.java:258) at org.drools.core.phreak.PhreakFromNode.doNode(PhreakFromNode.java:41) at org.drools.core.phreak.RuleNetworkEvaluator.innerEval(RuleNetworkEvaluator.java:348) at org.drools.core.phreak.RuleNetworkEvaluator.outerEval(RuleNetworkEvaluator.java:162) at org.drools.core.phreak.RuleNetworkEvaluator.evaluateNetwork(RuleNetworkEvaluator.java:117) at org.drools.core.phreak.RuleExecutor.reEvaluateNetwork(RuleExecutor.java:199) at org.drools.core.phreak.RuleExecutor.evaluateNetworkAndFire(RuleExecutor.java:66) at org.drools.core.common.DefaultAgenda.fireNextItem(DefaultAgenda.java:927) at org.drools.core.common.DefaultAgenda.fireAllRules(DefaultAgenda.java:1187) at org.drools.core.common.AbstractWorkingMemory.fireAllRules(AbstractWorkingMemory.java:957) at org.drools.core.common.AbstractWorkingMemory.fireAllRules(AbstractWorkingMemory.java:931) at org.drools.core.impl.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:233) at fr.edf.distribution.linky.lsp.DefaultTestWithSession.launchRules(DefaultTestWithSession.java:45) at fr.edf.distribution.linky.lsp.TestRulesOnDysf.testRG_MO_02(TestRulesOnDysf.java:53) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47) at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44) at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26) at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70) at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50) at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238) at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63) at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236) at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53) at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229) at org.junit.runners.ParentRunner.run(ParentRunner.java:309) at org.junit.runner.JUnitCore.run(JUnitCore.java:160) at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:77) at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:195) at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:63) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)
I also tried to reproduce this problem but hadn't better luck. I am assuming the 'context' in your rules is a global variable, is that correct? With this assumption I tried to use a DRL like: global java.util.Map context rule A when then context.remove("key"); end rule B when $s : String() from context.get("key") then System.out.println($s); end and do something like this on the session: Map<String, String> context = new HashMap<String, String>(); context.put("key", "value"); session.setGlobal("context", context); session.fireAllRules(); I also tried to populate the Map from inside another rule, to guard the executions of the rules with additional patterns or to change their order using salience, but nothing helped to reproduce the problem. Without a proper reproducer it is impossible for me to fix this issue. Do you have further information that could help me to write that reproducer?
Yes context is a global. Unfortunately, we have put this problem on the side for now. I hope we can get back to it later and try to reproduce. The problem is definitively there as it breaks with our "real" rules using PHREAK but not with RETE, but it seems more difficult to isolate it. We switched to RETE until we get more time to work on this. As I saw the comment in the source code (typicaly the TODO), I was hoping that this was something unfinished and would be a problem that you might be aware of already. More on this later.
I was able to reproduce the problem. It happens when the FROM node receives both a left insert and a left retract that get queued, and then they are evaluated at the same time. Fix: 6.0.x: http://github.com/droolsjbpm/drools/commit/affb4e389 master: http://github.com/droolsjbpm/drools/commit/4e5187c63 Richard, this problem was not related to the mutable global you mentioned in the problem description but this is something that should not be done in rules. * Globals that are used as part of the "condition" of a rule must be immutable. * Globals that are not used as part of the "conditions" of rules can be mutable. * If one needs to use a condition on a mutable object, then the object must be a fact inserted into the session and every time the object is changed, the engine must be notified using update/modify. From the docs: http://docs.jboss.org/drools/release/6.0.0.CR5/drools-expert-docs/html_single/index.html#d0e4569 "With global you define global variables. They are used to make application objects available to the rules. Typically, they are used to provide data or services that the rules use, especially application services used in rule consequences, and to return data from the rules, like logs or values added in rule consequences, or for the rules to interact with the application, doing callbacks. Globals are not inserted into the Working Memory, and therefore a global should never be used to establish conditions in rules except when it has a constant immutable value. The engine cannot be notified about value changes of globals and does not track their changes. Incorrect use of globals in constraints may yield surprising results - surprising in a bad way."
Verified on BRMS 6.0.0.ER5.