Uploaded image for project: 'JBRULES'
  1. JBRULES
  2. JBRULES-3307

Event does not expire when temporal operators are used.

This issue belongs to an archived project. You can view it, but you can't modify it. Learn more

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Unresolved
    • Icon: Major Major
    • None
    • 5.3.0.Final
    • drools-core
    • None
    • Hide
      package drools;
      
      import static org.junit.Assert.*;
      
      import java.util.concurrent.TimeUnit;
      
      import org.drools.KnowledgeBase;
      import org.drools.KnowledgeBaseConfiguration;
      import org.drools.KnowledgeBaseFactory;
      import org.drools.builder.KnowledgeBuilder;
      import org.drools.builder.KnowledgeBuilderFactory;
      import org.drools.builder.ResourceType;
      import org.drools.conf.EventProcessingOption;
      import org.drools.conf.MBeansOption;
      import org.drools.io.impl.ByteArrayResource;
      import org.drools.logger.KnowledgeRuntimeLoggerFactory;
      import org.drools.runtime.KnowledgeSessionConfiguration;
      import org.drools.runtime.StatefulKnowledgeSession;
      import org.drools.runtime.conf.ClockTypeOption;
      import org.drools.runtime.rule.WorkingMemoryEntryPoint;
      import org.drools.time.SessionPseudoClock;
      import org.junit.After;
      import org.junit.Before;
      import org.junit.Test;
      
      public class TemporalOperatorEventExpirationTest {
      	
      	KnowledgeBase kbase;
      	StatefulKnowledgeSession ksession;
      	SessionPseudoClock clock;
      	WorkingMemoryEntryPoint entryPointA;
      	WorkingMemoryEntryPoint entryPointB;
      	
      	@Before
      	public void setup(){
      		kbase = newExampleKnowledgeBase();
      		ksession = newExampleKnowledgeSession(kbase);
      		clock = ksession.getSessionClock();
      		assertEquals(0, clock.getCurrentTime());  //The clock always start at zero.
      		KnowledgeRuntimeLoggerFactory.newConsoleLogger(ksession); //View what is happening.
      		entryPointA = ksession.getWorkingMemoryEntryPoint("a");
      		entryPointB = ksession.getWorkingMemoryEntryPoint("b");
      	}
      	
      	@After
      	public void tearDown(){
      		ksession.dispose();
      	}
      	
      	@Test
      	public void testCoincideOperator() throws InterruptedException{		
      		A a = new A(0, 1000);
      		B b = new B(0, 1000);
      		
      		entryPointA.insert(a);
      		entryPointB.insert(b);
      		clock.advanceTime(1000, TimeUnit.MILLISECONDS);
      		ksession.fireAllRules();
      				
      		//Thread.sleep(30000); //For inspecting the expirationOffset using JMX.
      		
      		assertNotNull(ksession.getFactHandle("coincides"));
      		assertNull(entryPointB.getFactHandle(b));
      		assertNull(entryPointA.getFactHandle(a));
      	}
      	
      	@Test
      	public void testStartsOperator() throws InterruptedException{
      		A a = new A(0, 1000);
      		B b = new B(0, 500);
      		
      		entryPointA.insert(a);
      		entryPointB.insert(b);
      		clock.advanceTime(1000, TimeUnit.MILLISECONDS);
      		ksession.fireAllRules();
      		
      		assertNotNull(ksession.getFactHandle("starts"));
      		assertNull(entryPointB.getFactHandle(b));
      		assertNull(entryPointA.getFactHandle(a));
      	}
      	
      	@Test
      	public void testFinishesOperator() throws InterruptedException{
      		A a = new A(0, 1000);
      		B b = new B(500, 500);
      		
      		entryPointA.insert(a);
      		entryPointB.insert(b);
      		clock.advanceTime(1000, TimeUnit.MILLISECONDS);
      		ksession.fireAllRules();
      		
      		assertNotNull(ksession.getFactHandle("finishes"));
      		assertNull(entryPointB.getFactHandle(b));
      		assertNull(entryPointA.getFactHandle(a));
      	}
      	
      	@Test
      	public void testDuringOperator() throws InterruptedException{
      		A a = new A(0, 1000);
      		B b = new B(400, 200);
      		
      		entryPointA.insert(a);
      		entryPointB.insert(b);
      		clock.advanceTime(1000, TimeUnit.MILLISECONDS);
      		ksession.fireAllRules();
      		
      		assertNotNull(ksession.getFactHandle("during"));
      		assertNull(entryPointB.getFactHandle(b));
      		assertNull(entryPointA.getFactHandle(a));
      	}
      
      	private static final KnowledgeBase newExampleKnowledgeBase(){
      		KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
      		kbuilder.add(new ByteArrayResource(drl().getBytes()), ResourceType.DRL);
      		
      		if(kbuilder.hasErrors()) throw new IllegalStateException(kbuilder.getErrors().toString());
      		
      		KnowledgeBaseConfiguration kbaseConfig = KnowledgeBaseFactory.newKnowledgeBaseConfiguration();
      		kbaseConfig.setOption(EventProcessingOption.STREAM);  //Ensure that event expiration is enabled.
      		kbaseConfig.setOption(MBeansOption.ENABLED);
      		
      		KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(kbaseConfig);
      		kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
      		return kbase;
      	}
      	
      	private static final StatefulKnowledgeSession newExampleKnowledgeSession(KnowledgeBase kbase){
      		KnowledgeSessionConfiguration kconfig = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
      		kconfig.setOption(ClockTypeOption.get("pseudo"));
      		return kbase.newStatefulKnowledgeSession(kconfig, null);
      	}
      		
      	private static final String drl(){
      		return	"package test\n" + //Everything is defined under the same package.
      	
      				"import drools.TemporalOperatorEventExpirationTest.A\n" +
      				"import drools.TemporalOperatorEventExpirationTest.B\n" +
      				
      				"declare A\n" +
      				"  @role( event )\n" +
      				"  @timestamp( timestamp )\n" +
      				"  @duration( duration )\n" +
      				"  @expires( 1ms )\n" +  //Try to force the expiration for A.  B doesn't need this.
      				"end\n" + 
      				
      				"declare B\n" +
      				"  @role( event )\n" +
      				"  @timestamp( timestamp )\n" +
      				"  @duration( duration )\n" +
      				"end\n" + 
      				
      				"rule \"coincident events\"\n" + 
      				"when\n" + 
      				"  $a: A() from entry-point \"a\"\n" +
      				"  $b: B(this coincides $a) from entry-point \"b\"\n" +
      				"then insert(\"coincides\"); end\n" +
      				
      				"rule \"starts events\"\n" + 
      				"when\n" + 
      				"  $a: A() from entry-point \"a\"\n" +
      				"  $b: B(this starts $a) from entry-point \"b\"\n" +
      				"then insert(\"starts\"); end\n" +
      				
      				"rule \"finishes events\"\n" + 
      				"when\n" + 
      				"  $a: A() from entry-point \"a\"\n" +
      				"  $b: B(this finishes $a) from entry-point \"b\"\n" +
      				"then insert(\"finishes\"); end\n" +
      				
      				"rule \"during events\"\n" + 
      				"when\n" + 
      				"  $a: A() from entry-point \"a\"\n" +
      				"  $b: B(this during $a) from entry-point \"b\"\n" +
      				"then insert(\"during\"); end";
      	}
      	
      	public static class A{
      		public final long timestamp;
      		public final long duration;
      		public A(long timestamp, long duration){
      			this.timestamp = timestamp;
      			this.duration = duration;
      		}
      	}
      	
      	public static class B{
      		public final long timestamp;
      		public final long duration;
      		public B(long timestamp, long duration){
      			this.timestamp = timestamp;
      			this.duration = duration;
      		}
      	}
      }
      
      Show
      package drools; import static org.junit.Assert.*; import java.util.concurrent.TimeUnit; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseConfiguration; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.conf.EventProcessingOption; import org.drools.conf.MBeansOption; import org.drools.io.impl.ByteArrayResource; import org.drools.logger.KnowledgeRuntimeLoggerFactory; import org.drools.runtime.KnowledgeSessionConfiguration; import org.drools.runtime.StatefulKnowledgeSession; import org.drools.runtime.conf.ClockTypeOption; import org.drools.runtime.rule.WorkingMemoryEntryPoint; import org.drools.time.SessionPseudoClock; import org.junit.After; import org.junit.Before; import org.junit.Test; public class TemporalOperatorEventExpirationTest { KnowledgeBase kbase; StatefulKnowledgeSession ksession; SessionPseudoClock clock; WorkingMemoryEntryPoint entryPointA; WorkingMemoryEntryPoint entryPointB; @Before public void setup(){ kbase = newExampleKnowledgeBase(); ksession = newExampleKnowledgeSession(kbase); clock = ksession.getSessionClock(); assertEquals(0, clock.getCurrentTime()); //The clock always start at zero. KnowledgeRuntimeLoggerFactory.newConsoleLogger(ksession); //View what is happening. entryPointA = ksession.getWorkingMemoryEntryPoint("a"); entryPointB = ksession.getWorkingMemoryEntryPoint("b"); } @After public void tearDown(){ ksession.dispose(); } @Test public void testCoincideOperator() throws InterruptedException{ A a = new A(0, 1000); B b = new B(0, 1000); entryPointA.insert(a); entryPointB.insert(b); clock.advanceTime(1000, TimeUnit.MILLISECONDS); ksession.fireAllRules(); //Thread.sleep(30000); //For inspecting the expirationOffset using JMX. assertNotNull(ksession.getFactHandle("coincides")); assertNull(entryPointB.getFactHandle(b)); assertNull(entryPointA.getFactHandle(a)); } @Test public void testStartsOperator() throws InterruptedException{ A a = new A(0, 1000); B b = new B(0, 500); entryPointA.insert(a); entryPointB.insert(b); clock.advanceTime(1000, TimeUnit.MILLISECONDS); ksession.fireAllRules(); assertNotNull(ksession.getFactHandle("starts")); assertNull(entryPointB.getFactHandle(b)); assertNull(entryPointA.getFactHandle(a)); } @Test public void testFinishesOperator() throws InterruptedException{ A a = new A(0, 1000); B b = new B(500, 500); entryPointA.insert(a); entryPointB.insert(b); clock.advanceTime(1000, TimeUnit.MILLISECONDS); ksession.fireAllRules(); assertNotNull(ksession.getFactHandle("finishes")); assertNull(entryPointB.getFactHandle(b)); assertNull(entryPointA.getFactHandle(a)); } @Test public void testDuringOperator() throws InterruptedException{ A a = new A(0, 1000); B b = new B(400, 200); entryPointA.insert(a); entryPointB.insert(b); clock.advanceTime(1000, TimeUnit.MILLISECONDS); ksession.fireAllRules(); assertNotNull(ksession.getFactHandle("during")); assertNull(entryPointB.getFactHandle(b)); assertNull(entryPointA.getFactHandle(a)); } private static final KnowledgeBase newExampleKnowledgeBase(){ KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(); kbuilder.add(new ByteArrayResource(drl().getBytes()), ResourceType.DRL); if(kbuilder.hasErrors()) throw new IllegalStateException(kbuilder.getErrors().toString()); KnowledgeBaseConfiguration kbaseConfig = KnowledgeBaseFactory.newKnowledgeBaseConfiguration(); kbaseConfig.setOption(EventProcessingOption.STREAM); //Ensure that event expiration is enabled. kbaseConfig.setOption(MBeansOption.ENABLED); KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(kbaseConfig); kbase.addKnowledgePackages(kbuilder.getKnowledgePackages()); return kbase; } private static final StatefulKnowledgeSession newExampleKnowledgeSession(KnowledgeBase kbase){ KnowledgeSessionConfiguration kconfig = KnowledgeBaseFactory.newKnowledgeSessionConfiguration(); kconfig.setOption(ClockTypeOption.get("pseudo")); return kbase.newStatefulKnowledgeSession(kconfig, null); } private static final String drl(){ return "package test\n" + //Everything is defined under the same package. "import drools.TemporalOperatorEventExpirationTest.A\n" + "import drools.TemporalOperatorEventExpirationTest.B\n" + "declare A\n" + " @role( event )\n" + " @timestamp( timestamp )\n" + " @duration( duration )\n" + " @expires( 1ms )\n" + //Try to force the expiration for A. B doesn't need this. "end\n" + "declare B\n" + " @role( event )\n" + " @timestamp( timestamp )\n" + " @duration( duration )\n" + "end\n" + "rule \"coincident events\"\n" + "when\n" + " $a: A() from entry-point \"a\"\n" + " $b: B(this coincides $a) from entry-point \"b\"\n" + "then insert(\"coincides\"); end\n" + "rule \"starts events\"\n" + "when\n" + " $a: A() from entry-point \"a\"\n" + " $b: B(this starts $a) from entry-point \"b\"\n" + "then insert(\"starts\"); end\n" + "rule \"finishes events\"\n" + "when\n" + " $a: A() from entry-point \"a\"\n" + " $b: B(this finishes $a) from entry-point \"b\"\n" + "then insert(\"finishes\"); end\n" + "rule \"during events\"\n" + "when\n" + " $a: A() from entry-point \"a\"\n" + " $b: B(this during $a) from entry-point \"b\"\n" + "then insert(\"during\"); end"; } public static class A{ public final long timestamp; public final long duration; public A(long timestamp, long duration){ this.timestamp = timestamp; this.duration = duration; } } public static class B{ public final long timestamp; public final long duration; public B(long timestamp, long duration){ this.timestamp = timestamp; this.duration = duration; } } }

      Expectations:
      When rules use temporal operators to relate two events in time, it is expected that those both events will be expired when the session clock advances far enough. In the unit tests I have provided you'll see that there are two event types A and B. Each rule tries to relate these two events using a different temporal operator. After the rules match, both A and B should be expired, and a String should be inserted into working memory to signal to the test that the rule worked.

      Observed behavior:
      When A and B are inserted into their respective entry points the rules do match, and the appropriate String is inserted into working memory. B is also retracted. However A remains in working memory forever, causing heap space exceptions if there are many A's to process. It can also be seen using JMX that the computed event expiration for B is 0, and for A is the maximum long value.

            etirelli@redhat.com Edson Tirelli
            sembler_jira Scott Embler (Inactive)
            Archiver:
            rhn-support-ceverson Clark Everson

              Created:
              Updated:
              Archived: