Hello again,<br><br>Recently while continuing to work on a complex event processing project I have encountered strange,<br>but interesting, situations where the fireAllRules() method of a StatefulKnowledgeSession appears to<br>
return before actually emptying the activations on the agenda.  I was under the impression that this <br>method only returns after all activations, including subsequent activations, have been completed and<br>removed from the agenda.  If I&#39;m incorrect then feel free to disregard the rest of this message.<br>
<br>[Drools v 5.0.1, JUnit 4.0, Spring 2.5.6]<br><br>The behavior I have noticed begins when working with a rule such as &quot;Find Missing Input Events&quot; <br>(in file test.drl):<br>------------------------------------------------------------------------<br>
package org.drools.test<br><br>import org.drools.test.*<br><br>declare Event<br>    @role( event )<br>    @timestamp( beginning )<br>    @duration( length )<br>    @expires( 1h )<br>end<br><br>declare Fault<br>    @role( event )<br>
    @timestamp( beginning )<br>    @duration( length )<br>    @expires( 1h )<br>end<br><br>query &quot;getFaults&quot;<br>    $fault: Fault() from entry-point &quot;faults&quot;<br>end<br><br>rule &quot;Find Missing Input Events&quot;<br>
    when<br>        $a: Event()<br>            from entry-point &quot;input&quot;<br>        $b: Event( this after $a)<br>            from entry-point &quot;input&quot;<br>        not (Event( (this after $a || this metby $a), (this before $b || this meets $b))<br>
            from entry-point &quot;input&quot;)<br>        not (Fault(this metby $a, this meets $b)<br>            from entry-point &quot;faults&quot;)<br>    then<br>        entryPoints[&quot;faults&quot;].insert(new Fault($a, $b));<br>
end<br><br>rule &quot;Print Faults&quot;<br><br>    when<br>        $f: Fault() from entry-point &quot;faults&quot;<br>    then<br>        System.out.println($f);<br>end<br>------------------------------------------------------------------------<br>
<br>The purpose of this rule is to detect when one or more events are missing in an event sequence.  The<br>events in question are supposed to continue from one to the next without any time in-between.  If this <br>rule does discover a gap/absence between two events a Fault is created for further processing.  (*Note <br>
that the Event and Fault classes that I used only contain two fields, beginning and length, to be used as<br>the time-stamp and duration respectively.)  I noticed the unexpected behavior shortly after creating this <br>unit test:<br>
<br>------------------------------------------------------------------------<br>public class FireAllRulesTest {<br><br>    KnowledgeBase knowledgeBase;<br>    <br>    StatefulKnowledgeSession session;<br>    <br>    /**<br>
     * @throws java.lang.Exception<br>     */<br>    @Before<br>    public void setUp() throws Exception {<br>        KnowledgeBuilder builder = KnowledgeBuilderFactory.newKnowledgeBuilder();<br>        builder.add(ResourceFactory.newFileResource(&quot;target/test-classes/test.drl&quot;), ResourceType.DRL);<br>
        <br>        if(builder.hasErrors()){<br>            throw new RuntimeException(builder.getErrors().toString());<br>        }<br>        KnowledgeBaseConfiguration configuration = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();<br>
        configuration.setOption(EventProcessingOption.STREAM);<br>        <br>        knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(configuration);<br>        knowledgeBase.addKnowledgePackages(builder.getKnowledgePackages());<br>
        <br>        session = knowledgeBase.newStatefulKnowledgeSession();<br>        WorkingMemoryConsoleLogger logger = new WorkingMemoryConsoleLogger(session);<br>    }<br>    <br>    @Test<br>    public void testAbsence(){<br>
        System.out.println(&quot;Running...&quot;);<br>        //Record the rules that have fired.<br>        ActivationTrackingEventListener listener = new ActivationTrackingEventListener();<br>        session.addEventListener(listener);<br>
        <br>        //Start at 0, go to 100.<br>        Event first = new Event(0,100);<br>        <br>        //Start at 1000, go to 1100. Absence of 900.<br>        Event second = new Event(1000, 100);<br>        <br>        //Start at 1100, go to 1200.  No absence.<br>
        Event third = new Event(1100, 100);<br>        <br>        //Start at 1200, go to 1300.  No absence.<br>        Event fourth = new Event(1200, 100);<br><br>        //Start at 2000, go to 2100.  Absence of 700.<br>
        Event fifth = new Event(2000, 100);<br>        <br>        WorkingMemoryEntryPoint ingest = session.getWorkingMemoryEntryPoint(&quot;input&quot;);<br>        ingest.insert(first);<br>        ingest.insert(second);<br>
        ingest.insert(third);<br>        ingest.insert(fourth);        <br>        ingest.insert(fifth);<br>        <br>        System.out.println(&quot;All events have been inserted.&quot;);<br><br>        session.fireAllRules();<br>
        <br>        System.out.println(&quot;Rules have finished firing.&quot;);<br><br>        //Expect that &quot;Find Missing Input Events&quot; has fired and that there are two faults as a result.<br>        assertTrue(listener.hasRuleFired(&quot;Find Missing Input Events&quot;));<br>
        assertEquals(2,faultCount());<br>        <br>        System.out.println(&quot;Test completed.&quot;);<br>    }<br>    <br>    public int faultCount(){<br>        QueryResults results = session.getQueryResults(&quot;getFaults&quot;);<br>
        return results.size();<br>    }<br><br>}<br>------------------------------------------------------------------------<br><br>The typical result of this test is a failure about 90% of the time.  Strangely, the other 10% of the time this exact <br>
same test is run it will pass, without any modifications.  Equally interesting is the output of the WorkingMemoryLogger:<br><br>------------------------------------------------------------------------<br>Running...<br>OBJECT ASSERTED value:org.drools.test.Event@1b994de factId: 1<br>
BEFORE ACTIVATION FIRED rule:Find Missing Input Events activationId:Find Missing Input Events [2, 1, 0] <br>      declarations: $b=org.drools.test.Event@2803d5(2); $a=org.drools.test.Event@1b994de(1)<br>ACTIVATION CREATED rule:Find Missing Input Events activationId:Find Missing Input Events [2, 1, 0] <br>
      declarations: $b=org.drools.test.Event@2803d5(2); $a=org.drools.test.Event@1b994de(1)<br>OBJECT ASSERTED value:org.drools.test.Event@2803d5 factId: 2<br>OBJECT ASSERTED value:org.drools.test.Event@1b32627 factId: 3<br>
OBJECT ASSERTED value:org.drools.test.Event@ad157f factId: 4<br>ACTIVATION CREATED rule:Find Missing Input Events activationId:Find Missing Input Events [5, 4, 0] <br>      declarations: $b=org.drools.test.Event@1bfa3d3(5); $a=org.drools.test.Event@ad157f(4)<br>
OBJECT ASSERTED value:org.drools.test.Event@1bfa3d3 factId: 5<br>All events have been inserted.<br>***Rules have finished firing.<br>ACTIVATION CREATED rule:Print Faults activationId:Print Faults [6, 0] declarations: $f=org.drools.test.Fault@159780d(6)<br>
OBJECT ASSERTED value:org.drools.test.Fault@159780d factId: 6<br>AFTER ACTIVATION FIRED rule:Find Missing Input Events activationId:Find Missing Input Events [2, 1, 0] <br>      declarations: $b=org.drools.test.Event@2803d5(2); $a=org.drools.test.Event@1b994de(1)<br>
BEFORE ACTIVATION FIRED rule:Print Faults activationId:Print Faults [6, 0] declarations: $f=org.drools.test.Fault@159780d(6)<br>org.drools.test.Fault@159780d<br>AFTER ACTIVATION FIRED rule:Print Faults activationId:Print Faults [6, 0] declarations: $f=org.drools.test.Fault@159780d(6)<br>
BEFORE ACTIVATION FIRED rule:Find Missing Input Events activationId:Find Missing Input Events [5, 4, 0] <br>      declarations: $b=org.drools.test.Event@1bfa3d3(5); $a=org.drools.test.Event@ad157f(4)<br>ACTIVATION CREATED rule:Print Faults activationId:Print Faults [7, 0] declarations: $f=org.drools.test.Fault@c26b16(7)<br>
OBJECT ASSERTED value:org.drools.test.Fault@c26b16 factId: 7<br>AFTER ACTIVATION FIRED rule:Find Missing Input Events activationId:Find Missing Input Events [5, 4, 0] <br>      declarations: $b=org.drools.test.Event@1bfa3d3(5); $a=org.drools.test.Event@ad157f(4)<br>
BEFORE ACTIVATION FIRED rule:Print Faults activationId:Print Faults [7, 0] declarations: $f=org.drools.test.Fault@c26b16(7)<br>org.drools.test.Fault@c26b16<br>AFTER ACTIVATION FIRED rule:Print Faults activationId:Print Faults [7, 0] declarations: $f=org.drools.test.Fault@c26b16(7)<br>
------------------------------------------------------------------------<br><br>It seems that the rule is indeed firing correctly, but that it is doing so only after returning from fireAllRules method.  <br>I have marked line that follows fireAllRules() with *** above.  So the rule fires, but the test fails because the firing <br>
takes place after the junit  assertions are checked.<br><br>But that&#39;s not all, it gets more interesting.  Looking back at the rule file, when you comment out the last condition:<br><br>    not (Fault(this metby $a, this meets $b)<br>

                from entry-point &quot;faults&quot;)<br><br>The problem with the unit test disappears.  After much thought I&#39;m still not sure why this is, but the last condition is<br>critical to avoid creating redundant Faults (Because a lot of the Events are redundant as well.  It just comes with the<br>
problem being solved).  The problem also disappears if you put a Thread.sleep(1000) after firing the rules, which is<br>why I suspect a timing issue between the rules firing and the unit test assertions being evaluated.  This could explain<br>
why the test sometimes works, and sometimes doesn&#39;t.<br><br>I&#39;m hopeful that someone will understand why this is happening, and point how how to prevent it.  Thank you in advance.<br><br>Bill<br><br>