State Example

This example is implemented in three different versions to demonstrate different ways of implementing the same basic behavior: forward chaining, i.e., the ability the engine has to evaluate, activate and fire rules in sequence, based on changes on the facts in the Working Memory.

Understanding the State Example

Name: State Example
Main class: org.drools.examples.StateExampleUsingSalience
Type: Java application
Rules file: StateExampleUsingSalience.drl
Objective: Demonstrates basic rule use
  and Conflict Resolution for rule firing priority.

Each State class has fields for its name and its current state (see the class org.drools.examples.State). The two possible states for each objects are:

  • NOTRUN

  • FINISHED

Example 8.9. State Class

public class State {
    public static final int NOTRUN   = 0;
    public static final int FINISHED = 1;

    private final PropertyChangeSupport changes =
        new PropertyChangeSupport( this );

    private String name;
    private int    state;

    ... setters and getters go here...
}

Ignoring the PropertyChangeSupport, which will be explained later, we see the creation of four State objects named A, B, C and D. Initially their states are set to NOTRUN, which is default for the used constructor. Each instance is asserted in turn into the Session and then fireAllRules() is called.

Example 8.10. Salience State: Execution

State a = new State( "A" );
State b = new State( "B" );
State c = new State( "C" );
final State d = new State( "D" );

// By setting dynamic to TRUE, Drools will use JavaBean
// PropertyChangeListeners so you don't have to call modify or update().
boolean dynamic = true;

session.insert( a, dynamic );
session.insert( b, dynamic );
session.insert( c, dynamic );
session.insert( d, dynamic );

session.fireAllRules();
session.dispose(); // Stateful rule session must always be disposed when finished

To execute the application:

  1. Open the class org.drools.examples.StateExampleUsingSalience in your Eclipse IDE.

  2. Right-click the class and select "Run as..." and then "Java application"

You will see the following output in the Eclipse console window:

Example 8.11. Salience State: Console Output

A finished
B finished
C finished
D finished

There are four rules in total. First, the Bootstrap rule fires, setting A to state FINISHED, which then causes B to change its state to FINISHED. C and D are both dependent on B, causing a conflict which is resolved by the salience values. Let's look at the way this was executed.

The best way to understand what is happening is to use the Audit Logging feature to graphically see the results of each operation. To view the Audit log generated by a run of this example:

  1. If the Audit View is not visible, click on "Window" and then select "Show View", then "Other..." and "Drools" and finally "Audit View".

  2. In the "Audit View" click the "Open Log" button and select the file "<drools-examples-drl-dir>/log/state.log".

After that, the "Audit view" will look like the following screenshot:

Figure 8.4. Salience State Example Audit View

Salience State Example Audit View

Reading the log in the "Audit View", top to bottom, we see every action and the corresponding changes in the Working Memory. This way we observe that the assertion of the State object A in the state NOTRUN activates the Bootstrap rule, while the assertions of the other State objects have no immediate effect.

Example 8.12. Salience State: Rule "Bootstrap"

rule Bootstrap
    when
        a : State(name == "A", state == State.NOTRUN )
    then
        System.out.println(a.getName() + " finished" );
        a.setState( State.FINISHED );
end

The execution of rule Bootstrap changes the state of A to FINISHED, which, in turn, activates rule "A to B".

Example 8.13. Salience State: Rule "A to B"

rule "A to B"
    when
        State(name == "A", state == State.FINISHED )
        b : State(name == "B", state == State.NOTRUN )
    then
        System.out.println(b.getName() + " finished" );
        b.setState( State.FINISHED );
end

The execution of rule "A to B" changes the state of B to FINISHED, which activates both, rules "B to C" and "B to D", placing their Activations onto the Agenda. From this moment on, both rules may fire and, therefore, they are said to be "in conflict". The conflict resolution strategy allows the engine's Agenda to decide which rule to fire. As rule "B to C" has the higher salience value (10 versus the default salience value of 0), it fires first, modifying object C to state FINISHED. The Audit view shown above reflects the modification of the State object in the rule "A to B", which results in two activations being in conflict. The Agenda view can also be used to investigate the state of the Agenda, with debug points being placed in the rules themselves and the Agenda view opened. The screen shot below shows the breakpoint in the rule "A to B" and the state of the Agenda with the two conflicting rules.

Figure 8.5. State Example Agenda View

State Example Agenda View

Example 8.14. Salience State: Rule "B to C"

rule "B to C"
        salience 10
    when
        State(name == "B", state == State.FINISHED )
        c : State(name == "C", state == State.NOTRUN )
    then
        System.out.println(c.getName() + " finished" );
        c.setState( State.FINISHED );
end

Rule "B to D" fires last, modifying object D to state FINISHED.

Example 8.15. Salience State: Rule "B to D"

rule "B to D"
    when
        State(name == "B", state == State.FINISHED )
        d : State(name == "D", state == State.NOTRUN )
    then
        System.out.println(d.getName() + " finished" );
        d.setState( State.FINISHED );
end

There are no more rules to execute and so the engine stops.

Another notable concept in this example is the use of dynamic facts, based on PropertyChangeListener objects. As described in the documentation, in order for the engine to see and react to changes of fact properties, the application must tell the engine that changes occurred. This can be done explicitly in the rules by using the <kw>modify</kw> statement, or implicitly by letting the engine know that the facts implement PropertyChangeSupport as defined by the JavaBeans specification. This example demonstrates how to use PropertyChangeSupport to avoid the need for explicit <kw>modify</kw> statements in the rules. To make use of this feature, ensure that your facts implement PropertyChangeSupport, the same way the class org.drools.example.State does, and use the following code to insert the facts into the Working Memory:

Example 8.16. Inserting a Dynamic Fact

// By setting dynamic to TRUE, Drools will use JavaBean
// PropertyChangeListeners so you don't have to call modify or update().
final boolean dynamic = true;

session.insert( fact, dynamic );

When using PropertyChangeListener objects, each setter must implement a little extra code for the notification. Here is the setter for state in the class org.drools.examples:

:

Example 8.17. Setter Example with PropertyChangeSupport

public void setState(final int newState) {
    int oldState = this.state;
    this.state = newState;
    this.changes.firePropertyChange( "state",
                                     oldState,
                                     newState );
}

There are two other classes in this example: StateExampleUsingAgendGroup and StateExampleWithDynamicRules. Both execute from A to B to C to D, as just shown. The StateExampleUsingAgendGroup uses agenda-groups to control the rule conflict and which one fires first. StateExampleWithDynamicRules shows how an additional rule can be added to an already running Working Memory with all the existing data applying to it at runtime.

Agenda groups are a way to partition the Agenda into groups and to control which groups can execute. By default, all rules are in the agenda group "MAIN". The "agenda-group" attribute lets you specify a different agenda group for the rule. Initially, a Working Memory has its focus on the Agenda group "MAIN". A group's rules will only fire when the group receives the focus. This can be achieved either ny using the method by setFocus() or the rule attribute <kw>auto-focus</kw>. "auto-focus" means that the rule automatically sets the focus to its agenda group when the rule is matched and activated. It is this "auto-focus" that enables rule "B to C" to fire before "B to D".

Example 8.18. Agenda Group State Example: Rule "B to C"

rule "B to C"
      agenda-group "B to C"
      auto-focus true       
  when
      State(name == "B", state == State.FINISHED )      
      c : State(name == "C", state == State.NOTRUN )
  then
      System.out.println(c.getName() + " finished" );
      c.setState( State.FINISHED );
      kcontext.getKnowledgeRuntime().getAgenda().getAgendaGroup( "B to D" ).setFocus();
end

The rule "B to C" calls setFocus() on the agenda group "B to D", allowing its active rules to fire, which allows the rule "B to D" to fire.

Example 8.19. Agenda Group State Example: Rule "B to D"

rule "B to D"
      agenda-group "B to D"
  when
      State(name == "B", state == State.FINISHED )      
      d : State(name == "D", state == State.NOTRUN )
  then
      System.out.println(d.getName() + " finished" );
      d.setState( State.FINISHED );
end

The example StateExampleWithDynamicRules adds another rule to the Rule Base after fireAllRules(). The added rule is just another state transition.

Example 8.20. Dynamic State Example: Rule "D to E"

rule "D to E"
  when
      State(name == "D", state == State.FINISHED )      
      e : State(name == "E", state == State.NOTRUN )
  then
      System.out.println(e.getName() + " finished" );
      e.setState( State.FINISHED );
end

This produces the following expected output:

Example 8.21. Dynamic Sate Example Output

A finished
B finished
C finished
D finished
E finished