Learning Drools engine with a debugger part 3

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.

ReteTree-hello

[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.

Leave a comment