What happens if a fact is updated and re-evaluated?
3. Update a fact
This is a well-known Hello World example which is gererated by Eclipse Drools plugin.
rule "Hello World" when m : Message( status == Message.HELLO, myMessage : message ) then System.out.println( myMessage ); m.setMessage( "Goodbye cruel world" ); m.setStatus( Message.GOODBYE ); update( m ); end rule "GoodBye" when Message( status == Message.GOODBYE, myMessage : message ) then System.out.println( myMessage ); end
Rete tree looks usual.
[EntryPointNode(1) EntryPoint::DEFAULT ] [ObjectTypeNode(2)::EntryPoint::DEFAULT objectType=[ClassObjectType class=org.drools.core.reteoo.InitialFactImpl] expiration=-1ms ] [ObjectTypeNode(3)::EntryPoint::DEFAULT objectType=[ClassObjectType class=com.sample.DroolsTest$Message] expiration=-1ms ] [AlphaNode(4) constraint=status == Message.HELLO] [LeftInputAdapterNode(5)] [RuleTerminalNode(6): rule=Hello World] [AlphaNode(7) constraint=status == Message.GOODBYE] [LeftInputAdapterNode(8)] [RuleTerminalNode(9): rule=GoodBye]
Firstly, insert a Message with status == Message.HELLO. It matches the first rule so the RHS will be executed via DefaultAgenda.fireActivation(). The RHS is converted into the java code below. You can confirm that by setting system property “drools.dump.dir”.
public static void defaultConsequence(KnowledgeHelper drools, com.sample.DroolsTest.Message m, org.kie.api.runtime.rule.FactHandle m__Handle__ , java.lang.String myMessage, org.kie.api.runtime.rule.FactHandle myMessage__Handle__ ) throws java.lang.Exception { org.kie.api.runtime.rule.RuleContext kcontext = drools; System.out.println( myMessage ); m.setMessage( "Goodbye cruel world" ); m.setStatus( Message.GOODBYE ); { drools.update( m__Handle__, org.drools.core.util.bitmask.AllSetBitMask.get(), com.sample.DroolsTest.Message.class ); }; }
Here, the fact is modified and drools.update() is called to create a PropagationEntry$Update.
Thread [main] (Suspended (breakpoint at line 142 in PropagationEntry$Update)) PropagationEntry$Update.<init>(EntryPointNode, InternalFactHandle, PropagationContext, ObjectTypeConf) line: 142 EntryPointNode.modifyObject(InternalFactHandle, PropagationContext, ObjectTypeConf, InternalWorkingMemory) line: 236 NamedEntryPoint.update(InternalFactHandle, Object, Object, ObjectTypeConf, RuleImpl, PropagationContext) line: 414 NamedEntryPoint.update(InternalFactHandle, Object, BitMask, Class<?>, Activation) line: 403 DefaultKnowledgeHelper<T>.update(FactHandle, BitMask, Class<?>) line: 414 Rule_Hello_World1857861002.defaultConsequence(KnowledgeHelper, DroolsTest$Message, FactHandle, String, FactHandle) line: 10 Rule_Hello_World1857861002DefaultConsequenceInvokerGenerated.evaluate(KnowledgeHelper, WorkingMemory) line: not available Rule_Hello_World1857861002DefaultConsequenceInvoker.evaluate(KnowledgeHelper, WorkingMemory) line: not available DefaultAgenda.fireActivation(Activation) line: 1072 RuleExecutor.fire(InternalWorkingMemory, AgendaFilter, int, int, InternalAgenda) line: 121 RuleExecutor.evaluateNetworkAndFire(InternalWorkingMemory, AgendaFilter, int, int) line: 74 DefaultAgenda.fireNextItem(AgendaFilter, int, int) line: 978 DefaultAgenda.fireAllRules(AgendaFilter, int) line: 1292 ...
Actual work is done via StatefulKnowledgeSessionImpl.flushPropagations() inside RuleExecutor.fire(). Now AlphaNode.modifyObject() is called and the modified fact is evaluated against the constraints.
Thread [main] (Suspended (breakpoint at line 204 in MvelConstraint)) MvelConstraint.isAllowed(InternalFactHandle, InternalWorkingMemory, ContextEntry) line: 204 AlphaNode.modifyObject(InternalFactHandle, ModifyPreviousTuples, PropagationContext, InternalWorkingMemory) line: 158 CompositeObjectSinkAdapter.doPropagateModifyObject(InternalFactHandle, ModifyPreviousTuples, PropagationContext, InternalWorkingMemory, ObjectSink) line: 504 CompositeObjectSinkAdapter.propagateModifyObject(InternalFactHandle, ModifyPreviousTuples, PropagationContext, InternalWorkingMemory) line: 423 ObjectTypeNode.modifyObject(InternalFactHandle, ModifyPreviousTuples, PropagationContext, InternalWorkingMemory) line: 358 EntryPointNode.propagateModify(InternalFactHandle, PropagationContext, ObjectTypeConf, InternalWorkingMemory) line: 248 PropagationEntry$Update.execute(InternalWorkingMemory) line: 149 SynchronizedPropagationList.flush(InternalWorkingMemory, PropagationEntry) line: 96 SynchronizedPropagationList.flush() line: 69 StatefulKnowledgeSessionImpl.flushPropagations() line: 1993 RuleExecutor.fire(InternalWorkingMemory, AgendaFilter, int, int, InternalAgenda) line: 128 RuleExecutor.evaluateNetworkAndFire(InternalWorkingMemory, AgendaFilter, int, int) line: 74 DefaultAgenda.fireNextItem(AgendaFilter, int, int) line: 978 DefaultAgenda.fireAllRules(AgendaFilter, int) line: 1292 ...
Now the fact matches the constraint of the second rule “status == Message.GOODBYE” and goes into the next node, LeftInputAdapterNode.
Firstly, it unlinks the segment / rule for “Hello World” as it doesn’t match any longer.
Thread [main] (Suspended) PathMemory.doUnlinkRule(InternalWorkingMemory) line: 130 PathMemory.unlinkedSegment(long, InternalWorkingMemory) line: 164 SegmentMemory.unlinkNode(long, InternalWorkingMemory) line: 202 LeftInputAdapterNode$LiaNodeMemory.unlinkNode(InternalWorkingMemory) line: 568 LeftInputAdapterNode.doDeleteObject(LeftTuple, PropagationContext, SegmentMemory, InternalWorkingMemory, LeftInputAdapterNode, boolean, LeftInputAdapterNode$LiaNodeMemory) line: 283 LeftInputAdapterNode.processDeletesFromModify(ModifyPreviousTuples, PropagationContext, InternalWorkingMemory, ObjectTypeNode$Id) line: 409 LeftInputAdapterNode.modifyObject(InternalFactHandle, ModifyPreviousTuples, PropagationContext, InternalWorkingMemory) line: 371 SingleObjectSinkAdapter.propagateModifyObject(InternalFactHandle, ModifyPreviousTuples, PropagationContext, InternalWorkingMemory) line: 69 AlphaNode.modifyObject(InternalFactHandle, ModifyPreviousTuples, PropagationContext, InternalWorkingMemory) line: 161 ...
Then, it links the segment and the rule for “GoodBye” so a RuleAgendaItem is created.
com.sample.DroolsTest at localhost:49126 Thread [main] (Suspended) DefaultAgenda.createRuleAgendaItem(int, PathMemory, TerminalNode) line: 228 PathMemory.ensureAgendaItemCreated(InternalWorkingMemory) line: 109 PathMemory.doLinkRule(InternalWorkingMemory) line: 115 PathMemory.linkSegment(long, InternalWorkingMemory) line: 94 SegmentMemory.notifyRuleLinkSegment(InternalWorkingMemory) line: 181 SegmentMemory.linkNode(long, InternalWorkingMemory) line: 143 LeftInputAdapterNode$LiaNodeMemory.linkNode(InternalWorkingMemory) line: 564 LeftInputAdapterNode.doInsertObject(InternalFactHandle, PropagationContext, LeftInputAdapterNode, InternalWorkingMemory, LeftInputAdapterNode$LiaNodeMemory, boolean, boolean) line: 216 LeftInputAdapterNode.modifyObject(InternalFactHandle, ModifyPreviousTuples, PropagationContext, InternalWorkingMemory) line: 393 SingleObjectSinkAdapter.propagateModifyObject(InternalFactHandle, ModifyPreviousTuples, PropagationContext, InternalWorkingMemory) line: 69 AlphaNode.modifyObject(InternalFactHandle, ModifyPreviousTuples, PropagationContext, InternalWorkingMemory) line: 161 ...
The RuleAgendaItem will be picked up by the next DefaultAgenda.fireNextItem() so the rule “GoodBye” will be finally fired.