/* * 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.ArrayList; import java.util.List; import java.util.concurrent.Semaphore; import org.drools.compiler.LongAddress; import org.drools.compiler.Person; import org.drools.compiler.util.debug.DebugList; import org.drools.core.impl.InternalRuleUnitExecutor; import org.drools.core.ruleunit.RuleUnitFactory; import org.junit.Test; import org.kie.api.KieBase; import org.kie.api.KieServices; import org.kie.api.builder.KieFileSystem; import org.kie.api.builder.Results; import org.kie.api.definition.rule.UnitVar; import org.kie.api.io.ResourceType; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.rule.DataSource; import org.kie.api.runtime.rule.FactHandle; import org.kie.api.runtime.rule.RuleUnit; import org.kie.api.runtime.rule.RuleUnitExecutor; import org.kie.internal.builder.conf.PropertySpecificOption; import org.kie.internal.utils.KieHelper; import static java.util.Arrays.asList; import static org.drools.core.ruleunit.RuleUnitUtil.getUnitName; import static org.drools.core.util.ClassUtils.getCanonicalSimpleName; import static org.junit.Assert.*; public class RuleUnitTest { @Test public void testWithDataSource() throws Exception { String drl1 = "import " + Person.class.getCanonicalName() + "\n" + "import " + AdultUnit.class.getCanonicalName() + "\n" + "import " + NotAdultUnit.class.getCanonicalName() + "\n" + "rule Adult @Unit( AdultUnit.class ) when\n" + " Person(age >= 18, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is adult\");\n" + "end\n" + "rule NotAdult @Unit( NotAdultUnit.class ) when\n" + " Person(age < 18, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is NOT adult\");\n" + "end"; KieBase kbase = new KieHelper().addContent( drl1, ResourceType.DRL ).build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); DataSource<Person> persons = executor.newDataSource( "persons", new Person( "Mario", 42 ), new Person( "Marilena", 44 ), new Person( "Sofia", 4 ) ); // explicitly create unit assertEquals(2, executor.run( new AdultUnit(persons) ) ); // let RuleUnitExecutor internally create and wire the unit instance assertEquals(1, executor.run( NotAdultUnit.class ) ); } @Test public void testBindDataSource() throws Exception { String drl1 = "import " + Person.class.getCanonicalName() + "\n" + "import " + AdultUnit.class.getCanonicalName() + "\n" + "import " + NotAdultUnit.class.getCanonicalName() + "\n" + "rule Adult @Unit( AdultUnit.class ) when\n" + " Person(age >= 18, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is adult\");\n" + "end\n" + "rule NotAdult @Unit( NotAdultUnit.class ) when\n" + " Person(age < 18, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is NOT adult\");\n" + "end"; KieBase kbase = new KieHelper().addContent( drl1, ResourceType.DRL ).build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); DataSource<Person> persons = DataSource.create( new Person( "Mario", 42 ), new Person( "Marilena", 44 ), new Person( "Sofia", 4 ) ); executor.bindVariable( "persons", persons ); // explicitly create unit assertEquals(2, executor.run( AdultUnit.class ) ); // let RuleUnitExecutor internally create and wire the unit instance assertEquals(1, executor.run( NotAdultUnit.class ) ); } @Test public void testUnboundDataSource() throws Exception { // DROOLS-1533 String drl1 = "import " + Person.class.getCanonicalName() + "\n" + "import " + AdultUnit.class.getCanonicalName() + "\n" + "import " + NotAdultUnit.class.getCanonicalName() + "\n" + "rule Adult @Unit( AdultUnit.class ) when\n" + " Person(age >= 18, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is adult\");\n" + "end\n" + "rule NotAdult @Unit( NotAdultUnit.class ) when\n" + " Person(age < 18, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is NOT adult\");\n" + "end"; KieBase kbase = new KieHelper().addContent( drl1, ResourceType.DRL ).build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); DataSource<Person> persons = DataSource.create( new Person( "Mario", 42 ), new Person( "Marilena", 44 ), new Person( "Sofia", 4 ) ); // explicitly create unit assertEquals(2, executor.run( new AdultUnit(persons) ) ); // let RuleUnitExecutor internally create and wire the unit instance assertEquals(1, executor.run( new NotAdultUnit(persons) ) ); } @Test public void testRuleWithoutUnitsIsNotExecutor() throws Exception { String drl1 = "import " + Person.class.getCanonicalName() + "\n" + "import " + AdultUnit.class.getCanonicalName() + "\n" + "import " + NotAdultUnit.class.getCanonicalName() + "\n" + "rule Adult when\n" + " Person(age >= 18, $name : name)\n" + "then\n" + " System.out.println($name + \" is adult\");\n" + "end\n" + "rule NotAdult when\n" + " Person(age < 18, $name : name)\n" + "then\n" + " System.out.println($name + \" is NOT adult\");\n" + "end"; KieBase kbase = new KieHelper().addContent( drl1, ResourceType.DRL ).build(); try { RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); fail("It should throw IllegalStateException"); } catch (IllegalStateException e) { // expected } } @Test public void testRunUnexistingUnit() throws Exception { String drl1 = "import " + Person.class.getCanonicalName() + "\n" + "import " + AdultUnit.class.getCanonicalName() + "\n" + "import " + NotAdultUnit.class.getCanonicalName() + "\n" + "rule Adult @Unit( AdultUnit.class ) when\n" + " Person(age >= 18, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is adult\");\n" + "end"; KieBase kbase = new KieHelper().addContent( drl1, ResourceType.DRL ).build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); try { executor.run( NotAdultUnit.class ); fail("It should throw IllegalStateException"); } catch (IllegalStateException e) { // expected } } @Test public void testDisallowToMixRulesWithAndWithoutUnit() throws Exception { String drl1 = "import " + Person.class.getCanonicalName() + "\n" + "import " + AdultUnit.class.getCanonicalName() + "\n" + "import " + NotAdultUnit.class.getCanonicalName() + "\n" + "rule Adult @Unit( AdultUnit.class ) when\n" + " Person(age >= 18, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is adult\");\n" + "end\n" + "rule NotAdult when\n" + " Person(age < 18, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is NOT adult\");\n" + "end"; try { KieBase kbase = new KieHelper().addContent( drl1, ResourceType.DRL ).build(); fail("It should throw IllegalStateException"); } catch (IllegalStateException e) { // expected } } @Test public void testRuleUnitInvocationFromConsequence() throws Exception { String drl1 = "import " + Person.class.getCanonicalName() + "\n" + "import " + AdultUnit.class.getCanonicalName() + "\n" + "import " + NotAdultUnit.class.getCanonicalName() + "\n" + "rule Adult @Unit( AdultUnit.class ) when\n" + " Person(age >= 18, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is adult\");\n" + "end\n" + "rule NotAdult @Unit( NotAdultUnit.class ) when\n" + " $p : Person(age < 18, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is NOT adult\");\n" + " modify($p) { setAge(18); }\n" + " drools.run( AdultUnit.class );" + "end"; KieBase kbase = new KieHelper().addContent( drl1, ResourceType.DRL ).build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); DataSource<Person> persons = executor.newDataSource( "persons", new Person( "Mario", 42 ), new Person( "Marilena", 44 ), new Person( "Sofia", 4 ) ); List<String> log = new ArrayList<>(); executor.bindVariable( "log", log ); assertEquals(4, executor.run( NotAdultUnit.class ) ); List<String> expectedLogs = asList("org.drools.compiler.integrationtests.RuleUnitTest$NotAdultUnit started.", "org.drools.compiler.integrationtests.RuleUnitTest$NotAdultUnit yielded to org.drools.compiler.integrationtests.RuleUnitTest$AdultUnit", "org.drools.compiler.integrationtests.RuleUnitTest$AdultUnit started.", "org.drools.compiler.integrationtests.RuleUnitTest$AdultUnit ended."); assertEquals( expectedLogs, log ); } @Test public void testModifyOnDataSource() throws Exception { String drl1 = "import " + Person.class.getCanonicalName() + "\n" + "import " + AdultUnit.class.getCanonicalName() + "\n" + "rule Adult @Unit( AdultUnit.class ) when\n" + " Person(age >= 18, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is adult\");\n" + " results.add($name);\n" + "end\n" + "rule NotAdult @Unit( AdultUnit.class ) when\n" + " $p : Person(age < 18, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is NOT adult\");\n" + " modify($p) { setAge(18); }\n" + " drools.run( AdultUnit.class );" + "end"; KieBase kbase = new KieHelper().addContent( drl1, ResourceType.DRL ).build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); DataSource<Person> persons = executor.newDataSource( "persons", new Person( "Mario", 42 ), new Person( "Marilena", 44 ), new Person( "Sofia", 4 ) ); List<String> results = new ArrayList<>(); executor.bindVariable( "results", results ); assertEquals(4, executor.run( AdultUnit.class ) ); assertEquals(3, results.size()); assertTrue(results.containsAll( asList("Mario", "Marilena", "Sofia") )); } public static class AdultUnit implements RuleUnit { private int adultAge = 0; private DataSource<Person> persons; private List<String> log; private List<String> results; public AdultUnit( ) { } public AdultUnit( DataSource<Person> persons ) { this.persons = persons; } public DataSource<Person> getPersons() { return persons; } public int getAdultAge() { return adultAge; } public List<String> getResults() { return results; } @Override public void onStart() { if (log != null) { log.add( getUnitName(this) + " started." ); } else { System.out.println( getUnitName(this) + " started." ); } } @Override public void onEnd() { if (log != null) { log.add( getUnitName(this) + " ended." ); } else { System.out.println( getUnitName(this) + " ended." ); } } @Override public void onYield( RuleUnit other ) { if (log != null) { log.add( getUnitName(this) + " yielded to " + getUnitName(other) ); } else { System.out.println( getUnitName(this) + " yielded to " + getUnitName(other) ); } } } public static class NotAdultUnit implements RuleUnit { private DataSource<Person> persons; private List<String> log; public NotAdultUnit( ) { } public NotAdultUnit( DataSource<Person> persons ) { this.persons = persons; } public DataSource<Person> getPersons() { return persons; } @Override public void onStart() { if (log != null) { log.add( getUnitName(this) + " started." ); } else { System.out.println( getUnitName(this) + " started." ); } } @Override public void onEnd() { if (log != null) { log.add( getUnitName(this) + " ended." ); } else { System.out.println( getUnitName(this) + " ended." ); } } @Override public void onYield( RuleUnit other ) { if (log != null) { log.add( getUnitName(this) + " yielded to " + getUnitName(other) ); } else { System.out.println( getUnitName(this) + " yielded to " + getUnitName(other) ); } } } @Test public void testNotExistingDataSource() throws Exception { String drl1 = "import " + Person.class.getCanonicalName() + "\n" + "import " + AdultUnit.class.getCanonicalName() + "\n" + "rule Adult @Unit( AdultUnit.class ) when\n" + " Person(age >= 18, $name : name) from adults\n" + "then\n" + " System.out.println($name + \" is adult\");\n" + "end"; KieServices ks = KieServices.get(); KieFileSystem kfs = ks.newKieFileSystem().write( "src/main/resources/r1.drl", drl1 ); Results results = ks.newKieBuilder( kfs ).buildAll().getResults(); assertFalse( results.getMessages().isEmpty() ); } @Test public void testReactiveDataSource() throws Exception { String drl1 = "import " + Person.class.getCanonicalName() + "\n" + "import " + ReactiveAdultUnit.class.getCanonicalName() + "\n" + "import " + ReactiveNotAdultUnit.class.getCanonicalName() + "\n" + "rule Adult @Unit( ReactiveAdultUnit.class) when\n" + " Person(age >= 18, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is adult\");\n" + "end\n" + "rule NotAdult @Unit( ReactiveNotAdultUnit.class ) when\n" + " Person(age < 18, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is NOT adult\");\n" + "end"; KieBase kbase = new KieHelper().addContent( drl1, ResourceType.DRL ).build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); DataSource<Person> persons = executor.newDataSource( "persons", new Person( "Mario", 42 ) ); ReactiveAdultUnit adultUnit = new ReactiveAdultUnit(persons, null); assertEquals(1, executor.run( adultUnit ) ); ReactiveNotAdultUnit notAdultUnit = new ReactiveNotAdultUnit(persons); assertEquals(0, executor.run( notAdultUnit ) ); persons.insert( new Person( "Sofia", 4 ) ); assertEquals(0, executor.run( adultUnit ) ); assertEquals(1, executor.run( notAdultUnit ) ); persons.insert( new Person( "Marilena", 44 ) ); assertEquals(1, executor.run( adultUnit ) ); assertEquals(0, executor.run( notAdultUnit ) ); } public static class ReactiveAdultUnit implements RuleUnit { private final DataSource<Person> persons; private final List<String> list; public ReactiveAdultUnit( DataSource<Person> persons, List<String> list ) { this.persons = persons; this.list = list; } public DataSource<Person> getPersons() { return persons; } public List<String> getList() { return list; } @Override public void onStart() { System.out.println(getUnitName(this) + " started."); } @Override public void onEnd() { System.out.println(getUnitName(this) + " ended."); } @Override public void onSuspend() { System.out.println(getUnitName(this) + " suspended."); } @Override public void onResume() { System.out.println(getUnitName(this) + " resumed."); } } public static class ReactiveNotAdultUnit implements RuleUnit { private final DataSource<Person> persons; public ReactiveNotAdultUnit( DataSource<Person> persons ) { this.persons = persons; } public DataSource<Person> getPersons() { return persons; } } @Test(timeout = 10000L) public void testReactiveDataSourceWithRunUntilHalt() throws Exception { String drl1 = "import " + Person.class.getCanonicalName() + "\n" + "import " + ReactiveAdultUnit.class.getCanonicalName() + "\n" + "rule Adult @Unit( ReactiveAdultUnit.class ) when\n" + " Person(age >= 18, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is adult\");" + " list.add($name);\n" + "end"; KieBase kbase = new KieHelper().addContent( drl1, ResourceType.DRL ).build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); DebugList<String> list = new DebugList<>(); executor.bindVariable( "list", list ); DataSource<Person> persons = executor.newDataSource( "persons", new Person( "Mario", 42 ) ); ReactiveAdultUnit adultUnit = new ReactiveAdultUnit(persons, list); Semaphore ready = new Semaphore( 0, true); list.onItemAdded = ( l -> ready.release() ); new Thread( () -> executor.runUntilHalt( adultUnit ) ).start(); ready.acquire(); assertEquals( 1, list.size() ); assertEquals( "Mario", list.get(0) ); list.clear(); list.onItemAdded = ( l -> ready.release() ); persons.insert( new Person( "Sofia", 4 ) ); persons.insert( new Person( "Marilena", 44 ) ); ready.acquire(); assertEquals( 1, list.size() ); assertEquals( "Marilena", list.get(0) ); executor.halt(); } @Test public void testNamingConventionOnDrlFile() throws Exception { String drl1 = "package org.kie.test;\n" + "import " + Person.class.getCanonicalName() + "\n" + "rule Adult when\n" + " $p : /persons[age >= 18]\n" + "then\n" + " System.out.println($p.getName() + \" is adult\");\n" + "end"; String javaRuleUnit = "package org.kie.test;\n" + "\n" + "import " + Person.class.getCanonicalName() + ";\n" + "import " + RuleUnit.class.getCanonicalName() + ";\n" + "import " + DataSource.class.getCanonicalName() + ";\n" + "\n" + "public class MyRuleUnit implements RuleUnit {\n" + " private DataSource<Person> persons;\n" + "\n" + " public DataSource<Person> getPersons() {\n" + " return persons;\n" + " }\n" + "}\n"; String path = "org/kie/test/MyRuleUnit"; KieServices ks = KieServices.get(); KieFileSystem kfs = ks.newKieFileSystem(); kfs.writeKModuleXML(ks.newKieModuleModel().toXML()) .write("src/main/resources/" + path + ".drl", drl1) .write("src/main/java/" + path + ".java", javaRuleUnit); ks.newKieBuilder( kfs ).buildAll(); KieContainer kcontainer = ks.newKieContainer( ks.getRepository().getDefaultReleaseId() ); KieBase kbase = kcontainer.getKieBase(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); DataSource<Person> persons = executor.newDataSource( "persons", new Person( "Mario", 42 ) ); RuleUnit ruleUnit = new RuleUnitFactory().bindVariable( "persons", persons ) .getOrCreateRuleUnit( ( (InternalRuleUnitExecutor) executor ), "org.kie.test.MyRuleUnit", kcontainer.getClassLoader() ); assertEquals(1, executor.run( ruleUnit ) ); persons.insert( new Person( "Sofia", 4 ) ); assertEquals(0, executor.run( ruleUnit ) ); persons.insert( new Person( "Marilena", 44 ) ); assertEquals(1, executor.run( ruleUnit ) ); } @Test public void testWithOOPath() throws Exception { String drl1 = "import " + Person.class.getCanonicalName() + "\n" + "import " + AdultUnit.class.getCanonicalName() + "\n" + "rule Adult @Unit( AdultUnit.class ) when\n" + " $p : /persons[age >= 18]\n" + "then\n" + " System.out.println($p.getName() + \" is adult\");\n" + "end"; KieBase kbase = new KieHelper().addContent( drl1, ResourceType.DRL ).build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); DataSource<Person> persons = executor.newDataSource( "persons", new Person( "Mario", 42 ), new Person( "Marilena", 44 ), new Person( "Sofia", 4 ) ); RuleUnit adultUnit = new AdultUnit(persons); assertEquals(2, executor.run( adultUnit ) ); } @Test public void testVarResolution() throws Exception { String drl1 = "import " + Person.class.getCanonicalName() + "\n" + "import " + AdultUnit.class.getCanonicalName() + "\n" + "rule Adult @Unit( AdultUnit.class ) when\n" + " $p : /persons[age >= adultAge]\n" + "then\n" + " System.out.println($p.getName() + \" is adult and greater than \" + adultAge);\n" + "end"; KieBase kbase = new KieHelper().addContent( drl1, ResourceType.DRL ).build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); DataSource<Person> persons = executor.newDataSource( "persons", new Person( "Mario", 42 ), new Person( "Marilena", 44 ), new Person( "Sofia", 4 ) ); executor.bindVariable( "adultAge", 18 ); assertEquals(2, executor.run( AdultUnit.class ) ); } @Test public void testUnitDeclaration() throws Exception { String drl1 = "package org.drools.compiler.integrationtests\n" + "unit " + getCanonicalSimpleName(AdultUnit.class) + "\n" + "import " + Person.class.getCanonicalName() + "\n" + "rule Adult when\n" + " Person(age >= adultAge, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is adult\");\n" + "end"; String drl2 = "package org.drools.compiler.integrationtests\n" + "unit " + getCanonicalSimpleName(NotAdultUnit.class) + "\n" + "import " + AdultUnit.class.getCanonicalName() + "\n" + "import " + Person.class.getCanonicalName() + "\n" + "rule NotAdult when\n" + " $p : Person(age < 18, $name : name) from persons\n" + "then\n" + " System.out.println($name + \" is NOT adult\");\n" + " modify($p) { setAge(18); }\n" + " drools.run( AdultUnit.class );\n" + "end"; KieBase kbase = new KieHelper().addContent( drl1, ResourceType.DRL ) .addContent( drl2, ResourceType.DRL ) .build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); DataSource<Person> persons = executor.newDataSource( "persons", new Person( "Mario", 42 ), new Person( "Marilena", 44 ), new Person( "Sofia", 4 ) ); List<String> log = new ArrayList<>(); executor.bindVariable( "log", log ) .bindVariable( "adultAge", 18 ); assertEquals(4, executor.run( NotAdultUnit.class ) ); List<String> expectedLogs = asList("org.drools.compiler.integrationtests.RuleUnitTest$NotAdultUnit started.", "org.drools.compiler.integrationtests.RuleUnitTest$NotAdultUnit yielded to org.drools.compiler.integrationtests.RuleUnitTest$AdultUnit", "org.drools.compiler.integrationtests.RuleUnitTest$AdultUnit started.", "org.drools.compiler.integrationtests.RuleUnitTest$AdultUnit ended."); assertEquals( expectedLogs, log ); } @Test public void testBindingWithNamedVars() throws Exception { String drl1 = "import " + Person.class.getCanonicalName() + "\n" + "import " + NamedVarsUnit.class.getCanonicalName() + "\n" + "rule Adult @Unit( NamedVarsUnit.class ) when\n" + " $p : /persons[age >= adultAge]\n" + "then\n" + " System.out.println($p.getName() + \" is adult and greater than \" + adultAge);\n" + "end"; KieBase kbase = new KieHelper().addContent( drl1, ResourceType.DRL ).build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); DataSource<Person> persons = executor.newDataSource( "data", new Person( "Mario", 42 ), new Person( "Marilena", 44 ), new Person( "Sofia", 4 ) ); executor.bindVariable( "minAge", 18 ); assertEquals(2, executor.run( NamedVarsUnit.class ) ); } public static class NamedVarsUnit implements RuleUnit { @UnitVar("minAge") private int adultAge = 0; @UnitVar("data") private DataSource<Person> persons; public NamedVarsUnit( ) { } public DataSource<Person> getPersons() { return persons; } public int getAdultAge() { return adultAge; } } @Test public void testGuardedUnit() throws Exception { String drl1 = "package org.drools.compiler.integrationtests\n" + "unit " + getCanonicalSimpleName( BoxOfficeUnit.class ) + ";\n" + "import " + BoxOffice.class.getCanonicalName() + "\n" + "import " + TicketIssuerUnit.class.getCanonicalName() + "\n" + "\n" + "rule BoxOfficeIsOpen when\n" + " $box: /boxOffices[ open ]\n" + "then\n" + " drools.guard( TicketIssuerUnit.class );" + "end"; String drl2 = "package org.drools.compiler.integrationtests\n" + "unit " + getCanonicalSimpleName( TicketIssuerUnit.class ) + ";\n" + "import " + Person.class.getCanonicalName() + "\n" + "import " + AdultTicket.class.getCanonicalName() + "\n" + "rule IssueAdultTicket when\n" + " $p: /persons[ age >= 18 ]\n" + "then\n" + " tickets.insert(new AdultTicket($p));\n" + "end\n" + "rule RegisterAdultTicket when\n" + " $t: /tickets\n" + "then\n" + " results.add( $t.getPerson().getName() );\n" + "end"; KieBase kbase = new KieHelper().addContent( drl1, ResourceType.DRL ) .addContent( drl2, ResourceType.DRL ) .build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); DataSource<Person> persons = executor.newDataSource( "persons" ); DataSource<BoxOffice> boxOffices = executor.newDataSource( "boxOffices" ); DataSource<AdultTicket> tickets = executor.newDataSource( "tickets" ); List<String> list = new ArrayList<>(); executor.bindVariable( "results", list ); // two open box offices BoxOffice office1 = new BoxOffice(true); FactHandle officeFH1 = boxOffices.insert( office1 ); BoxOffice office2 = new BoxOffice(true); FactHandle officeFH2 = boxOffices.insert( office2 ); persons.insert(new Person("Mario", 40)); executor.run(BoxOfficeUnit.class); // fire BoxOfficeIsOpen -> run TicketIssuerUnit -> fire RegisterAdultTicket assertEquals( 1, list.size() ); assertEquals( "Mario", list.get(0) ); list.clear(); persons.insert(new Person("Matteo", 30)); executor.run(BoxOfficeUnit.class); // fire RegisterAdultTicket assertEquals( 1, list.size() ); assertEquals( "Matteo", list.get(0) ); list.clear(); // close one box office, the other is still open office1.setOpen(false); boxOffices.update(officeFH1, office1); persons.insert(new Person("Mark", 35)); executor.run(BoxOfficeUnit.class); assertEquals( 1, list.size() ); assertEquals( "Mark", list.get(0) ); list.clear(); // all box offices, are now closed office2.setOpen(false); boxOffices.update(officeFH2, office2); // guarding rule no longer true persons.insert(new Person("Edson", 35)); executor.run(BoxOfficeUnit.class); // no fire assertEquals( 0, list.size() ); } public static class BoxOffice { private boolean open; public BoxOffice( boolean open ) { this.open = open; } public boolean isOpen() { return open; } public void setOpen( boolean open ) { this.open = open; } } public static class AdultTicket { private final Person person; public AdultTicket( Person person ) { this.person = person; } public Person getPerson() { return person; } } public static class BoxOfficeUnit implements RuleUnit { private DataSource<BoxOffice> boxOffices; public DataSource<BoxOffice> getBoxOffices() { return boxOffices; } } public static class TicketIssuerUnit implements RuleUnit { private DataSource<Person> persons; private DataSource<AdultTicket> tickets; private List<String> results; public TicketIssuerUnit() { } public TicketIssuerUnit( DataSource<Person> persons, DataSource<AdultTicket> tickets ) { this.persons = persons; this.tickets = tickets; } public DataSource<Person> getPersons() { return persons; } public DataSource<AdultTicket> getTickets() { return tickets; } public List<String> getResults() { return results; } } @Test public void testMultiLevelGuards() throws Exception { String drl1 = "package org.drools.compiler.integrationtests\n" + "unit " + getCanonicalSimpleName( Unit0.class ) + "\n" + "import " + UnitA.class.getCanonicalName() + "\n" + "rule X when\n" + " $b: /ds#Boolean\n" + "then\n" + " Boolean b = $b;\n" + " drools.guard( UnitA.class );\n" + "end"; String drl2 = "package org.drools.compiler.integrationtests\n" + "unit " + getCanonicalSimpleName( UnitA.class ) + "\n" + "import " + UnitB.class.getCanonicalName() + "\n" + "rule A when\n" + " $s: /ds#String\n" + "then\n" + " drools.guard( UnitB.class );" + "end"; String drl3 = "package org.drools.compiler.integrationtests\n" + "unit " + getCanonicalSimpleName( UnitB.class ) + "\n" + "import " + UnitB.class.getCanonicalName() + "\n" + "rule B when\n" + " $i: /ds#Integer\n" + "then\n" + " list.add($i);" + "end"; KieBase kbase = new KieHelper().addContent( drl1, ResourceType.DRL ) .addContent( drl2, ResourceType.DRL ) .addContent( drl3, ResourceType.DRL ) .build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); DataSource<Object> ds = executor.newDataSource( "ds" ); List<Integer> list = new ArrayList<>(); executor.bindVariable( "list", list ); ds.insert( 1 ); executor.run(Unit0.class); assertEquals( 0, list.size() ); // all units are inactive FactHandle guardA = ds.insert( true ); executor.run(Unit0.class); assertEquals( 0, list.size() ); // UnitB still inactive FactHandle guardB = ds.insert( "test" ); executor.run(Unit0.class); assertEquals( 1, list.size() ); // all units are active assertEquals( 1, (int)list.get(0) ); // all units are active list.clear(); ds.insert( 2 ); executor.run(Unit0.class); assertEquals( 1, list.size() ); // all units are inactive assertEquals( 2, (int)list.get(0) ); // all units are active list.clear(); ds.delete( guardA ); // retracting guard A deactivate unitA and in cascade unit B ds.insert( 3 ); executor.run(Unit0.class); assertEquals( 0, list.size() ); // all units are inactive guardA = ds.insert( true ); // activating guard A reactivate unitA and in cascade unit B executor.run(Unit0.class); assertEquals( 1, list.size() ); // all units are active list.clear(); } public static class Unit0 implements RuleUnit { private DataSource<Object> ds; public DataSource<Object> getDs() { return ds; } } public static class UnitA implements RuleUnit { private DataSource<Object> ds; public DataSource<Object> getDs() { return ds; } } public static class UnitB implements RuleUnit { private DataSource<Object> ds; private List<Integer> list; public DataSource<Object> getDs() { return ds; } public List<Integer> getList() { return list; } } @Test public void testRuleUnitIdentity() throws Exception { String drl1 = "package org.drools.compiler.integrationtests\n" + "unit " + getCanonicalSimpleName( Unit0.class ) + "\n" + "import " + AgeCheckUnit.class.getCanonicalName() + "\n" + "\n" + "rule R1 when\n" + " $i: /ds#Integer\n" + "then\n" + " drools.guard( new AgeCheckUnit($i) );" + "end\n" + "rule RegisterAdultTicket when\n" + " $s: /ds#String\n" + "then\n" + " drools.guard( new AgeCheckUnit($s.length()) );" + "end"; String drl2 = "package org.drools.compiler.integrationtests\n" + "unit " + getCanonicalSimpleName( AgeCheckUnit.class ) + ";\n" + "import " + Person.class.getCanonicalName() + "\n" + "rule CheckAge when\n" + " $p : /persons[ age > minAge ]\n" + "then\n" + " list.add($p.getName() + \">\" + minAge);\n" + "end"; KieBase kbase = new KieHelper().addContent( drl1, ResourceType.DRL ) .addContent( drl2, ResourceType.DRL ) .build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); DataSource<Object> ds = executor.newDataSource( "ds" ); DataSource<Person> persons = executor.newDataSource( "persons", new Person( "Mario", 42 ), new Person( "Sofia", 4 ) ); List<String> list = new ArrayList<>(); executor.bindVariable( "list", list ); ds.insert("test"); ds.insert(3); ds.insert(4); executor.run(Unit0.class); System.out.println(list); assertEquals(3, list.size()); assertTrue( list.containsAll( asList("Mario>4", "Mario>3", "Sofia>3") ) ); list.clear(); ds.insert("xxx"); ds.insert("yyyy"); executor.run(Unit0.class); assertEquals(0, list.size()); } public static class AgeCheckUnit implements RuleUnit { private final int minAge; private DataSource<Person> persons; private List<String> list; public AgeCheckUnit( int minAge ) { this.minAge = minAge; } public DataSource<Person> getPersons() { return persons; } public int getMinAge() { return minAge; } public List<String> getList() { return list; } @Override public Identity getUnitIdentity() { return new Identity(getClass(), minAge); } @Override public String toString() { return "AgeCheckUnit(" + minAge + ")"; } } @Test(timeout = 10000L) public void testPropertyReactiveModify() throws Exception { String drl1 = "package org.drools.compiler.integrationtests\n" + "unit " + getCanonicalSimpleName( AdultUnit.class ) + "\n" + "import " + Person.class.getCanonicalName() + "\n" + "rule Adult when\n" + " $p: /persons[ age < 18 ]\n" + "then\n" + " System.out.println($p.getName() + \" is NOT adult\");\n" + " modify($p) { setHappy(true); }\n" + "end"; KieBase kbase = new KieHelper( PropertySpecificOption.ALWAYS ).addContent( drl1, ResourceType.DRL ).build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); Person mario = new Person( "Mario", 42 ); Person sofia = new Person( "Sofia", 4 ); DataSource<Person> persons = executor.newDataSource( "persons" ); FactHandle marioFh = persons.insert( mario ); FactHandle sofiaFh = persons.insert( sofia ); executor.run( AdultUnit.class ); assertTrue( sofia.isHappy() ); assertFalse( mario.isHappy() ); sofia.setAge( 5 ); persons.update( sofiaFh, sofia, "age" ); assertEquals( 1, executor.run( AdultUnit.class ) ); sofia.setSex( 'F' ); persons.update( sofiaFh, sofia, "sex" ); assertEquals( 0, executor.run( AdultUnit.class ) ); } @Test public void testTwoPartsOOPath() throws Exception { // DROOLS-1539 String drl1 = "package org.drools.compiler.integrationtests\n" + "unit " + getCanonicalSimpleName( AdultUnit.class ) + "\n" + "import " + Person.class.getCanonicalName() + "\n" + "import " + LongAddress.class.getCanonicalName() + "\n" + "rule Adult when\n" + " $a: /persons[ age > 18 ]/addresses#LongAddress[ country == \"it\" ]\n" + "then\n" + " System.out.println($a.getCountry());\n" + "end"; KieBase kbase = new KieHelper( PropertySpecificOption.ALWAYS ).addContent( drl1, ResourceType.DRL ).build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); Person mario = new Person( "Mario", 43 ); mario.setAddresses(asList( new LongAddress( "street", "suburb", "zipCode", "it") ) ); Person mark = new Person( "Mark", 40 ); mark.setAddresses(asList( new LongAddress( "street", "suburb", "zipCode", "uk") ) ); DataSource<Person> persons = executor.newDataSource( "persons", mario, mark ); assertEquals( 1, executor.run( AdultUnit.class ) ); } @Test public void testNestedOOPath() throws Exception { // DROOLS-1539 String drl1 = "package org.drools.compiler.integrationtests\n" + "unit " + getCanonicalSimpleName( AdultUnit.class ) + "\n" + "import " + Person.class.getCanonicalName() + "\n" + "import " + LongAddress.class.getCanonicalName() + "\n" + "rule Adult when\n" + " $p: /persons[ age > 18, $a: /addresses#LongAddress[ country == \"it\" ] ]\n" + "then\n" + " System.out.println($p.getName() + \" is in \" + $a.getCountry());\n" + "end"; KieBase kbase = new KieHelper( PropertySpecificOption.ALWAYS ).addContent( drl1, ResourceType.DRL ).build(); RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); Person mario = new Person( "Mario", 43 ); mario.setAddresses(asList( new LongAddress( "street", "suburb", "zipCode", "it") ) ); Person mark = new Person( "Mark", 40 ); mark.setAddresses(asList( new LongAddress( "street", "suburb", "zipCode", "uk") ) ); DataSource<Person> persons = executor.newDataSource( "persons", mario, mark ); assertEquals( 1, executor.run( AdultUnit.class ) ); } }