/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.drools.compiler.integrationtests;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import org.drools.core.ClassObjectFilter;
import org.drools.core.ClockType;
import org.drools.core.time.impl.PseudoClockScheduler;
import org.junit.Test;
import org.kie.api.KieBase;
import org.kie.api.conf.EventProcessingOption;
import org.kie.api.definition.type.Expires;
import org.kie.api.definition.type.Role;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.KieSessionConfiguration;
import org.kie.api.runtime.conf.ClockTypeOption;
import org.kie.internal.KnowledgeBaseFactory;
import org.kie.internal.utils.KieHelper;
import static org.junit.Assert.assertEquals;
import static org.kie.api.definition.type.Expires.Policy.TIME_SOFT;
public class ExpirationTest {
@Test
public void testAlpha() {
String drl = "import " + A.class.getCanonicalName() + "\n" +
"declare A @role( event ) @expires(11ms) end\n" +
"global java.util.concurrent.atomic.AtomicInteger counter;\n" +
"rule R0 when\n" +
" $a: A( $Aid: id > 0 )\n" +
"then\n" +
" System.out.println(\"[\" + $a + \"]\");" +
" counter.incrementAndGet();\n" +
"end";
KieSessionConfiguration sessionConfig = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
sessionConfig.setOption( ClockTypeOption.get( ClockType.PSEUDO_CLOCK.getId() ) );
KieHelper helper = new KieHelper();
helper.addContent( drl, ResourceType.DRL );
KieBase kbase = helper.build( EventProcessingOption.STREAM );
KieSession ksession = kbase.newKieSession( sessionConfig, null );
PseudoClockScheduler sessionClock = ksession.getSessionClock();
AtomicInteger counter = new AtomicInteger( 0 );
ksession.setGlobal( "counter", counter );
ksession.insert( new A(1) );
sessionClock.advanceTime( 10, TimeUnit.MILLISECONDS );
ksession.insert( new A(2) );
sessionClock.advanceTime( 10, TimeUnit.MILLISECONDS );
ksession.fireAllRules();
assertEquals(2, counter.get());
}
@Test
public void testBeta() {
// DROOLS-1329
String drl = "import " + A.class.getCanonicalName() + "\n" +
"import " + B.class.getCanonicalName() + "\n" +
"declare A @role( event ) @expires(11ms) end\n" +
"declare B @role( event ) @expires(11ms) end\n" +
"global java.util.concurrent.atomic.AtomicInteger counter;\n" +
"rule R0 when\n" +
" $a: A( $Aid: id > 0 )\n" +
" $b: B( ($Bid: id <= $Aid) && (id > ($Aid - 1 )))\n" +
"then\n" +
" System.out.println(\"[\" + $a + \",\" + $b + \"]\");" +
" counter.incrementAndGet();\n" +
"end";
KieSessionConfiguration sessionConfig = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
sessionConfig.setOption( ClockTypeOption.get( ClockType.PSEUDO_CLOCK.getId() ) );
KieHelper helper = new KieHelper();
helper.addContent( drl, ResourceType.DRL );
KieBase kbase = helper.build( EventProcessingOption.STREAM );
KieSession ksession = kbase.newKieSession( sessionConfig, null );
PseudoClockScheduler sessionClock = ksession.getSessionClock();
AtomicInteger counter = new AtomicInteger( 0 );
ksession.setGlobal( "counter", counter );
ksession.insert( new A(1) );
ksession.insert( new B(1) );
sessionClock.advanceTime( 10, TimeUnit.MILLISECONDS );
ksession.insert( new A(2) );
ksession.insert( new B(2) );
sessionClock.advanceTime( 10, TimeUnit.MILLISECONDS );
ksession.fireAllRules();
assertEquals(2, counter.get());
}
@Test
public void testBetaRightExpired() {
// DROOLS-1329
String drl = "import " + A.class.getCanonicalName() + "\n" +
"import " + B.class.getCanonicalName() + "\n" +
"declare A @role( event ) @expires(11ms) end\n" +
"declare B @role( event ) @expires(11ms) end\n" +
"global java.util.concurrent.atomic.AtomicInteger counter;\n" +
"rule R0 when\n" +
" $a: A( $Aid: id > 0 )\n" +
" $b: B( id == $Aid )\n" +
"then\n" +
" System.out.println(\"[\" + $a + \",\" + $b + \"]\");" +
" counter.incrementAndGet();\n" +
"end";
KieSessionConfiguration sessionConfig = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
sessionConfig.setOption( ClockTypeOption.get( ClockType.PSEUDO_CLOCK.getId() ) );
KieHelper helper = new KieHelper();
helper.addContent( drl, ResourceType.DRL );
KieBase kbase = helper.build( EventProcessingOption.STREAM );
KieSession ksession = kbase.newKieSession( sessionConfig, null );
PseudoClockScheduler sessionClock = ksession.getSessionClock();
AtomicInteger counter = new AtomicInteger( 0 );
ksession.setGlobal( "counter", counter );
ksession.insert( new A(1) );
sessionClock.advanceTime( 20, TimeUnit.MILLISECONDS );
ksession.insert( new B(1) );
ksession.fireAllRules();
assertEquals(0, counter.get());
}
@Test
public void testBetaLeftExpired() {
// DROOLS-1329
String drl = "import " + A.class.getCanonicalName() + "\n" +
"import " + B.class.getCanonicalName() + "\n" +
"declare A @role( event ) @expires(11ms) end\n" +
"declare B @role( event ) @expires(11ms) end\n" +
"global java.util.concurrent.atomic.AtomicInteger counter;\n" +
"rule R0 when\n" +
" $a: A( $Aid: id > 0 )\n" +
" $b: B( id == $Aid )\n" +
"then\n" +
" System.out.println(\"[\" + $a + \",\" + $b + \"]\");" +
" counter.incrementAndGet();\n" +
"end";
KieSessionConfiguration sessionConfig = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
sessionConfig.setOption( ClockTypeOption.get( ClockType.PSEUDO_CLOCK.getId() ) );
KieHelper helper = new KieHelper();
helper.addContent( drl, ResourceType.DRL );
KieBase kbase = helper.build( EventProcessingOption.STREAM );
KieSession ksession = kbase.newKieSession( sessionConfig, null );
PseudoClockScheduler sessionClock = ksession.getSessionClock();
AtomicInteger counter = new AtomicInteger( 0 );
ksession.setGlobal( "counter", counter );
ksession.insert( new B(1) );
sessionClock.advanceTime( 20, TimeUnit.MILLISECONDS );
ksession.insert( new A(1) );
ksession.fireAllRules();
assertEquals(0, counter.get());
}
@Test
public void testBetaLeftExpired2() {
// DROOLS-1329
String drl = "import " + A.class.getCanonicalName() + "\n" +
"import " + B.class.getCanonicalName() + "\n" +
"import " + C.class.getCanonicalName() + "\n" +
"declare A @role( event ) @expires(31ms) end\n" +
"declare B @role( event ) @expires(11ms) end\n" +
"declare C @role( event ) @expires(31ms) end\n" +
"global java.util.concurrent.atomic.AtomicInteger counter;\n" +
"rule R0 when\n" +
" $a: A( $Aid: id > 0 )\n" +
" $b: B( $Bid: id == $Aid )\n" +
" $c: C( id == $Bid )\n" +
"then\n" +
" System.out.println(\"[\" + $a + \",\" + $b + \",\" + $c + \"]\");" +
" counter.incrementAndGet();\n" +
"end";
KieSessionConfiguration sessionConfig = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
sessionConfig.setOption( ClockTypeOption.get( ClockType.PSEUDO_CLOCK.getId() ) );
KieHelper helper = new KieHelper();
helper.addContent( drl, ResourceType.DRL );
KieBase kbase = helper.build( EventProcessingOption.STREAM );
KieSession ksession = kbase.newKieSession( sessionConfig, null );
PseudoClockScheduler sessionClock = ksession.getSessionClock();
AtomicInteger counter = new AtomicInteger( 0 );
ksession.setGlobal( "counter", counter );
ksession.insert( new A(1) );
ksession.insert( new B(1) );
sessionClock.advanceTime( 20, TimeUnit.MILLISECONDS );
ksession.insert( new C(1) );
ksession.fireAllRules();
assertEquals(0, counter.get());
}
public class A {
private final int id;
public A( int id ) {
this.id = id;
}
public int getId() {
return id;
}
@Override
public String toString() {
return "A(" + id + ")";
}
}
public class B {
private final int id;
public B( int id ) {
this.id = id;
}
public int getId() {
return id;
}
@Override
public String toString() {
return "B(" + id + ")";
}
}
public class C {
private final int id;
public C( int id ) {
this.id = id;
}
public int getId() {
return id;
}
@Override
public String toString() {
return "C(" + id + ")";
}
}
@Role(Role.Type.EVENT)
@Expires( "10s" )
public static class ExpiringEventA { }
@Role(Role.Type.EVENT)
@Expires( value = "30s", policy = TIME_SOFT )
public static class ExpiringEventB { }
@Role(Role.Type.EVENT)
@Expires( value = "30s", policy = TIME_SOFT )
public static class ExpiringEventC { }
@Test
public void testSoftExpiration() {
// DROOLS-1483
String drl = "import " + ExpiringEventA.class.getCanonicalName() + "\n" +
"import " + ExpiringEventB.class.getCanonicalName() + "\n" +
"import " + ExpiringEventC.class.getCanonicalName() + "\n" +
"rule Ra when\n" +
" $e : ExpiringEventA() over window:time(20s)\n" +
"then end\n " +
"rule Rb when\n" +
" $e : ExpiringEventB() over window:time(20s)\n" +
"then end\n " +
"rule Rc when\n" +
" $e : ExpiringEventC()\n" +
"then end\n";
KieSessionConfiguration sessionConfig = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
sessionConfig.setOption( ClockTypeOption.get( ClockType.PSEUDO_CLOCK.getId() ) );
KieHelper helper = new KieHelper();
helper.addContent( drl, ResourceType.DRL );
KieBase kbase = helper.build( EventProcessingOption.STREAM );
KieSession ksession = kbase.newKieSession( sessionConfig, null );
PseudoClockScheduler clock = ksession.getSessionClock();
ksession.insert( new ExpiringEventA() );
ksession.insert( new ExpiringEventB() );
ksession.insert( new ExpiringEventC() );
ksession.fireAllRules();
clock.advanceTime( 5, TimeUnit.SECONDS );
ksession.fireAllRules();
assertEquals( 1, ksession.getObjects( new ClassObjectFilter( ExpiringEventA.class ) ).size() );
assertEquals( 1, ksession.getObjects( new ClassObjectFilter( ExpiringEventB.class ) ).size() );
assertEquals( 1, ksession.getObjects( new ClassObjectFilter( ExpiringEventC.class ) ).size() );
clock.advanceTime( 10, TimeUnit.SECONDS );
ksession.fireAllRules();
// t=15 -> hard expiration of A
assertEquals( 0, ksession.getObjects( new ClassObjectFilter( ExpiringEventA.class ) ).size() );
assertEquals( 1, ksession.getObjects( new ClassObjectFilter( ExpiringEventB.class ) ).size() );
assertEquals( 1, ksession.getObjects( new ClassObjectFilter( ExpiringEventC.class ) ).size() );
clock.advanceTime( 10, TimeUnit.SECONDS );
ksession.fireAllRules();
// t=25 -> implicit expiration of B
assertEquals( 0, ksession.getObjects( new ClassObjectFilter( ExpiringEventA.class ) ).size() );
assertEquals( 0, ksession.getObjects( new ClassObjectFilter( ExpiringEventB.class ) ).size() );
assertEquals( 1, ksession.getObjects( new ClassObjectFilter( ExpiringEventC.class ) ).size() );
clock.advanceTime( 10, TimeUnit.SECONDS );
ksession.fireAllRules();
// t=35 -> soft expiration of C
assertEquals( 0, ksession.getObjects( new ClassObjectFilter( ExpiringEventA.class ) ).size() );
assertEquals( 0, ksession.getObjects( new ClassObjectFilter( ExpiringEventB.class ) ).size() );
assertEquals( 0, ksession.getObjects( new ClassObjectFilter( ExpiringEventC.class ) ).size() );
}
@Test
public void testSoftExpirationWithDeclaration() {
// DROOLS-1483
String drl = "import " + A.class.getCanonicalName() + "\n" +
"import " + B.class.getCanonicalName() + "\n" +
"import " + C.class.getCanonicalName() + "\n" +
"declare A @role( event ) @expires(10s) end\n" +
"declare B @role( event ) @expires(value = 30s, policy = TIME_SOFT) end\n" +
"declare C @role( event ) @expires(value = 30s, policy = TIME_SOFT) end\n" +
"rule Ra when\n" +
" $e : A() over window:time(20s)\n" +
"then end\n " +
"rule Rb when\n" +
" $e : B() over window:time(20s)\n" +
"then end\n " +
"rule Rc when\n" +
" $e : C()\n" +
"then end\n";
KieSessionConfiguration sessionConfig = KnowledgeBaseFactory.newKnowledgeSessionConfiguration();
sessionConfig.setOption( ClockTypeOption.get( ClockType.PSEUDO_CLOCK.getId() ) );
KieHelper helper = new KieHelper();
helper.addContent( drl, ResourceType.DRL );
KieBase kbase = helper.build( EventProcessingOption.STREAM );
KieSession ksession = kbase.newKieSession( sessionConfig, null );
PseudoClockScheduler clock = ksession.getSessionClock();
ksession.insert( new A(1) );
ksession.insert( new B(2) );
ksession.insert( new C(3) );
ksession.fireAllRules();
clock.advanceTime( 5, TimeUnit.SECONDS );
ksession.fireAllRules();
assertEquals( 1, ksession.getObjects( new ClassObjectFilter( A.class ) ).size() );
assertEquals( 1, ksession.getObjects( new ClassObjectFilter( B.class ) ).size() );
assertEquals( 1, ksession.getObjects( new ClassObjectFilter( C.class ) ).size() );
clock.advanceTime( 10, TimeUnit.SECONDS );
ksession.fireAllRules();
// t=15 -> hard expiration of A
assertEquals( 0, ksession.getObjects( new ClassObjectFilter( A.class ) ).size() );
assertEquals( 1, ksession.getObjects( new ClassObjectFilter( B.class ) ).size() );
assertEquals( 1, ksession.getObjects( new ClassObjectFilter( C.class ) ).size() );
clock.advanceTime( 10, TimeUnit.SECONDS );
ksession.fireAllRules();
// t=25 -> implicit expiration of B
assertEquals( 0, ksession.getObjects( new ClassObjectFilter( A.class ) ).size() );
assertEquals( 0, ksession.getObjects( new ClassObjectFilter( B.class ) ).size() );
assertEquals( 1, ksession.getObjects( new ClassObjectFilter( C.class ) ).size() );
clock.advanceTime( 10, TimeUnit.SECONDS );
ksession.fireAllRules();
// t=35 -> soft expiration of C
assertEquals( 0, ksession.getObjects( new ClassObjectFilter( A.class ) ).size() );
assertEquals( 0, ksession.getObjects( new ClassObjectFilter( B.class ) ).size() );
assertEquals( 0, ksession.getObjects( new ClassObjectFilter( C.class ) ).size() );
}
}