16.12.2. Rule unit execution control
Rule units are helpful when you want to coordinate rule execution so that the execution of one rule unit triggers the start of another rule unit and so on.
To facilitate rule unit execution control, the decision engine supports the following rule unit methods that you can use in DRL rule actions to coordinate the execution of rule units:
-
drools.run()
: Triggers the execution of a specified rule unit class. This method imperatively interrupts the execution of the rule unit and activates the other specified rule unit. -
drools.guard()
: Prevents (guards) a specified rule unit class from being executed until the associated rule condition is met. This method declaratively schedules the execution of the other specified rule unit. When the decision engine produces at least one match for the condition in the guarding rule, the guarded rule unit is considered active. A rule unit can contain multiple guarding rules.
As an example of the drools.run()
method, consider the following DRL rules that each belong to a specified rule unit. The NotAdult
rule uses the drools.run( AdultUnit.class )
method to trigger the execution of the AdultUnit
rule unit:
Example DRL rules with controlled execution using drools.run()
package org.mypackage.myunit unit AdultUnit rule Adult when Person(age >= 18, $name : name) from persons then System.out.println($name + " is adult"); end
package org.mypackage.myunit unit NotAdultUnit rule NotAdult when $p : Person(age < 18, $name : name) from persons then System.out.println($name + " is NOT adult"); modify($p) { setAge(18); } drools.run( AdultUnit.class ); end
The example also uses a RuleUnitExecutor
class created from the KIE base that was built from these rules and a DataSource
definition of persons
bound to it:
Example rule executor and data source definitions
RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); DataSource<Person> persons = executor.newDataSource( "persons", new Person( "John", 42 ), new Person( "Jane", 44 ), new Person( "Sally", 4 ) );
In this case, the example creates the DataSource
definition directly from the RuleUnitExecutor
class and binds it to the "persons"
variable in a single statement.
The example execution code produces the following output when the relevant Person
facts are inserted in the persons
data source:
Example rule unit execution output
Sally is NOT adult John is adult Jane is adult Sally is adult
The NotAdult
rule detects a match when evaluating the person "Sally"
, who is under 18 years old. The rule then modifies her age to 18
and uses the drools.run( AdultUnit.class )
method to trigger the execution of the AdultUnit
rule unit. The AdultUnit
rule unit contains a rule that can now be executed for all of the 3 persons
in the DataSource
definition.
As an example of the drools.guard()
method, consider the following BoxOffice
class and BoxOfficeUnit
rule unit class:
Example BoxOffice
class
public 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; } }
Example BoxOfficeUnit
rule unit class
public class BoxOfficeUnit implements RuleUnit { private DataSource<BoxOffice> boxOffices; public DataSource<BoxOffice> getBoxOffices() { return boxOffices; } }
The example also uses the following TicketIssuerUnit
rule unit class to keep selling box office tickets for the event as long as at least one box office is open. This rule unit uses DataSource
definitions of persons
and tickets
:
Example TicketIssuerUnit
rule unit class
public 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; } }
The BoxOfficeUnit
rule unit contains a BoxOfficeIsOpen
DRL rule that uses the drools.guard( TicketIssuerUnit.class )
method to guard the execution of the TicketIssuerUnit
rule unit that distributes the event tickets, as shown in the following DRL rule examples:
Example DRL rules with controlled execution using drools.guard()
package org.mypackage.myunit; unit TicketIssuerUnit; rule IssueAdultTicket when $p: /persons[ age >= 18 ] then tickets.insert(new AdultTicket($p)); end rule RegisterAdultTicket when $t: /tickets then results.add( $t.getPerson().getName() ); end
package org.mypackage.myunit; unit BoxOfficeUnit; rule BoxOfficeIsOpen when $box: /boxOffices[ open ] then drools.guard( TicketIssuerUnit.class ); end
In this example, so long as at least one box office is open
, the guarded TicketIssuerUnit
rule unit is active and distributes event tickets. When no more box offices are in open
state, the guarded TicketIssuerUnit
rule unit is prevented from being executed.
The following example class illustrates a more complete box office scenario:
Example class for the box office scenario
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 box offices are open: BoxOffice office1 = new BoxOffice(true); FactHandle officeFH1 = boxOffices.insert( office1 ); BoxOffice office2 = new BoxOffice(true); FactHandle officeFH2 = boxOffices.insert( office2 ); persons.insert(new Person("John", 40)); // Execute `BoxOfficeIsOpen` rule, run `TicketIssuerUnit` rule unit, and execute `RegisterAdultTicket` rule: executor.run(BoxOfficeUnit.class); assertEquals( 1, list.size() ); assertEquals( "John", list.get(0) ); list.clear(); persons.insert(new Person("Matteo", 30)); // Execute `RegisterAdultTicket` rule: executor.run(BoxOfficeUnit.class); assertEquals( 1, list.size() ); assertEquals( "Matteo", list.get(0) ); list.clear(); // One box office is closed, the other is 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 closed: office2.setOpen(false); boxOffices.update(officeFH2, office2); // Guarding rule is no longer true. persons.insert(new Person("Edson", 35)); executor.run(BoxOfficeUnit.class); // No execution assertEquals( 0, list.size() );