JBoss.orgCommunity Documentation
Make sure the Drools Eclipse plugin is installed, which needs the Graphical Editing Framework (GEF) dependency installed first. Then download and extract the drools-examples zip file, which includes an already created Eclipse project. Import that project into a new Eclipse workspace. The rules all have example classes that execute the rules. If you want to try the examples in another project (or another IDE) then you will need to set up the dependencies by hand, of course. Many, but not all of the examples are documented below, enjoy!
Some examples require Java 1.6 to run.
Name: Hello World Main class: org.drools.examples.helloworld.HelloWorldExample Module: drools-examples Type: Java application Rules file: HelloWorld.drl Objective: demonstrate basic rules in use
The "Hello World" example shows a simple example of rules usage, and both the MVEL and Java dialects.
This example demonstrates how to build Knowledge Bases and Sessions.
  Also, audit logging and debug outputs are shown, which is omitted
  from other examples as it's all very similar. A KnowledgeBuilder
  is used to turn a DRL source file into Package objects which
  the Knowledge Base can consume. The add method takes a Resource
  interface and a Resource Type as parameters. The Resource can be 
  used to retrieve a DRL source file from various locations; in this case the
  DRL file is being retrieved from the classpath using a
  ResourceFactory, but it could come from a disk file or a URL.
  Here, we only add a single DRL source file, but multiple DRL files can be 
  added. Also, DRL files with different namespaces can be added, where
  the Knowledge Builder creates a package for each namespace. Multiple
  packages of different namespaces can be added to the same Knowledge Base.
  When all the DRL files have been added, we should check the builder for 
  errors. While the Knowledge Base will validate the package, it will only
  have access to the error information as a String, so if you wish to debug
  the error information you should do it on the KnowledgeBuilder
  instance. Once you know the builder is error free, get the
  Package collection, instantiate a KnowledgeBase
  from the KnowledgeBaseFactory and add the package
  collection.
Example 7.1. HelloWorld: Creating the KnowledgeBase and Session
final KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
// this will parse and compile in one step
kbuilder.add(ResourceFactory.newClassPathResource("HelloWorld.drl",
HelloWorldExample.class), ResourceType.DRL);
// Check the builder for errors
if (kbuilder.hasErrors()) {
System.out.println(kbuilder.getErrors().toString());
throw new RuntimeException("Unable to compile \"HelloWorld.drl\".");
}
// get the compiled packages (which are serializable)
final Collection<KnowledgePackage> pkgs = kbuilder.getKnowledgePackages();
// add the packages to a KnowledgeBase (deploy the knowledge packages).
final KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages(pkgs);
final StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
Drools has an event model that exposes much of what's happening
  internally. Two default debug listeners are supplied,
  DebugAgendaEventListener and 
  DebugWorkingMemoryEventListener which print out
  debug event information to the System.err stream displayed
  in the Console window. Adding listeners to a
  Session is trivial, as shown below. The KnowledgeRuntimeLogger
  provides execution auditing, the result of which can be viewed in a
  graphical viewer. The logger is actually a specialised implementation
  built on the Agenda and Working Memory listeners. When the engine has
  finished executing, logger.close() must be called.
Most of the examples use the Audit logging features of Drools to record execution flow for later inspection.
Example 7.2. HelloWorld: Event logging and Auditing
// setup the debug listeners
ksession.addEventListener( new DebugAgendaEventListener() );
ksession.addEventListener( new DebugWorkingMemoryEventListener() );
// setup the audit logging
KnowledgeRuntimeLogger logger =
KnowledgeRuntimeLoggerFactory.newFileLogger(ksession, "log/helloworld");
The single class used in this example is very simple. It has two
  fields: the message, which is a String and the status which can be one
  of the two integers HELLO or GOODBYE.
Example 7.3. HelloWorld example: Message Class
public static class Message {
public static final int HELLO = 0;
public static final int GOODBYE = 1;
private String message;
private int status;
...
}
A single Message object is created with the
  message text "Hello World" and the status HELLO and then
  inserted into the engine, at which point fireAllRules()
  is executed. Remember that all the network evaluation is done
  during the insert time, so that by the time the program execution reaches the
  fireAllRules() method call the engine already knows which rules
  are fully matches and able to fire.
Example 7.4. HelloWorld: Execution
final Message message = new Message();
message.setMessage("Hello World");
message.setStatus(Message.HELLO);
ksession.insert(message);
ksession.fireAllRules();
logger.close();
ksession.dispose();
To execute the example as a Java application:
Open the class org.drools.examples.helloworld.HelloWorldExample in your Eclipse IDE
Right-click the class and select "Run as..." and then "Java application"
If we put a breakpoint on the fireAllRules() method
  and select the ksession variable, we can see that the
  "Hello World" rule is already activated and on the Agenda, confirming
  that all the pattern matching work was already done during the insert.
The may application print outs go to to System.out while the debug listener print outs go to System.err.
Example 7.6. HelloWorld: System.err in the Console window
==>[ActivationCreated(0): rule=Hello World; 
                   tuple=[fid:1:1:org.drools.examples.helloworld.HelloWorldExample$Message@17cec96]]
[ObjectInserted: handle=[fid:1:1:org.drools.examples.helloworld.HelloWorldExample$Message@17cec96];
                 object=org.drools.examples.helloworld.HelloWorldExample$Message@17cec96]
[BeforeActivationFired: rule=Hello World; 
                   tuple=[fid:1:1:org.drools.examples.helloworld.HelloWorldExample$Message@17cec96]]
==>[ActivationCreated(4): rule=Good Bye; 
                   tuple=[fid:1:2:org.drools.examples.helloworld.HelloWorldExample$Message@17cec96]]
[ObjectUpdated: handle=[fid:1:2:org.drools.examples.helloworld.HelloWorldExample$Message@17cec96];
                old_object=org.drools.examples.helloworld.HelloWorldExample$Message@17cec96;
                new_object=org.drools.examples.helloworld.HelloWorldExample$Message@17cec96]
[AfterActivationFired(0): rule=Hello World]
[BeforeActivationFired: rule=Good Bye; 
                   tuple=[fid:1:2:org.drools.examples.helloworld.HelloWorldExample$Message@17cec96]]
[AfterActivationFired(4): rule=Good Bye]  The LHS (after when) section of the rule states that it will be
  activated for each Message object inserted into the Working
  Memory whose status is Message.HELLO. Besides that, two 
  variable bindings are created: the variable message is bound
  to the message attribute and the variable m
  is bound to the matched Message object itself.
The RHS (after then) or consequence part of the rule is
  written using the MVEL expression language, as declared by
  the rule's attribute dialect. After printing the content of
  the bound variable message to System.out,
  the rule changes the values of the message and
  status attributes of the Message object
  bound to m. This is done MVEL's modify statement,
  which allows you to apply a block of assignments in one statement, with the
  engine being automatically notified of the changes at the end of the 
  block.
Example 7.7. HelloWorld: rule "Hello World"
rule "Hello World"
      dialect "mvel"
  when
      m : Message( status == Message.HELLO, message : message )
  then
      System.out.println( message ); 
      modify ( m ) { message = "Goodbye cruel world",
                     status = Message.GOODBYE };
endWe can set a breakpoint into the DRL, on the modify
  call, and inspect the Agenda view again during the execution of the
  rule's consequence. This time we start the execution via "Debug As"
  and "Drools application" and not by running a "Java application":
Open the class org.drools.examples.HelloWorld in your
      Eclipse IDE.
Right-click the class and select "Debug as..." and then "Drools application".
Now we can see that the other rule "Good Bye", which
  uses the Java dialect, is activated and placed on the Agenda.
The "Good Bye" rule, which specifies the "java" dialect, is similar
  to the "Hello World" rule except that it matches Message
  objects whose  status is Message.GOODBYE.
Example 7.8. HelloWorld: rule "Good Bye"
rule "Good Bye"
      dialect "java"
  when
      Message( status == Message.GOODBYE, message : message )
  then
      System.out.println( message ); 
endRemember the Java code where we used the
  KnowledgeRuntimeLoggerFactory method newFileLogger
  to create a KnowledgeRuntimeLogger and called
  logger.close() at the end. This created an audit log file that
  can be shown in the Audit view. We use the Audit view in many of the
  examples to demonstrate the example execution flow. In the view screen shot
  below we can see that the object is inserted, which creates an activation
  for the "Hello World" rule; the activation is then executed which updates
  the Message object causing the "Good Bye" rule to
  activate; finally the "Good Bye" rule also executes. Selecting an event in
  the Audit view highlights the origin event in green; therefore the
  "Activation created" event is highlighted in green as the origin of the
  "Activation executed" event.
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.
Name: State Example Main class: org.drools.examples.state.StateExampleUsingSalience Module: drools-examples 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.State).
      The two possible states for each objects are:
NOTRUN
FINISHED
Example 7.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 7.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:
Open the class org.drools.examples.state.StateExampleUsingSalience in your Eclipse IDE.
Right-click the class and select "Run as..." and then "Java application"
You will see the following output in the Eclipse console window:
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:
If the Audit View is not visible, click on "Window" and then select "Show View", then "Other..." and "Drools" and finally "Audit View".
In the "Audit View" click the "Open Log" button and select the file "<drools-examples-dir>/log/state.log".
After that, the "Audit view" will look like the following screenshot:
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 7.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 );
endThe execution of rule Bootstrap changes the state of A to
      FINISHED, which, in turn, activates rule "A to B".
Example 7.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.
Example 7.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 7.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 );
endThere 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
      modify 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 modify 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 7.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 7.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:
      StateExampleUsingAgendaGroup and
      StateExampleWithDynamicRules. Both execute from A to B
      to C to D, as just shown. The StateExampleUsingAgendaGroup
      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 auto-focus. "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 7.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();
endThe 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 7.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 );
endThe example StateExampleWithDynamicRules adds
      another rule to the Rule Base after fireAllRules().
      The added rule is just another state transition.
Example 7.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 );
endThis produces the following expected output:
Name: Fibonacci 
Main class: org.drools.examples.fibonacci.FibonacciExample
Module: drools-examples
Type: Java application
Rules file: Fibonacci.drl
Objective: Demonstrates Recursion,
  the CE not and cross product matchingThe Fibonacci Numbers (see http://en.wikipedia.org/wiki/Fibonacci_number) discovered by Leonardo of Pisa (see http://en.wikipedia.org/wiki/Fibonacci) is a sequence that starts with 0 and 1. The next Fibonacci number is obtained by adding the two preceding Fibonacci numbers. The Fibonacci sequence begins with 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765, 10946,... The Fibonacci Example demonstrates recursion and conflict resolution with salience values.
The single fact class Fibonacci is used in this
    example. It has two fields, sequence and value. The sequence field
    is used to indicate the position of the object in the Fibonacci
    number sequence. The value field shows the value of that
    Fibonacci object for that sequence position, using -1 to indicate
    a value that still needs to be computed.
Example 7.22. Fibonacci Class
public static class Fibonacci {
private int sequence;
private long value;
public Fibonacci( final int sequence ) {
this.sequence = sequence;
this.value = -1;
}
... setters and getters go here...
}
Execute the example:
Open the class org.drools.examples.fibonacci.FibonacciExample in your Eclipse IDE.
Right-click the class and select "Run as..." and then "Java application"
Eclipse shows the following output in its console window (with "...snip..." indicating lines that were removed to save space):
Example 7.23. Fibonacci Example: Console Output
recurse for 50 recurse for 49 recurse for 48 recurse for 47 ...snip... recurse for 5 recurse for 4 recurse for 3 recurse for 2 1 == 1 2 == 1 3 == 2 4 == 3 5 == 5 6 == 8 ...snip... 47 == 2971215073 48 == 4807526976 49 == 7778742049 50 == 12586269025
To kick this off from Java we only insert a single Fibonacci
    object, with a sequence field of 50. A recursive rule is then used
    to insert the other 49 Fibonacci objects. This example
    doesn't use
    PropertyChangeSupport. It uses the MVEL dialect, which
    means we can use the modify keyword, which allows a block
    setter action which also notifies the engine of changes.
Example 7.24. Fibonacci Example: Execution
ksession.insert( new Fibonacci( 50 ) );
ksession.fireAllRules();
The rule Recurse is very simple. It matches each asserted
    Fibonacci object with a value of -1, creating and 
    asserting a new Fibonacci object with a sequence of
    one less than the currently matched object. Each time a Fibonacci
    object is added while the one with a sequence field equal to 1
    does not exist, the rule re-matches and fires again. The
    not conditional element is used to stop the rule's matching
    once we have all 50 Fibonacci objects in memory. The rule also has a
    salience value, because we need to have all 50 Fibonacci
    objects asserted before we execute the Bootstrap rule.
Example 7.25. Fibonacci Example: Rule "Recurse"
rule Recurse
    salience 10
    when
        f : Fibonacci ( value == -1 )
        not ( Fibonacci ( sequence == 1 ) )
    then
        insert( new Fibonacci( f.sequence - 1 ) );
        System.out.println( "recurse for " + f.sequence );
endThe Audit view shows the original assertion of the
    Fibonacci object with a sequence field of 50, done from
    Java code. From there on, the Audit view shows the continual
    recursion of the rule, where each asserted Fibonacci
    object causes the Recurse rule to become activated and to fire again.
When a Fibonacci object with a sequence field of 2 is
    asserted the "Bootstrap" rule is matched and activated along with the
    "Recurse" rule. Note the multi-restriction on field
    sequence, testing for equality with 1 or 2.
Example 7.26. Fibonacci Example: Rule "Bootstrap"
rule Bootstrap
    when
        f : Fibonacci( sequence == 1 || == 2, value == -1 ) // multi-restriction
    then 
        modify ( f ){ value = 1 };
        System.out.println( f.sequence + " == " + f.value );
endAt this point the Agenda looks as shown below. However, the "Bootstrap" rule does not fire because the "Recurse" rule has a higher salience.
When a Fibonacci object with a sequence of 1 is
    asserted the Bootstrap rule is matched again, causing two activations
    for this rule. Note that the "Recurse" rule does not match and activate
    because the not conditional element stops the rule's matching
    as soon as a Fibonacci object with a sequence of 1
    exists.
Once we have two Fibonacci objects with values
    not equal to -1 the "Calculate" rule is able to match. It was
    the "Bootstrap" rule that set the objects with sequence 1 and 2 to
    values of 1. At this point we have 50 Fibonacci objects in the Working
    Memory. Now we need to select a suitable triple to calculate each
    of their values in turn. Using three Fibonacci patterns in a rule without
    field constraints to confine the possible cross products would result
    in 50x49x48 possible combinations, leading to about 125,000 possible rule
    firings, most of them incorrect. The "Calculate" rule uses field
    constraints to correctly constraint the thee Fibonacci patterns in the
    correct order; this
    technique is called cross product matching. The
    first pattern finds any Fibonacci with a value != -1 and binds both
    the pattern and the field.  The second Fibonacci does this, too, but
    it adds an additional field constraint to ensure that its sequence is
    greater by one than the Fibonacci bound to f1. When this
    rule fires for the first time, we know that only sequences 1 and 2
    have values of 1, and the two constraints ensure that f1
    references sequence 1 and f2 references sequence 2. The
    final pattern finds the Fibonacci with a value equal to -1 and with a
    sequence one greater than f2. At this point, we have
    three Fibonacci objects correctly selected from the
    available cross products, and we can calculate the value for the
    third Fibonacci object that's bound to f3.
Example 7.27. Fibonacci Example: Rule "Calculate"
rule Calculate
    when
        // Bind f1 and s1
        f1 : Fibonacci( s1 : sequence, value != -1 )
        // Bind f2 and v2; refer to bound variable s1
        f2 : Fibonacci( sequence == (s1 + 1), v2 : value != -1 )
        // Bind f3 and s3; alternative reference of f2.sequence
        f3 : Fibonacci( s3 : sequence == (f2.sequence + 1 ), value == -1 )      
    then
        // Note the various referencing techniques.
        modify ( f3 ) { value = f1.value + v2 };
        System.out.println( s3 + " == " + f3.value );
end 
The modify statement updated the value of the
    Fibonacci object bound to f3. This means
    we now have another new Fibonacci object with a value not equal to -1,
    which allows the "Calculate" rule to rematch and calculate the next
    Fibonacci number. The Audit view below shows how the firing of the
    last "Bootstrap" modifies the Fibonacci object,
    enabling the "Calculate" rule to match, which then modifies
    another Fibonacci object allowing the "Calculate" rule to match again.
    This continues till the value is set for all Fibonacci
    objects.
Name: BankingTutorial Main class: org.drools.tutorials.banking.BankingExamplesApp.java Module: drools-examples Type: Java application Rules file: org.drools.tutorials.banking.*.drl Objective: Demonstrate pattern matching, basic sorting and calculation rules.
This tutorial demonstrates the process of developing a complete personal banking application to handle credits and debits on multiple accounts. It uses a set of design patterns that have been created for the process.
The class RuleRunner is a simple harness to execute
    one or more DRL files against a set of data. It compiles the Packages 
    and creates the Knowledge Base for each execution, allowing us to
    easily execute each scenario and inspect the outputs. In reality this
    is not a good solution for a production system, where the Knowledge Base
    should be built just once and cached, but for the purposes of this
    tutorial it shall suffice.
Example 7.28. Banking Tutorial: RuleRunner
public class RuleRunner {
public RuleRunner() {
}
public void runRules(String[] rules,
Object[] facts) throws Exception {
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
for ( int i = 0; i < rules.length; i++ ) {
String ruleFile = rules[i];
System.out.println( "Loading file: " + ruleFile );
kbuilder.add( ResourceFactory.newClassPathResource( ruleFile,
RuleRunner.class ),
ResourceType.DRL );
}
Collection<KnowledgePackage> pkgs = kbuilder.getKnowledgePackages();
kbase.addKnowledgePackages( pkgs );
StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
for ( int i = 0; i < facts.length; i++ ) {
Object fact = facts[i];
System.out.println( "Inserting fact: " + fact );
ksession.insert( fact );
}
ksession.fireAllRules();
}
}
The first of our sample Java classes loads and executes a single
    DRL file, Example.drl, but without inserting any
    data.
Example 7.29. Banking Tutorial : Java Example1
public class Example1 {
public static void main(String[] args) throws Exception {
new RuleRunner().runRules( new String[] { "Example1.drl" },
new Object[0] );
}
}
The first simple rule to execute has a single eval
    condition that will always be true, so that this rule will match and
    fire, once, after the start.
Example 7.30. Banking Tutorial: Rule in Example1.drl
rule "Rule 01"   
    when
        eval( 1==1 )
    then
        System.out.println( "Rule 01 Works" );
endThe output for the rule is below, showing that the rule matches and executes the single print statement.
The next step is to assert some simple facts and print them out.
Example 7.32. Banking Tutorial: Java Example2
public class Example2 {
public static void main(String[] args) throws Exception {
Number[] numbers = new Number[] {wrap(3), wrap(1), wrap(4), wrap(1), wrap(5)};
new RuleRunner().runRules( new String[] { "Example2.drl" },
numbers );
}
private static Integer wrap( int i ) {
return new Integer(i);
}
}
This doesn't use any specific facts but instead asserts a set
    of java.lang.Integer objects. This is not considered
    "best practice" as a number is not a useful fact, but we use it here
    to demonstrate basic techniques before more complexity is added.
Now we will create a simple rule to print out these numbers.
Example 7.33. Banking Tutorial: Rule in Example2.drl
rule "Rule 02"
    when
        Number( $intValue : intValue )
    then
        System.out.println( "Number found with value: " + $intValue ); 
endOnce again, this rule does nothing special. It identifies any
    facts that are Number objects and prints out the values.
    Notice the use of the abstract class Number: we inserted
    Integer objects but we now look for any kind of number.
    The pattern matching engine is able to match interfaces and
    superclasses of asserted objects.
The output shows the DRL being loaded, the facts inserted and then the matched and fired rules. We can see that each inserted number is matched and fired and thus printed.
Example 7.34. Banking Tutorial: Output of Example2.java
Loading file: Example2.drl Inserting fact: 3 Inserting fact: 1 Inserting fact: 4 Inserting fact: 1 Inserting fact: 5 Number found with value: 5 Number found with value: 1 Number found with value: 4 Number found with value: 1 Number found with value: 3
There are certainly many better ways to sort numbers than using rules, but since we will need to apply some cashflows in date order when we start looking at banking rules we'll develop simple rule based sorting technique.
Example 7.35. Banking Tutorial: Example3.java
public class Example3 {
public static void main(String[] args) throws Exception {
Number[] numbers = new Number[] {wrap(3), wrap(1), wrap(4), wrap(1), wrap(5)};
new RuleRunner().runRules( new String[] { "Example3.drl" },
numbers );
}
private static Integer wrap(int i) {
return new Integer(i);
}
}
Again we insert our Integer objects, but this time the
    rule is slightly different:
Example 7.36. Banking Tutorial: Rule in Example3.drl
rule "Rule 03"
    when
        $number : Number( )
        not Number( intValue < $number.intValue )
    then
        System.out.println("Number found with value: " + $number.intValue() ); 
        retract( $number );
endThe first line of the rule identifies a Number and
    extracts the value. The second line ensures that there does not exist
    a smaller number than the one found by the first pattern. We might
    expect to match only one number - the smallest in the set. However,
    the retraction of the number after it has been printed means that the
    smallest number has been removed, revealing the next smallest number,
    and so on. 
The resulting output shows that the numbers are now sorted numerically.
Example 7.37. Banking Tutorial: Output of Example3.java
Loading file: Example3.drl Inserting fact: 3 Inserting fact: 1 Inserting fact: 4 Inserting fact: 1 Inserting fact: 5 Number found with value: 1 Number found with value: 1 Number found with value: 3 Number found with value: 4 Number found with value: 5
We are ready to start moving towards our personal accounting
    rules. The first step is to create a Cashflow object.
Example 7.38. Banking Tutorial: Class Cashflow
public class Cashflow {
private Date date;
private double amount;
public Cashflow() {
}
public Cashflow(Date date, double amount) {
this.date = date;
this.amount = amount;
}
public Date getDate() {
return date;
}
public void setDate(Date date) {
this.date = date;
}
public double getAmount() {
return amount;
}
public void setAmount(double amount) {
this.amount = amount;
}
public String toString() {
return "Cashflow[date=" + date + ",amount=" + amount + "]";
}
}
Class Cashflow has two simple attributes, a date
    and an amount. (Note that using the type double for
    monetary units is generally not a good idea
    because floating point numbers cannot represent most numbers accurately.)
    There is also an overloaded constructor to set the values, and a
    method toString to print a cashflow. The Java code of
    Example4.java inserts five Cashflow objects,
    with varying dates and amounts.
Example 7.39. Banking Tutorial: Example4.java
public class Example4 {
public static void main(String[] args) throws Exception {
Object[] cashflows = {
new Cashflow(new SimpleDate("01/01/2007"), 300.00),
new Cashflow(new SimpleDate("05/01/2007"), 100.00),
new Cashflow(new SimpleDate("11/01/2007"), 500.00),
new Cashflow(new SimpleDate("07/01/2007"), 800.00),
new Cashflow(new SimpleDate("02/01/2007"), 400.00),
};
new RuleRunner().runRules( new String[] { "Example4.drl" },
cashflows );
}
}
The convenience class SimpleDate extends
    java.util.Date, providing a constructor taking
    a String as input and defining a date format. The code is
    listed below
Example 7.40. Banking Tutorial: Class SimpleDate
public class SimpleDate extends Date {
private static final SimpleDateFormat format = new SimpleDateFormat("dd/MM/yyyy");
public SimpleDate(String datestr) throws Exception {
setTime(format.parse(datestr).getTime());
}
}
Now, let’s look at Example4.drl to see how
    we print the sorted Cashflow objects:
Example 7.41. Banking Tutorial: Rule in Example4.drl
rule "Rule 04"   
    when
        $cashflow : Cashflow( $date : date, $amount : amount )
        not Cashflow( date < $date)
    then
        System.out.println("Cashflow: "+$date+" :: "+$amount);  
        retract($cashflow);
endHere, we identify a Cashflow and extract the date
    and the amount. In the second line of the rule we ensure that there
    is no Cashflow with an earlier date than the one found. In the
    consequence, we print the Cashflow that satisfies the
    rule and then retract it, making way for the next earliest
    Cashflow. So, the output we generate is:
Example 7.42. Banking Tutorial: Output of Example4.java
Loading file: Example4.drl Inserting fact: Cashflow[date=Mon Jan 01 00:00:00 GMT 2007,amount=300.0] Inserting fact: Cashflow[date=Fri Jan 05 00:00:00 GMT 2007,amount=100.0] Inserting fact: Cashflow[date=Thu Jan 11 00:00:00 GMT 2007,amount=500.0] Inserting fact: Cashflow[date=Sun Jan 07 00:00:00 GMT 2007,amount=800.0] Inserting fact: Cashflow[date=Tue Jan 02 00:00:00 GMT 2007,amount=400.0] Cashflow: Mon Jan 01 00:00:00 GMT 2007 :: 300.0 Cashflow: Tue Jan 02 00:00:00 GMT 2007 :: 400.0 Cashflow: Fri Jan 05 00:00:00 GMT 2007 :: 100.0 Cashflow: Sun Jan 07 00:00:00 GMT 2007 :: 800.0 Cashflow: Thu Jan 11 00:00:00 GMT 2007 :: 500.0
Next, we extend our Cashflow, resulting in a
    TypedCashflow which can be a credit or a debit operation.
    (Normally, we would just add this to the Cashflow type, but
    we use extension to keep the previous version of the class intact.)
Example 7.43. Banking Tutorial: Class TypedCashflow
public class TypedCashflow extends Cashflow {
public static final int CREDIT = 0;
public static final int DEBIT = 1;
private int type;
public TypedCashflow() {
}
public TypedCashflow(Date date, int type, double amount) {
super( date, amount );
this.type = type;
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String toString() {
return "TypedCashflow[date=" + getDate() +
",type=" + (type == CREDIT ? "Credit" : "Debit") +
",amount=" + getAmount() + "]";
}
}
There are lots of ways to improve this code, but for the sake of the example this will do.
Now let's create Example5, a class for running our code.
Example 7.44. Banking Tutorial: Example5.java
public class Example5 {
public static void main(String[] args) throws Exception {
Object[] cashflows = {
new TypedCashflow(new SimpleDate("01/01/2007"),
TypedCashflow.CREDIT, 300.00),
new TypedCashflow(new SimpleDate("05/01/2007"),
TypedCashflow.CREDIT, 100.00),
new TypedCashflow(new SimpleDate("11/01/2007"),
TypedCashflow.CREDIT, 500.00),
new TypedCashflow(new SimpleDate("07/01/2007"),
TypedCashflow.DEBIT, 800.00),
new TypedCashflow(new SimpleDate("02/01/2007"),
TypedCashflow.DEBIT, 400.00),
};
new RuleRunner().runRules( new String[] { "Example5.drl" },
cashflows );
}
}
Here, we simply create a set of Cashflow objects
    which are either credit or debit operations. We supply them and
    Example5.drl to the RuleEngine. 
Now, let’s look at a rule printing the sorted
    Cashflow objects.
Example 7.45. Banking Tutorial: Rule in Example5.drl
rule "Rule 05"  
    when
        $cashflow : TypedCashflow( $date : date,
                                   $amount : amount,
                                   type == TypedCashflow.CREDIT )
        not TypedCashflow( date < $date,
                           type == TypedCashflow.CREDIT )
    then
        System.out.println("Credit: "+$date+" :: "+$amount);   
        retract($cashflow);
endHere, we identify a Cashflow fact with a type
    of CREDIT and extract the date and the amount. In the
    second line of the rule we ensure that there is no Cashflow
    of the same type with an earlier date than the one found. In the
    consequence, we print the cashflow satisfying the patterns and then
    retract it, making way for the next earliest cashflow of type
    CREDIT.
So, the output we generate is
Example 7.46. Banking Tutorial: Output of Example5.java
Loading file: Example5.drl Inserting fact: TypedCashflow[date=Mon Jan 01 00:00:00 GMT 2007,type=Credit,amount=300.0] Inserting fact: TypedCashflow[date=Fri Jan 05 00:00:00 GMT 2007,type=Credit,amount=100.0] Inserting fact: TypedCashflow[date=Thu Jan 11 00:00:00 GMT 2007,type=Credit,amount=500.0] Inserting fact: TypedCashflow[date=Sun Jan 07 00:00:00 GMT 2007,type=Debit,amount=800.0] Inserting fact: TypedCashflow[date=Tue Jan 02 00:00:00 GMT 2007,type=Debit,amount=400.0] Credit: Mon Jan 01 00:00:00 GMT 2007 :: 300.0 Credit: Fri Jan 05 00:00:00 GMT 2007 :: 100.0 Credit: Thu Jan 11 00:00:00 GMT 2007 :: 500.0
Continuing our banking exercise, we are now going to process both
    credits and debits on two bank accounts, calculating the account balance.
    In order to do this, we create two separate Account objects
    and inject them into the Cashflows objects before passing
    them to the Rule Engine. The reason for this is to provide easy access
    to the correct account without having to resort to helper classes. Let’s
    take a look at the Account class first. This is a simple
    Java object with an account number and balance:
Example 7.47. Banking Tutorial: Class Account
public class Account {
private long accountNo;
private double balance = 0;
public Account() {
}
public Account(long accountNo) {
this.accountNo = accountNo;
}
public long getAccountNo() {
return accountNo;
}
public void setAccountNo(long accountNo) {
this.accountNo = accountNo;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
public String toString() {
return "Account[" + "accountNo=" + accountNo + ",balance=" + balance + "]";
}
}
Now let’s extend our TypedCashflow, resulting in
    AllocatedCashflow, to include an Account
    reference.
Example 7.48. Banking Tutorial: Class AllocatedCashflow
public class AllocatedCashflow extends TypedCashflow {
private Account account;
public AllocatedCashflow() {
}
public AllocatedCashflow(Account account, Date date, int type, double amount) {
super( date, type, amount );
this.account = account;
}
public Account getAccount() {
return account;
}
public void setAccount(Account account) {
this.account = account;
}
public String toString() {
return "AllocatedCashflow[" +
"account=" + account +
",date=" + getDate() +
",type=" + (getType() == CREDIT ? "Credit" : "Debit") +
",amount=" + getAmount() + "]";
}
}
The Java code of Example5.java creates 
    two Account objects and passes one of them into each
    cashflow, in the constructor call.
Example 7.49. Banking Tutorial: Example5.java
public class Example6 {
public static void main(String[] args) throws Exception {
Account acc1 = new Account(1);
Account acc2 = new Account(2);
Object[] cashflows = {
new AllocatedCashflow(acc1,new SimpleDate("01/01/2007"),
TypedCashflow.CREDIT, 300.00),
new AllocatedCashflow(acc1,new SimpleDate("05/02/2007"),
TypedCashflow.CREDIT, 100.00),
new AllocatedCashflow(acc2,new SimpleDate("11/03/2007"),
TypedCashflow.CREDIT, 500.00),
new AllocatedCashflow(acc1,new SimpleDate("07/02/2007"),
TypedCashflow.DEBIT, 800.00),
new AllocatedCashflow(acc2,new SimpleDate("02/03/2007"),
TypedCashflow.DEBIT, 400.00),
new AllocatedCashflow(acc1,new SimpleDate("01/04/2007"),
TypedCashflow.CREDIT, 200.00),
new AllocatedCashflow(acc1,new SimpleDate("05/04/2007"),
TypedCashflow.CREDIT, 300.00),
new AllocatedCashflow(acc2,new SimpleDate("11/05/2007"),
TypedCashflow.CREDIT, 700.00),
new AllocatedCashflow(acc1,new SimpleDate("07/05/2007"),
TypedCashflow.DEBIT, 900.00),
new AllocatedCashflow(acc2,new SimpleDate("02/05/2007"),
TypedCashflow.DEBIT, 100.00)
};
new RuleRunner().runRules( new String[] { "Example6.drl" },
cashflows );
}
}
Now, let’s look at the rule in Example6.drl
    to see how we apply each cashflow in date order and calculate and print
    the balance. 
Example 7.50. Banking Tutorial: Rule in Example6.drl
rule "Rule 06 - Credit"  
    when
        $cashflow : AllocatedCashflow( $account : account,
                                       $date : date,
                                       $amount : amount,
                                       type == TypedCashflow.CREDIT )
        not AllocatedCashflow( account == $account, date < $date)
    then
        System.out.println("Credit: " + $date + " :: " + $amount);     
        $account.setBalance($account.getBalance()+$amount);
        System.out.println("Account: " + $account.getAccountNo() +
                           " - new balance: " + $account.getBalance());          
        retract($cashflow);
end
rule "Rule 06 - Debit"  
    when
        $cashflow : AllocatedCashflow( $account : account,
                            $date : date,
                            $amount : amount,
                            type == TypedCashflow.DEBIT )
        not AllocatedCashflow( account == $account, date < $date)
    then
        System.out.println("Debit: " + $date + " :: " + $amount);      
        $account.setBalance($account.getBalance() - $amount);
        System.out.println("Account: " + $account.getAccountNo() +
                           " - new balance: " + $account.getBalance());           
        retract($cashflow);
endAlthough we have separate rules for credits and debits, but we do not specify a type when checking for earlier cashflows. This is so that all cashflows are applied in date order, regardless of the cashflow type. In the conditions we identify the account to work with, and in the consequences we update it with the cashflow amount.
Example 7.51. Banking Tutorial: Output of Example6.java
Loading file: Example6.drl Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Mon Jan 01 00:00:00 GMT 2007,type=Credit,amount=300.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Mon Feb 05 00:00:00 GMT 2007,type=Credit,amount=100.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=2,balance=0.0],date=Sun Mar 11 00:00:00 GMT 2007,type=Credit,amount=500.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Wed Feb 07 00:00:00 GMT 2007,type=Debit,amount=800.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=2,balance=0.0],date=Fri Mar 02 00:00:00 GMT 2007,type=Debit,amount=400.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Sun Apr 01 00:00:00 BST 2007,type=Credit,amount=200.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Thu Apr 05 00:00:00 BST 2007,type=Credit,amount=300.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=2,balance=0.0],date=Fri May 11 00:00:00 BST 2007,type=Credit,amount=700.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=1,balance=0.0],date=Mon May 07 00:00:00 BST 2007,type=Debit,amount=900.0] Inserting fact: AllocatedCashflow[account=Account[accountNo=2,balance=0.0],date=Wed May 02 00:00:00 BST 2007,type=Debit,amount=100.0] Debit: Fri Mar 02 00:00:00 GMT 2007 :: 400.0 Account: 2 - new balance: -400.0 Credit: Sun Mar 11 00:00:00 GMT 2007 :: 500.0 Account: 2 - new balance: 100.0 Debit: Wed May 02 00:00:00 BST 2007 :: 100.0 Account: 2 - new balance: 0.0 Credit: Fri May 11 00:00:00 BST 2007 :: 700.0 Account: 2 - new balance: 700.0 Credit: Mon Jan 01 00:00:00 GMT 2007 :: 300.0 Account: 1 - new balance: 300.0 Credit: Mon Feb 05 00:00:00 GMT 2007 :: 100.0 Account: 1 - new balance: 400.0 Debit: Wed Feb 07 00:00:00 GMT 2007 :: 800.0 Account: 1 - new balance: -400.0 Credit: Sun Apr 01 00:00:00 BST 2007 :: 200.0 Account: 1 - new balance: -200.0 Credit: Thu Apr 05 00:00:00 BST 2007 :: 300.0 Account: 1 - new balance: 100.0 Debit: Mon May 07 00:00:00 BST 2007 :: 900.0 Account: 1 - new balance: -800.0
The Pricing Rule decision table demonstrates the use of a decision table in a spreadsheet, in Excel's XLS format, in calculating the retail cost of an insurance policy. The purpose of the provide set of rules is to calculate a base price and a discount for a car driver applying for a specific policy. The driver's age, history and the policy type all contribute to what the basic premium is, and an additional chunk of rules deals with refining this with a discount percentage.
Name: Example Policy Pricing Main class: org.drools.examples.decisiontable.PricingRuleDTExample Module: drools-examples Type: Java application Rules file: ExamplePolicyPricing.xls Objective: demonstrate spreadsheet-based decision tables.
Open the file PricingRuleDTExample.java and 
      execute it as a Java application. It should produce the following
      output in the Console window:
Cheapest possible BASE PRICE IS: 120 DISCOUNT IS: 20
The code to execute the example follows the usual pattern. The rules are loaded, the facts inserted and a Stateless Session is created. What is different is how the rules are added.
DecisionTableConfiguration dtableconfiguration =
KnowledgeBuilderFactory.newDecisionTableConfiguration();
dtableconfiguration.setInputType( DecisionTableInputType.XLS );
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
Resource xlsRes = ResourceFactory.newClassPathResource( "ExamplePolicyPricing.xls",
getClass() );
kbuilder.add( xlsRes,
ResourceType.DTABLE,
dtableconfiguration );
Note the use of the DecisionTableConfiguration object.
      Its input type is set to DecisionTableInputType.XLS.
      If you use the BRMS, all this is of course taken care of for you.
There are two fact types used in this example, Driver
      and Policy. Both are used with their default values. The
      Driver is 30 years old, has had no prior claims and
      currently has a risk profile of LOW. The Policy
      being applied for is COMPREHENSIVE, and it has not yet been
      approved.
In this decision table, each row is a rule, and each column is a condition or an action.
Referring to the spreadsheet show above, we have the
      RuleSet declaration, which provides the package name.
      There are also other optional items you can have here, such as
      Variables for global variables, and Imports
      for importing classes. In this case, the namespace of the rules is
      the same as the fact classes we are using, so we can omit it.
Moving further down, we can see the RuleTable
      declaration. The name after this (Pricing bracket) is used as the
      prefix for all the generated rules. Below that, we have
      "CONDITION or ACTION", indicating the purpose of the column, i.e.,
      whether it forms part of the condition or the consequence of the rule
      that will be generated.
You can see that there is a driver, his data spanned across three
      cells, which means that the template expressions below it apply to that
      fact. We observe the driver's age range (which uses $1 and
      $2 with comma-separated values), 
      locationRiskProfile, and priorClaims in the
      respective columns. In the action columns, we are set the policy
      base price and log a message.
In the preceding spreadsheet section, there are broad category brackets, indicated by the comment in the leftmost column. As we know the details of our drivers and their policies, we can tell (with a bit of thought) that they should match row number 18, as they have no prior accidents, and are 30 years old. This gives us a base price of 120.
The above section contains the conditions for the discount we
      might grant our driver. The discount results from the Age
      bracket, the number of prior claims, and the policy type. In our case,
      the driver is 30, with no prior claims, and is applying for a
      COMPREHENSIVE policy, which means we can give a discount
      of 20%. Note that this is actually a separate table, but in the same
      worksheet, so that different templates apply.
It is important to note that decision tables generate rules. This means they aren't simply top-down logic, but more a means to capture data resulting in rules. This is a subtle difference that confuses some people. The evaluation of the rules is not necessarily in the given order, since all the normal mechanics of the rule engine still apply.
Name: Pet Store Main class: org.drools.examples.petstore.PetStoreExample Module: drools-examples Type: Java application Rules file: PetStore.drl Objective: Demonstrate use of Agenda Groups, Global Variables and integration with a GUI, including callbacks from within the rules
The Pet Store example shows how to integrate Rules with a GUI, in this case a Swing based desktop application. Within the rules file, it demonstrates how to use Agenda groups and auto-focus to control which of a set of rules is allowed to fire at any given time. It also illustrates the mixing of the Java and MVEL dialects within the rules, the use of accumulate functions and the way of calling Java functions from within the ruleset.
All of the Java code is contained in one file,
      PetStore.java, defining the following principal
      classes (in addition to several classes to handle Swing Events):
Petstore contains the main()
          method that we will look at shortly.
PetStoreUI is responsible for creating and
          displaying the Swing based GUI. It contains several smaller
          classes, mainly for responding to various GUI events such as
          mouse button clicks.
TableModel holds the table data. Think of it
          as a JavaBean that extends the Swing class
          AbstractTableModel.
CheckoutCallback allows the GUI to interact
          with the Rules.
Ordershow keeps the items that we wish to
          buy.
Purchase stores details of the order and
          the products we are buying.
Product is a JavaBean holding details of
          the product available for purchase, and its price.
Much of the Java code is either plain JavaBeans or Swing-based. Only a few Swing-related points will be discussed in this section, but a good tutorial about Swing components can be found at Sun's Swing website, in http://java.sun.com/docs/books/tutorial/uiswing/.
The pieces of Java code in Petstore.java
      that relate to rules and facts are shown below.
Example 7.52. Creating the PetStore RuleBase in PetStore.main
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add( ResourceFactory.newClassPathResource( "PetStore.drl",
PetStore.class ),
ResourceType.DRL );
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );
// Create the stock.
Vector<Product> stock = new Vector<Product>();
stock.add( new Product( "Gold Fish", 5 ) );
stock.add( new Product( "Fish Tank", 25 ) );
stock.add( new Product( "Fish Food", 2 ) );
// A callback is responsible for populating the
// Working Memory and for firing all rules.
PetStoreUI ui = new PetStoreUI( stock,
new CheckoutCallback( kbase ) );
ui.createAndShowGUI();
The code shown above loads the rules from a DRL file on the
      classpath. Unlike other examples where the facts are asserted and
      fired straight away, this example defers this step to later. The
      way it does this is via the second last line where a
      PetStoreUI object is created using a constructor 
      accepting the Vector object stock
      collecting our products, and an instance of
      the CheckoutCallback class containing the Rule Base
      that we have just loaded.
The Java code that fires the rules is within the 
      CheckoutCallBack.checkout() method. This is triggered
      (eventually) when the Checkout button is pressed by the user.
Example 7.53. Firing the Rules - extract from CheckoutCallBack.checkout()
public String checkout(JFrame frame, List<Product> items) {
Order order = new Order();
// Iterate through list and add to cart
for ( Product p: items ) {
order.addItem( new Purchase( order, p ) );
}
// Add the JFrame to the ApplicationData to allow for user interaction
StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
ksession.setGlobal( "frame", frame );
ksession.setGlobal( "textArea", this.output );
ksession.insert( new Product( "Gold Fish", 5 ) );
ksession.insert( new Product( "Fish Tank", 25 ) );
ksession.insert( new Product( "Fish Food", 2 ) );
ksession.insert( new Product( "Fish Food Sample", 0 ) );
ksession.insert( order );
ksession.fireAllRules();
// Return the state of the cart
return order.toString();
}
Two items get passed into this method. One is the handle to the
      JFrame Swing component surrounding the output text
      frame, at the bottom of the GUI. The second is a list of order items;
      this comes from the TableModel storing the information
      from the "Table" area at the top right section of the GUI.
The for loop transforms the list of order items coming from the
      GUI into the Order JavaBean, also contained in the
      file PetStore.java. Note that it would be 
      possible to refer to the Swing dataset directly within the rules,
      but it is better coding practice to do it this way, using simple
      Java objects. It means that we are not tied to Swing if we wanted
      to transform the sample into a Web application.
It is important to note that all state in this
      example is stored in the Swing components, and that the rules are
      effectively stateless. Each time the "Checkout" button is
      pressed, this code copies the contents of the Swing
      TableModel into the Session's Working Memory.
Within this code, there are nine calls to the Working Memory.
      The first of these creates a new Working Memory, as a Stateful
      Knowledge Session from the Knowledge Base. Remember that we passed
      in this Knowledge Base when we created the CheckoutCallBack
      class in the main() method. The next two calls pass in
      two objects that we will hold as global variables in the rules: the
      Swing text area and the Swing frame used for writing messages.
More inserts put information on products into the Working Memory,
      as well as the order list. The final call is the standard
      fireAllRules(). Next, we look at what this method causes
      to happen within the rules file.
Example 7.54. Package, Imports, Globals and Dialect: extract from PetStore.drl
package org.drools.examples
import org.drools.core.WorkingMemory
import org.drools.examples.petstore.PetStoreExample.Order
import org.drools.examples.petstore.PetStoreExample.Purchase
import org.drools.examples.petstore.PetStoreExample.Product
import java.util.ArrayList
import javax.swing.JOptionPane;
import javax.swing.JFrame
global JFrame frame
global javax.swing.JTextArea textArea
The first part of file PetStore.drl
      contains the standard package and import statements to make various
      Java classes available to the rules. New to us are the two globals
      frame and textArea. They hold references
      to the Swing components JFrame and JTextArea
      components that were previously passed on by the Java code calling
      the setGlobal() method. Unlike  variables in rules,
      which expire as soon as the rule has fired, global variables retain
      their value for the lifetime of the Session.
The next extract from the file PetStore.drl
      contains two functions that are referenced by the rules that we will
      look at shortly.
Example 7.55. Java Functions in the Rules: extract from PetStore.drl
function void doCheckout(JFrame frame, WorkingMemory workingMemory) {
Object[] options = {"Yes",
"No"};
int n = JOptionPane.showOptionDialog(frame,
"Would you like to checkout?",
"",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[0]);
if (n == 0) {
workingMemory.setFocus( "checkout" );
}
}
function boolean requireTank(JFrame frame, WorkingMemory workingMemory, Order order, Product fishTank, int total) {
Object[] options = {"Yes",
"No"};
int n = JOptionPane.showOptionDialog(frame,
"Would you like to buy a tank for your " + total + " fish?",
"Purchase Suggestion",
JOptionPane.YES_NO_OPTION,
JOptionPane.QUESTION_MESSAGE,
null,
options,
options[0]);
System.out.print( "SUGGESTION: Would you like to buy a tank for your "
+ total + " fish? - " );
if (n == 0) {
Purchase purchase = new Purchase( order, fishTank );
workingMemory.insert( purchase );
order.addItem( purchase );
System.out.println( "Yes" );
} else {
System.out.println( "No" );
}
return true;
}
Having these functions in the rules file just makes the Pet Store
      example more compact. In real life you probably have the functions
      in a file of their own, within the same rules package, or as a
      static method on a standard Java class, and import them, using
      import function my.package.Foo.hello.
The purpose of these two functions is:
doCheckout() displays a dialog asking users
          whether they wish to checkout. If they do, focus is set to the
          checkOut agenda-group, allowing rules in that group
          to (potentially) fire.
requireTank() displays a dialog asking
          users whether they wish to buy a tank. If so, a new fish tank
          Product is added to the order list in Working
          Memory.
We'll see the rules that call these functions later on. The
      next set of examples are from the Pet Store rules themselves. The
      first extract is the one that happens to fire first, partly because
      it has the auto-focus attribute set to true.
Example 7.56. Putting items into working memory: extract from PetStore.drl
// Insert each item in the shopping cart into the Working Memory 
// Insert each item in the shopping cart into the Working Memory
rule "Explode Cart"
    agenda-group "init"
    auto-focus true
    salience 10
    dialect "java"
when
    $order : Order( grossTotal == -1 )
    $item : Purchase() from $order.items
then
    insert( $item );
    kcontext.getKnowledgeRuntime().getAgenda().getAgendaGroup( "show items" ).setFocus();
    kcontext.getKnowledgeRuntime().getAgenda().getAgendaGroup( "evaluate" ).setFocus();
endThis rule matches against all orders that do not yet have their
      grossTotal calculated . It loops for each purchase item
      in that order. Some parts of the "Explode Cart" rule should be familiar:
      the rule name, the salience (suggesting the order for the rules being
      fired) and the dialect set to "java". There are three
      new features:
agenda-group "init" defines the name
           of the agenda group. In this case, there is only one rule in the
           group. However, neither the Java code nor a rule consequence sets
           the focus to this group, and therefore it relies on the next
           attribute for its chance to fire.
auto-focus true ensures that this rule,
          while being the only rule in the agenda group, gets a chance to fire
          when fireAllRules() is called from the Java code.
kcontext....setFocus() sets the focus to the
          "show items" and "evaluate" agenda groups
          in turn, permitting their rules to fire. In practice, we loop
          through all items on the order, inserting them into memory, then
          firing the other rules after each insert.
The next two listings show the rules within the
      "show items" and evaluate agenda groups.
      We look at them in the order that they are called.
Example 7.57. Show Items in the GUI - extract from PetStore.drl
rule "Show Items"
    agenda-group "show items"
    dialect "mvel"
when
    $order : Order( )
    $p : Purchase( order == $order )
then
   textArea.append( $p.product + "\n");
end
The "show items" agenda-group has only one rule,
      called "Show Items" (note the difference in case). For each purchase
      on the order currently in the Working Memory (or Session), it logs
      details to the text area at the bottom of the GUI. The
      textArea variable used to do this is one of the global
      variables we looked at earlier.
The evaluate Agenda group also gains focus from
      the "Explode Cart" rule listed previously. This
      Agenda group has two rules, "Free Fish Food Sample" and
      "Suggest Tank", shown below.
Example 7.58. Evaluate Agenda Group: extract from PetStore.drl
// Free Fish Food sample when we buy a Gold Fish if we haven't already bought 
// Fish Food and don't already have a Fish Food Sample
rule "Free Fish Food Sample"
    agenda-group "evaluate"
    dialect "mvel"
when
    $order : Order()
    not ( $p : Product( name == "Fish Food") && Purchase( product == $p ) )
    not ( $p : Product( name == "Fish Food Sample") && Purchase( product == $p ) )
    exists ( $p : Product( name == "Gold Fish") && Purchase( product == $p ) )
    $fishFoodSample : Product( name == "Fish Food Sample" );
then
    System.out.println( "Adding free Fish Food Sample to cart" );
    purchase = new Purchase($order, $fishFoodSample);
    insert( purchase );
    $order.addItem( purchase ); 
end
// Suggest a tank if we have bought more than 5 gold fish and don't already have one
rule "Suggest Tank"
    agenda-group "evaluate"
    dialect "java"
when
    $order : Order()
    not ( $p : Product( name == "Fish Tank") && Purchase( product == $p ) )
    ArrayList( $total : size > 5 ) from collect( Purchase( product.name == "Gold Fish" ) )
    $fishTank : Product( name == "Fish Tank" )
then
    requireTank(frame, drools.getWorkingMemory(), $order, $fishTank, $total); 
end
The rule "Free Fish Food Sample" will only fire if
we don't already have any fish food, and
we don't already have a free fish food sample, and
we do have a Gold Fish in our order.
If the rule does fire, it creates a new product (Fish Food Sample), and adds it to the order in Working Memory.
The rule "Suggest Tank" will only fire if
we don't already have a Fish Tank in our order, and
we do have more than 5 Gold Fish Products in our order.
If the rule does fire, it calls the requireTank() function
      that we looked at earlier (showing a Dialog to the user, and adding a Tank to
      the order / working memory if confirmed). When calling the
      requireTank() function the rule passes
      the global frame variable so that the
      function has a handle to the Swing GUI.
The next rule we look at is "do checkout".
Example 7.59. Doing the Checkout - extract (6) from PetStore.drl
rule "do checkout"
    dialect "java"
    when
    then
        doCheckout(frame, drools.getWorkingMemory());
endThe rule "do checkout" has no
      agenda group set and no auto-focus attribute. As such, is is
      deemed part of the default (MAIN) agenda group. This group gets focus by
      default when all the rules in agenda-groups that explicitly had focus set
      to them have run their course.
There is no LHS to the rule, so the RHS will always call the
      doCheckout() function. When calling the
      doCheckout() function, the rule passes the global
      frame variable to give the function a handle to the Swing GUI.
      As we saw earlier, the doCheckout() function shows a
      confirmation dialog to the user. If confirmed, the function sets the focus
      to the checkout agenda-group, allowing
      the next lot of rules to fire.
Example 7.60. Checkout Rules: extract from PetStore.drl
rule "Gross Total"
    agenda-group "checkout"
    dialect "mvel"
when
   $order : Order( grossTotal == -1)
   Number( total : doubleValue )
       from accumulate( Purchase( $price : product.price ), sum( $price ) )
then
    modify( $order ) { grossTotal = total };
    textArea.append( "\ngross total=" + total + "\n" );
end
rule "Apply 5% Discount"
    agenda-group "checkout"
dialect "mvel"
when
   $order : Order( grossTotal >= 10 && < 20 )
then
   $order.discountedTotal = $order.grossTotal * 0.95;
   textArea.append( "discountedTotal total=" + $order.discountedTotal + "\n" );
end
rule "Apply 10% Discount"
    agenda-group "checkout"
    dialect "mvel"
when
   $order : Order( grossTotal >= 20 )
then
   $order.discountedTotal = $order.grossTotal * 0.90;
   textArea.append( "discountedTotal total=" + $order.discountedTotal + "\n" );
end
There are three rules in the checkout agenda-group:
If we haven't already calculated the gross total,
          Gross Total accumulates the product prices into a total,
          puts this total into Working Memory, and displays it via the Swing
          JTextArea, using the textArea global
          variable yet again.
If our gross total is between 10 and 20, 
          "Apply 5% Discount" calculates the discounted total and
          adds it to the Working Memory and displays it in the text area.
If our gross total is not less than 20, 
          "Apply 10% Discount" calculates the discounted total and
          adds it to the Working Memory and displays it in the text area.
Now that we've run through what happens in the code, let's have a
      look at what happens when we actually run the code. The file
      PetStore.java contains a main() method,
      so that it can be run as a standard Java application, either from the
      command line or via the IDE. This assumes you have your classpath set
      correctly. (See the start of the examples section for more information.)
The first screen that we see is the Pet Store Demo. It has a list of available products (top left), an empty list of selected products (top right), checkout and reset buttons (middle) and an empty system messages area (bottom).
To get to this point, the following things have happened:
The main() method has run and loaded the Rule Base
          but not yet fired the rules. So far, this is the
          only code in connection with rules that has been run.
A new PetStoreUI object has been created and given a
          handle to the Rule Base, for later use.
Various Swing components do their stuff, and the above screen is shown and waits for user input.
Clicking on various products from the list might give you a screen similar to the one below.
Note that no rules code has been fired here. This
      is only Swing code, listening for mouse click events, and adding some
      selected product to the TableModel object for display in the
      top right hand section. (As an aside, note that this is a classic use of
      the Model View Controller design pattern).
It is only when we press the "Checkout" button that we fire our business rules, in roughly the same order that we walked through the code earlier.
Method CheckOutCallBack.checkout() is called
          (eventually) by the Swing class waiting for the click on the
          "Checkout" button. This inserts the data from the
          TableModel object (top right hand side of the GUI),
          and inserts it into the Session's Working Memory. It then fires
          the rules.
The "Explode Cart" rule is the first to fire,
          given that it has auto-focus set to true. It loops through
          all the products in the cart, ensures that the products are in the
          Working Memory, and then gives the "Show Items" and
          Evaluation agenda groups a chance to fire. The rules
          in these groups add the contents of the cart to the text area
          (at the bottom of the window), decide whether or not to give us free
          fish food, and to ask us whether we want to buy a fish tank. This
          is shown in the figure below.
The Do Checkout rule is the next to fire as it (a) No other agenda group currently has focus and (b) it is part of the default (MAIN) agenda group. It always calls the doCheckout() function which displays a 'Would you like to Checkout?' Dialog Box.
The doCheckout() function sets the focus to the
          checkout agenda-group, giving the rules in that group
          the option to fire.
The rules in the the checkout agenda-group display
          the contents of the cart and apply the appropriate discount.
Swing then waits for user input to either checkout more products (and to cause the rules to fire again), or to close the GUI - see the figure below.
We could add more System.out calls to demonstrate this flow of events. The output, as it currently appears in the Console window, is given in the listing below.
Example 7.61. Console (System.out) from running the PetStore GUI
Adding free Fish Food Sample to cart SUGGESTION: Would you like to buy a tank for your 6 fish? - Yes
Name: Honest Politician Main class: org.drools.examples.honestpolitician.HonestPoliticianExample Module: drools-examples Type: Java application Rules file: HonestPoliticianExample.drl Objective: Illustrate the concept of "truth maintenance" based on the logical insertion of facts
The Honest Politician example demonstrates truth maintenance with
  logical assertions. The basic premise is that an object can only exist
  while a statement is true. A rule's consequence can logically insert an 
  object with the insertLogical() method. This means the object
  will only remain in the Working Memory as long as the rule that logically
  inserted  it remains true. When the rule is no longer true the object is
  automatically retracted.
In this example there is the class Politician, with a
  name and a boolean value for being honest. Four politicians with honest
  state set to true are inserted.
Example 7.62. Class Politician
public class Politician {
private String name;
private boolean honest;
...
}
Example 7.63. Honest Politician: Execution
Politician blair = new Politician("blair", true);
Politician bush = new Politician("bush", true);
Politician chirac = new Politician("chirac", true);
Politician schroder = new Politician("schroder", true);
ksession.insert( blair );
ksession.insert( bush );
ksession.insert( chirac );
ksession.insert( schroder );
ksession.fireAllRules();
The Console window output shows that, while there is at least one honest politician, democracy lives. However, as each politician is in turn corrupted by an evil corporation, so that all politicians become dishonest, democracy is dead.
Example 7.64. Honest Politician: Console Output
Hurrah!!! Democracy Lives I'm an evil corporation and I have corrupted schroder I'm an evil corporation and I have corrupted chirac I'm an evil corporation and I have corrupted bush I'm an evil corporation and I have corrupted blair We are all Doomed!!! Democracy is Dead
As soon as there is at least one honest politician in the
    Working Memory a new Hope object is logically asserted.
    This object will only exist while there is at least one honest
    politician. As soon as all politicians are dishonest, the
    Hope object will be automatically retracted. This rule
    is given a salience of 10 to ensure that it fires before any other
    rule, as at this stage the "Hope is Dead" rule is actually true.
Example 7.65. Honest Politician: Rule "We have an honest politician"
rule "We have an honest Politician"
    salience 10
    when
        exists( Politician( honest == true ) )
    then
        insertLogical( new Hope() );
endAs soon as a Hope object exists the "Hope Lives" rule
  matches and fires. It has a salience of 10 so that it takes priority
  over "Corrupt the Honest".
Example 7.66. Honest Politician: Rule "Hope Lives"
rule "Hope Lives"
    salience 10
        when
            exists( Hope() )
        then
            System.out.println("Hurrah!!! Democracy Lives");
endNow that there is hope and we have, at the start, four honest
    politicians, we have four activations for this rule, all in conflict.
    They will fire in turn, corrupting each politician so that they are
    no longer honest. When all four politicians have been corrupted we
    have no politicians with the property honest == true.
    Thus, the rule "We have an honest Politician" is no longer true and
    the object it logical inserted (due to the last execution of
    new Hope()) is automatically retracted.
Example 7.67. Honest Politician: Rule "Corrupt the Honest"
rule "Corrupt the Honest"
    when
        politician : Politician( honest == true )   
        exists( Hope() )
    then
        System.out.println( "I'm an evil corporation and I have corrupted " + politician.getName() );
        modify ( politician ) { honest = false };
endWith the Hope object being automatically retracted,
    via the truth maintenance system, the conditional element not
    applied to Hope is no longer true so that the following
    rule will match and fire.
Example 7.68. Honest Politician: Rule "Hope is Dead"
rule "Hope is Dead"
    when
        not( Hope() )
    then
        System.out.println( "We are all Doomed!!! Democracy is Dead" );
endLet's take a look at the Audit trail for this application:
The moment we insert the first politician we have two activations.
    The rule "We have an honest Politician" is activated only once for the first
    inserted politician because it uses an exists conditional
    element, which matches once for any number. The rule "Hope is Dead" is
    also activated at this stage, because we have not yet inserted the
    Hope object. Rule "We have an honest Politician" fires first,
    as it has a higher salience than "Hope is Dead", which inserts the
    Hope object. (That action is highlighted green.) The
    insertion of the Hope object activates "Hope Lives" and
    de-activates "Hope is Dead"; it also activates "Corrupt the Honest"
    for each inserted honest politician. Rule "Hope Lives" executes,
    printing  "Hurrah!!! Democracy Lives". Then, for each politician, rule
    "Corrupt the Honest" fires, printing "I'm an evil corporation and I
    have corrupted X", where X is the name of the politician, and modifies
    the politician's honest value to false. When the last honest politician
    is corrupted, Hope is automatically retracted, by the truth
    maintenance system, as shown by the blue highlighted area. The green
    highlighted area shows the origin of the currently selected blue
    highlighted area. Once the Hope fact is retracted, "Hope is
    dead" activates and fires printing "We are all Doomed!!! Democracy is
    Dead".
Name: Sudoku Main class: org.drools.examples.sudoku.SudokuExample Type: Java application Rules file: sudoku.drl, validate.drl Objective: Demonstrates the solving of logic problems, and complex pattern matching.
This example demonstrates how Drools can be used to find a solution in a large potential solution space based on a number of constraints. We use the popular puzzle of Sudoku. This example also shows how Drools can be integrated into a graphical interface and how callbacks can be used to interact with a running Drools rules engine in order to update the graphical interface based on changes in the Working Memory at runtime.
Sudoku is a logic-based number placement puzzle. The objective is to fill a 9x9 grid so that each column, each row, and each of the nine 3x3 zones contains the digits from 1 to 9, once, and only once.
The puzzle setter provides a partially completed grid and the puzzle solver's task is to complete the grid with these constraints.
The general strategy to solve the problem is to ensure that when you insert a new number it should be unique in its particular 3x3 zone, row and column.
See Wikipedia for a more detailed description.
Download and install drools-examples as described above and then
      execute java org.drools.examples.DroolsExamplesApp and
      click on "SudokuExample".
The window contains an empty grid, but the program comes with a number of grids stored internally which can be loaded and solved. Click on "File", then "Samples" and select "Simple" to load one of the examples. Note that all buttons are disabled until a grid is loaded.

Loading the "Simple" example fills the grid according to the puzzle's initial state.

Click on the "Solve" button and the Drools-based engine will fill out the remaining values, and the buttons are inactive once more.

Alternatively, you may click on the "Step" button to see the next digit found by the rule set. The Console window will display detailed information about the rules which are executing to solve the step in a human readable form. Some examples of these messages are presented below.
single 8 at [0,1] column elimination due to [1,2]: remove 9 from [4,2] hidden single 9 at [1,2] row elimination due to [2,8]: remove 7 from [2,4] remove 6 from [3,8] due to naked pair at [3,2] and [3,7] hidden pair in row at [4,6] and [4,4]
Click on the "Dump" button to see the state of the grid, with cells showing either the established value or the remaining possibilitiescandidates.
       Col: 0     Col: 1     Col: 2     Col: 3     Col: 4     Col: 5     Col: 6     Col: 7     Col: 8     
Row 0:   2 4  7 9   2 456        4567 9   23 56  9  --- 5 ---  --- 1 ---    3  67 9  --- 8 ---     4 67   
Row 1:  12    7 9  --- 8 ---  1    67 9   23  6  9  --- 4 ---   23  67    1 3  67 9    3  67 9  --- 5 --- 
Row 2:  1  4  7 9  1  456     --- 3 ---      56 89      5 78       5678   --- 2 ---     4 67 9  1  4 67   
Row 3:  1234       12345      1  45      12  5  8   --- 6 ---   2  5 78       5 78      45 7    --- 9 --- 
Row 4:  --- 6 ---  --- 7 ---      5      --- 4 ---   2  5  8   --- 9 ---      5  8   --- 1 ---  --- 3 --- 
Row 5:  --- 8 ---  12 45      1  45   9  12  5      --- 3 ---   2  5 7        567       4567     2 4 67   
Row 6:  1 3   7    1 3  6     --- 2 ---    3 56 8       5  8     3 56 8   --- 4 ---    3 567 9  1    678  
Row 7:  --- 5 ---  1 34 6     1  4 678     3  6 8   --- 9 ---    34 6 8   1 3  678   --- 2 ---  1    678  
Row 8:    34       --- 9 ---     4 6 8   --- 7 ---  --- 1 ---   23456 8     3 56 8     3 56          6 8  
Now, let us load a Sudoku grid that is deliberately invalid. Click on "File", "Samples" and "!DELIBERATELY BROKEN!". Note that this grid starts with some issues, for example the value 5 appears twice in the first row.

A few simple rules perform a sanity check, right after loading a grid. In this case, the following messages are printed on standard output:
cell [0,8]: 5 has a duplicate in row 0 cell [0,0]: 5 has a duplicate in row 0 cell [6,0]: 8 has a duplicate in col 0 cell [4,0]: 8 has a duplicate in col 0 Validation complete.
Nevertheless, click on the "Solve" button to apply the solving rules to this invalid grid. This will not complete; some cells remain empty.

The solving functionality has been achieved by the use of rules that implement standard solving techniques. They are based on the sets of values that are still candidates for a cell. If, for instance, such a set contains a single value, then this is the value for the cell. A little less obvious is the single occurrence of a value in one of the groups of nine cells. The rules detecting these situations insert a fact of type Setting with the solution value for some specific cell. This fact causes the elimination of this value from all other cells in any of the groups the cell belongs to. Finally, it is retracted.
Other rules merely reduce the permissible values for some cells. Rules "naked pair", "hidden pair in row", "hidden pair in column" and "hidden pair in square" merely eliminate possibilities but do not establish solutions. More sophisticated eliminations are done by "X-wings in rows", "X-wings in columns", "intersection removal row" and "intersection removal column".
The Java source code can be found in the /src/main/java/org/drools/examples/sudoku directory, with the two DRL files defining the rules located in the /src/main/rules/org/drools/examples/sudoku directory.
The package org.drools.examples.sudoku.swing
      contains a set of classes which implement a framework for Sudoku
      puzzles. Note that this package does not have any dependencies on
      the Drools libraries. SudokuGridModel defines an
      interface which can be implemented to store a Sudoku puzzle as a 9x9
      grid of Cell objects. SudokuGridView is
      a Swing component which can visualize any implementation of
      SudokuGridModel. SudokuGridEvent and
      SudokuGridListener are used to
      communicate state changes between the model and the view: events are
      fired when a cell's value is resolved or changed. If you are familiar
      with the model-view-controller patterns in other Swing components such
      as JTable then this pattern should be familiar.
      SudokuGridSamples provides a number of partially filled
      Sudoku puzzles for demonstration purposes.
Package org.drools.examples.sudoku.rules contains a
      utility class with a method for compiling DRL files.
The package org.drools.examples.sudoku contains a
      set of classes implementing the elementary Cell object
      and its various aggregations: the CellFile subtypes
      CellRow and CellCol as well as
      CellSqr, all of which are subtypes of
      CellGroup. It's interesting to note that Cell
      and CellGroup are subclasses of SetOfNine,
      which provides a property free with the
      type Set<Integer>. For a Cell it
      represents the individual candidate set; for a CellGroup
      the set is the union of all candidate sets of its cells, or, simply,
      the set of digits that still need to be allocated.
With 81 Cell and 27 CellGroup objects and
      the linkage provided by the Cell properties 
      cellRow, cellCol and cellSqr
      and the CellGroup property cells, a list of
      Cell objects, it is possible to write rules that
      detect the specific situations that permit the allocation of a
      value to a cell or the elimination of a value from some candidate 
      set.
An object of class Setting is used for triggering
      the operations that accompany the allocation of a value: its removal
      from the candidate sets of sibling cells and associated cell groups.
      Moreover, the presence of a Setting fact is used in
      all rules that should detect a new situation; this is to avoid
      reactions to inconsistent intermediary states.
An object of class Stepping is used in a
      low priority rule to execute an emergency halt when a "Step"
      does not terminate regularly. This indicates that the puzzle
      cannot be solved by the program.
The class org.drools.examples.sudoku.SudokuExample
      implements a Java application combining the components described.
Validation rules detect duplicate numbers in cell groups. They are combined in an agenda group which enables us to activate them, explicitly, after loading a puzzle.
The three rules "duplicate in cell..." are very similar. The first pattern locates a cell with an allocated value. The second pattern pulls in any of the three cell groups the cell belongs to. The final pattern would find a cell (other than the first one) with the same value as the first cell and in the same row, column or square, respectively.
Rule "terminate group" fires last. It prints a message and calls halt.
There are three types of rules in this file: one group handles the allocation of a number to a cell, another group detects feasible allocations, and the third group eliminates values from candidate sets.
Rules  "set a value", "eliminate a value from Cell" and
      "retract setting" depend on the presence of a Setting
      object. The first rule handles the assignment to the cell and the
      operations for removing the value from the "free" sets of the 
      cell's three groups. Also, it decrements a counter that, when
      zero, returns control to the Java application that has called
      fireUntilHalt(). The purpose of rule
      "eliminate a value from Cell" is to reduce the candidate lists
      of all cells that are related to the newly assigned cell. Finally,
      when all eliminations have been made, rule "retract setting"
      retracts the triggering Setting fact.
There are just two rules that detect a situation where an
      allocation of a number to a cell is possible. Rule "single" fires
      for a Cell with a candidate set containing a single
      number. Rule "hidden single" fires when there is no cell with a
      single candidate but when there is a cell containing a candidate but  
      this candidate is absent from all other cells in one of the three
      groups the cell belongs to. Both rules create and insert a
      Setting fact.
Rules from the largest group of rules implement, singly or in groups of two or three, various solving techniques, as they are employed when solving Sudoku puzzles manually.
Rule "naked pair" detects identical candidate sets of size 2 in two cells of a group; these two values may be removed from all other candidate sets of that group.
A similar idea motivates the three rules "hidden pair in..."; here, the rules look for a subset of two numbers in exactly two cells of a group, with neither value occurring in any of the other cells of this group. This, then, means that all other candidates can be eliminated from the two cells harbouring the hidden pair.
A pair of rules deals with "X-wings" in rows and columns. When there are only two possible cells for a value in each of two different rows (or columns) and these candidates lie also in the same columns (or rows), then all other candidates for this value in the columns (or rows) can be eliminated. If you follow the pattern sequence in one of these rules, you will see how the conditions that are conveniently expressed by words such as "same" or "only" result in patterns with suitable constraints or prefixed with "not".
The rule pair "intersection removal..." is based on the restricted occurrence of some number within one square, either in a single row or in a single column. This means that this number must be in one of those two or three cells of the row or column; hence it can be removed from the candidate sets of all other cells of the group. The pattern establishes the restricted occurrence and then fires for each cell outside the square and within the same cell file.
These rules are sufficient for many but certainly not for all Sudoku puzzles. To solve very difficult grids, the rule set would need to be extended with more complex rules. (Ultimately, there are puzzles that cannot be solved except by trial and error.)
Name: Number Guess Main class: org.drools.examples.numberguess.NumberGuessExample Module: droolsjbpm-integration-examples (Note: this is in a different download, the droolsjbpm-integration download.) Type: Java application Rules file: NumberGuess.drl Objective: Demonstrate use of Rule Flow to organise Rules
The "Number Guess" example shows the use of Rule Flow, a way of controlling the order in which rules are fired. It uses widely understood workflow diagrams for defining the order in which groups of rules will be executed.
Example 7.69. Creating the Number Guess RuleBase: NumberGuessExample.main() - part 1
final KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add( ResourceFactory.newClassPathResource( "NumberGuess.drl",
ShoppingExample.class ),
ResourceType.DRL );
kbuilder.add( ResourceFactory.newClassPathResource( "NumberGuess.rf",
ShoppingExample.class ),
ResourceType.DRF );
final KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages( kbuilder.getKnowledgePackages() );
The creation of the package and the loading of the rules (using the add() method) is the same as
  the previous examples. There is an additional line to add the Rule Flow (NumberGuess.rf), which
  provides the option of specifying different rule flows for the same Knowledge Base. Otherwise, the Knowledge Base is
  created in the same manner as before.
Example 7.70. Starting the RuleFlow: NumberGuessExample.main() - part 2
final StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
KnowledgeRuntimeLogger logger =
KnowledgeRuntimeLoggerFactory.newFileLogger(ksession, "log/numberguess");
ksession.insert( new GameRules( 100, 5 ) );
ksession.insert( new RandomNumber() );
ksession.insert( new Game() );
ksession.startProcess( "Number Guess" );
ksession.fireAllRules();
logger.close();
ksession.dispose();
Once we have a Knowledge Base, we can use it to obtain a Stateful Session. Into our session we insert our facts,
  i.e., standard Java objects. (For simplicity, in this sample, these classes are all contained within our
  NumberGuessExample.java file. Class GameRules provides the maximum range and the
  number of guesses allowed. Class RandomNumber automatically generates a number between 0 and 100 and
  makes it available to our rules, by insertion via the getValue() method. Class Game keeps
  track of the guesses we have made before, and their number.
Note that before we call the standard fireAllRules() method, we also start the process that we
  loaded earlier, via the startProcess() method. We'll learn where to obtain the parameter we pass ("Number
  Guess", i.e., the identifier of the rule flow) when we talk about the rule flow file and the graphical Rule Flow
  Editor below.
Before we finish the discussion of our Java code, we note that in some real-life application we would examine
  the final state of the objects. (Here, we could retrieve the number of guesses, to add it to a high score table.) For
  this example we are content to ensure that the Working Memory session is cleared by calling the dispose()
  method.
If you open the NumberGuess.rf file in the Drools IDE (provided you have the JBoss Rules
  extensions installed correctly in Eclipse) you should see the above diagram, similar to a standard flowchart. Its
  icons are similar (but not exactly the same) as in the JBoss jBPM workflow product. Should you wish to edit the
  diagram, a menu of available components should be available to the left of the diagram in the IDE, which is called the
  palette. This diagram is saved in XML, an (almost) human readable format, using XStream.
If it is not already open, ensure that the Properties View is visible in the IDE. It can be opened by clicking "Window", then "Show View" and "Other", where you can select the "Properties" view. If you do this before you select any item on the rule flow (or click on the blank space in the rule flow) you should be presented with the following set of properties.
Keep an eye on the Properties View as we progress through the example's rule flow, as it presents valuable
  information. In this case, it provides us with the identification of the Rule Flow Process that we used in our earlier
  code snippet, when we called session.startProcess().
In the "Number Guess" Rule Flow we encounter several node types, many of them identified by an icon.
The Start node (white arrow in a green circle) and the End node (red box) mark beginning and end of the rule flow.
A Rule Flow Group box (yellow, without an icon) represents a Rule Flow Groups defined in our rules (DRL)
        file that we will look at later. For example, when the flow reaches the Rule Flow Group "Too High", only those
        rules marked with an attribute of ruleflow-group "Too High" can potentially
        fire.
Action nodes (yellow, cog-shaped icon) perform standard Java method calls. Most action nodes in this
        example call System.out.println(), indicating the program's progress to the user.
Split and Join Nodes (blue ovals, no icon) such as "Guess Correct?" and "More guesses Join" mark places where the flow of control can split, according to various conditions, and rejoin, respectively
Arrows indicate the flow between the various nodes.
The various nodes in combination with the rules make the Number Guess game work. For example, the "Guess" Rule
  Flow Group allows only the rule "Get user Guess" to fire, because only that rule has a matching attribute of
  ruleflow-group "Guess".
Example 7.71. A Rule firing only at a specific point in the Rule Flow: NumberGuess.drl
rule "Get user Guess"
    ruleflow-group "Guess"
    no-loop
    when
        $r : RandomNumber()
        rules : GameRules( allowed : allowedGuesses )
        game : Game( guessCount < allowed )
        not ( Guess() )
    then
        System.out.println( "You have " + ( rules.allowedGuesses - game.guessCount )
                            + " out of " + rules.allowedGuesses
                            + " guesses left.\nPlease enter your guess from 0 to "
                            + rules.maxRange );
        br = new BufferedReader( new InputStreamReader( System.in ) );
        i = br.readLine();        
        modify ( game ) { guessCount = game.guessCount + 1 }
        insert( new Guess( i ) );
endThe rest of this rule is fairly standard. The LHS section (after when) of the rule states
  that it will be activated for each RandomNumber object inserted into the Working Memory where
  guessCount is less than allowedGuesses from the GameRules object and where the
  user has not guessed the correct number.
The RHS section (or consequence, after then) prints a message to the user and then awaits
  user input from System.in. After obtaining this input (the readLine() method call blocks
  until the return key is pressed) it modifies the guess count and inserts the new guess, making both available to the
  Working Memory.
The rest of the rules file is fairly standard: the package declares the dialect as MVEL, and various Java classes are imported. In total, there are five rules in this file:
Get User Guess, the Rule we examined above.
A Rule to record the highest guess.
A Rule to record the lowest guess.
A Rule to inspect the guess and retract it from memory if incorrect.
A Rule that notifies the user that all guesses have been used up.
One point of integration between the standard Rules and the RuleFlow is via the
  ruleflow-group attribute on the rules, as discussed above. A second point of integration
  between the rules (.drl) file and the Rules Flow .rf files is that the Split Nodes (the blue ovals) can use
  values in the Working Memory (as updated by the rules) to decide which flow of action to take. To see how this works,
  click on the "Guess Correct Node"; then within the Properties View, open the Constraints Editor by clicking the button
  at the right that appears once you click on the "Constraints" property line. You should see something similar to the
  diagram below.
Click on the "Edit" button beside "To node Too High" and you'll see a dialog like the one below. The values in the "Textual Editor" window follow the standard rule format for the LHS and can refer to objects in Working Memory. The consequence (RHS) is that the flow of control follows this node (i.e., "To node Too High") if the LHS expression evaluates to true.
Since the file NumberGuess.java contains a main() method, it can be run as a
  standard Java application, either from the command line or via the IDE. A typical game might result in the interaction
  below. The numbers in bold are typed in by the user.
Example 7.72. Example Console output where the Number Guess Example beat the human!
You have 5 out of 5 guesses left. Please enter your guess from 0 to 100 50 Your guess was too high You have 4 out of 5 guesses left. Please enter your guess from 0 to 100 25 Your guess was too low You have 3 out of 5 guesses left. Please enter your guess from 0 to 100 37 Your guess was too low You have 2 out of 5 guesses left. Please enter your guess from 0 to 100 44 Your guess was too low You have 1 out of 5 guesses left. Please enter your guess from 0 to 100 47 Your guess was too low You have no more guesses The correct guess was 48
A summary of what is happening in this sample is:
The main() method of NumberGuessExample.java loads a Rule Base, creates a
      Stateful Session and inserts Game, GameRules and RandomNumber (containing
      the target number) objects into it. The method also sets the process flow we are going to use, and fires all
      rules. Control passes to the Rule Flow.
File NumberGuess.rf, the Rule Flow, begins at the "Start" node.
Control passes (via the "More guesses" join node) to the Guess node.
At the Guess node, the appropriate Rule Flow Group ("Get user Guess") is enabled. In this case the Rule
      "Guess" (in the NumberGuess.drl file) is triggered. This rule displays a message to the user,
      takes the response, and puts it into Working Memory. Flow passes to the next Rule Flow Node.
At the next node, "Guess Correct", constraints inspect the current session and decide which path to take.
If the guess in step 4 was too high or too low, flow proceeds along a path which has an action node with normal Java code printing a suitable message and a Rule Flow Group causing a highest guess or lowest guess rule to be triggered. Flow passes from these nodes to step 6.
If the guess in step 4 was right, we proceed along the path towards the end of the Rule Flow. Before we get there, an action node with normal Java code prints a statement "you guessed correctly". There is a join node here (just before the Rule Flow end) so that our no-more-guesses path (step 7) can also terminate the Rule Flow.
Control passes as per the Rule Flow via a join node, a guess incorrect Rule Flow Group (triggering a rule to retract a guess from Working Memory) onto the "More guesses" decision node.
The "More guesses" decision node (on the right hand side of the rule flow) uses constraints, again looking at values that the rules have put into the working memory, to decide if we have more guesses and if so, goto step 3. If not, we proceed to the end of the rule flow, via a Rule Flow Group that triggers a rule stating "you have no more guesses".
The loop over steps 3 to 7 continues until the number is guessed correctly, or we run out of guesses.
Name: Conway's Game Of Life Main class: org.drools.examples.conway.ConwayAgendaGroupRun org.drools.examples.conway.ConwayRuleFlowGroupRun Module: droolsjbpm-integration-examples (Note: this is in a different download, the droolsjbpm-integration download.) Type: Java application Rules file: conway-ruleflow.drl conway-agendagroup.drl Objective: Demonstrates 'accumulate', 'collect' and 'from'
Conway's Game Of Life, described in http://en.wikipedia.org/wiki/Conway's_Game_of_Life and in http://www.math.com/students/wonders/life/life.html, is a famous cellular automaton conceived in the early 1970's by the mathematician John Conway. While the system is well known as "Conway's Game Of Life", it really isn't a game at all. Conway's system is more like a simulation of a form of life. Don't be intimidated. The system is terribly simple and terribly interesting. Math and Computer Science students alike have marvelled over Conway's system for more than 30 years now. The application presented here is a Swing-based implementation of Conway's Game of Life. The rules that govern the system are implemented as business rules using Drools. This document will explain the rules that drive the simulation and discuss the Drools parts of the implementation.
We'll first introduce the grid view, shown below, designed for the visualisation of the game, showing the "arena" where the life simulation takes place. Initially the grid is empty, meaning that there are no live cells in the system. Each cell is either alive or dead, with live cells showing a green ball. Preselected patterns of live cells can be chosen from the "Pattern" drop-down list. Alternatively, individual cells can be doubled-clicked to toggle them between live and dead. It's important to understand that each cell is related to its neighboring cells, which is fundamental for the game's rules. Neighbors include not only cells to the left, right, top and bottom but also cells that are connected diagonally, so that each cell has a total of 8 neighbors. Exceptions are the four corner cells which have only three neighbors, and the cells along the four border, with five neighbors each.
So what are the basic rules that govern this game? Its goal is to show the development of a population, generation by generation. Each generation results from the preceding one, based on the simultaneous evaluation of all cells. This is the simple set of rules that govern what the next generation will look like:
If a live cell has fewer than 2 live neighbors, it dies of loneliness.
If a live cell has more than 3 live neighbors, it dies from overcrowding.
If a dead cell has exactly 3 live neighbors, it comes to life.
That is all there is to it. Any cell that doesn't meet any of those criteria is left as is for the next generation. With those simple rules in mind, go back and play with the system a little bit more and step through some generations, one at a time, and notice these rules taking their effect.
The screenshot below shows an example generation, with a number of live cells. Don't worry about matching the exact patterns represented in the screen shot. Just get some groups of cells added to the grid. Once you have groups of live cells in the grid, or select a pre-designed pattern, click the "Next Generation" button and notice what happens. Some of the live cells are killed (the green ball disappears) and some dead cells come to life (a green ball appears). Step through several generations and see if you notice any patterns. If you click on the "Start" button, the system will evolve itself so you don't need to click the "Next Generation" button over and over. Play with the system a little and then come back here for more details of how the application works.
Now lets delve into the code. As this is an advanced example we'll
    assume that by now you know your way around the Drools framework and are
    able to connect the presented highlight, so that we'll just focus at a
    high level overview. The example has two ways to execute, one way 
    uses Agenda Groups to manage execution flow, and the other one uses
    Rule Flow Groups to manage execution flow. These two versions are
    implemented in ConwayAgendaGroupRun and
    ConwayRuleFlowGroupRun, respectively. Here,
    we'll discuss the Rule Flow version, as it's what most people will
    use.
All the Cell objects are inserted into the Session
    and the rules in the ruleflow-group "register neighbor" are
    allowed to execute by the Rule Flow process. This group of four rules
    creates Neighbor relations between some cell and its
    northeastern, northern, northwestern and western neighbors. This
    relation is bidirectional, which takes care of the other four directions.
    Border cells don't need any special treatment - they simply won't be
    paired with neighboring cells where there isn't any. By
    the time all activations have fired for these rules, all cells are related
    to all their neighboring cells.
Example 7.73. Conway's Game of Life: Register Cell Neighbour relations
rule "register north east"
    ruleflow-group "register neighbor"
when
    $cell: Cell( $row : row, $col : col )            
    $northEast : Cell( row  == ($row - 1), col == ( $col + 1 ) )    
then                    
    insert( new Neighbor( $cell, $northEast ) );
    insert( new Neighbor( $northEast, $cell ) );        
end
rule "register north"
    ruleflow-group "register neighbor"  
when
    $cell: Cell( $row : row, $col : col )   
    $north : Cell( row  == ($row - 1), col == $col )    
then        
    insert( new Neighbor( $cell, $north ) );
    insert( new Neighbor( $north, $cell ) );        
end
rule "register north west"
    ruleflow-group "register neighbor"
when
    $cell: Cell( $row : row, $col : col )           
    $northWest : Cell( row  == ($row - 1), col == ( $col - 1 ) )                        
then        
    insert( new Neighbor( $cell, $northWest ) );
    insert( new Neighbor( $northWest, $cell ) );        
end
rule "register west"
    ruleflow-group "register neighbor"
when
    $cell: Cell( $row : row, $col : col )          
    $west : Cell( row  == $row, col == ( $col - 1 ) )                       
then        
    insert( new Neighbor( $cell, $west ) );
    insert( new Neighbor( $west, $cell ) );         
endOnce all the cells are inserted, some Java code applies the pattern to the grid, setting certain cells to Live. Then, when the user clicks "Start" or "Next Generation", it executes the "Generation" ruleflow. This ruleflow is responsible for the management of all changes of cells in each generation cycle.
The rule flow process first enters the "evaluate" group, which means
    that any active rule in the group can fire. The rules in this group apply
    the Game-of-Life rules discussed in the beginning of the example,
    determining the cells to be killed and the ones to be given life. We use
    the "phase" attribute to drive the reasoning of the Cell by specific
    groups of rules; typically the phase is tied to a Rule Flow Group in the
    Rule Flow process definition. Notice that it doesn't actually change the
    state of any Cell objectss at this point; this is because 
    it's evaluating the grid in turn and it must complete the full evaluation
    until those changes can be applied. To achieve this, it sets the cell to
    a "phase" which is either Phase.KILL or
    Phase.BIRTH, used later to control actions applied
    to the Cell object.
Example 7.74. Conway's Game of Life: Evaluate Cells with state changes
rule "Kill The Lonely"
    ruleflow-group "evaluate"
    no-loop
when
//  A live cell has fewer than 2 live neighbors
    theCell: Cell( liveNeighbors < 2, cellState == CellState.LIVE,
                   phase == Phase.EVALUATE )
then
    modify( theCell ){
        setPhase( Phase.KILL );
    }
end
rule "Kill The Overcrowded"
    ruleflow-group "evaluate"
    no-loop
when
//  A live cell has more than 3 live neighbors
    theCell: Cell( liveNeighbors > 3, cellState == CellState.LIVE,
                   phase == Phase.EVALUATE )
then
    modify( theCell ){
        setPhase( Phase.KILL );
    }
end
rule "Give Birth"
    ruleflow-group "evaluate"
    no-loop
when
//  A dead cell has 3 live neighbors
    theCell: Cell( liveNeighbors == 3, cellState == CellState.DEAD,
                   phase == Phase.EVALUATE )
then
    modify( theCell ){
        theCell.setPhase( Phase.BIRTH );
    }
end
Once all Cell objects in the grid have been evaluated,
    we first clear any calculation activations that occurred from any previous
    data changes. This is done via the "reset calculate" rule, which clears
    any activations in the "calculate" group. We then enter a split in the
    rule flow which allows any activations in both the "kill" and the "birth"
    group to fire. These rules are responsible for applying the state
    change.
Example 7.75. Conway's Game of Life: Apply the state changes
rule "reset calculate"
    ruleflow-group "reset calculate"
when
then
    WorkingMemory wm = drools.getWorkingMemory();
    wm.clearRuleFlowGroup( "calculate" );
end
rule "kill"
    ruleflow-group "kill"
    no-loop
when
    theCell: Cell( phase == Phase.KILL )
then
    modify( theCell ){
        setCellState( CellState.DEAD ),
        setPhase( Phase.DONE );   
    }
end 
 
rule "birth"
    ruleflow-group "birth"
    no-loop
when
    theCell: Cell( phase == Phase.BIRTH )
then
    modify( theCell ){
        setCellState( CellState.LIVE ),
        setPhase( Phase.DONE );
    }
end At this stage, a number of Cell objects have been
    modified with the state changed to either LIVE or
    DEAD. Now we get to see the power of the
    Neighbor facts defining the cell relations. When a cell
    becomes live or dead, we use the Neighbor relation to
    iterate over all surrounding cells, increasing or decreasing the
    liveNeighbor count. Any cell that has its count changed
    is also set to to the EVALUATE phase, to make sure
    it is included in the reasoning during the evaluation stage of the
    Rule Flow Process. Notice that we don't have to do any iteration
    ourselves; simply by applying the relations in the rules we make
    the rule engine do all the hard work for us, with a minimal amount of
    code. Once the live count has been determined and set for all cells,
    the Rule Flow Process comes to and end. If the user has initially
    clicked the "Start" button, the engine will restart the rule flow;
    otherwise the user may request another generation.
Example 7.76. Conway's Game of Life: Evaluate cells with state changes
rule "Calculate Live"
    ruleflow-group "calculate"
    lock-on-active  
when
    theCell: Cell( cellState == CellState.LIVE )
    Neighbor( cell == theCell, $neighbor : neighbor ) 
then
    modify( $neighbor ){
        setLiveNeighbors( $neighbor.getLiveNeighbors() + 1 ),
        setPhase( Phase.EVALUATE );   
    }
end 
rule "Calculate Dead"
    ruleflow-group "calculate"
    lock-on-active  
when
    theCell: Cell( cellState == CellState.DEAD )
    Neighbor( cell == theCell, $neighbor : neighbor )
then
    modify( $neighbor ){
        setLiveNeighbors( $neighbor.getLiveNeighbors() - 1 ),
        setPhase( Phase.EVALUATE );
    }
end A Conversion for the classic game Pong. Use the keys A, Z and K, M. The ball should get faster after each bounce.
Name: Example Pong Main class: org.drools.games.pong.PongMain
Based on the Adventure in Prolog, over at the Amzi website, http://www.amzi.com/AdventureInProlog/, we started to work on a text adventure game for Drools. They are ideal as they can start off simple and build in complexity and size over time, they also demonstrate key aspects of declarative relational programming.
Name: Example Text Adventure Main class: org.drools.games.adventure.TextAdventure
You can view the 8 minute demonstration and introduction for the example at http://downloads.jboss.org/drools/videos/text-adventures.swf
Name: Example Wumpus World Main class: org.drools.games.wumpus.WumpusWorldMain
Wumpus World is an AI example covered in the book "Artificial Intelligence : A Modern Approach". When the game first starts all the cells are greyed out. As you walk around they become visible. The cave has pits, a wumpus and gold. When you are next to a pit you will feel a breeze, when you are next to the wumpus you will smell a stench and see glitter when next to gold. The sensor icons are shown above the move buttons. If you walk into a pit or the wumpus, you die. A more detailed overview of Wumpus World can be found at http://www.cis.temple.edu/~giorgio/cis587/readings/wumpus.shtml. A 20 minute video showing how the game is created and works is at http://www.youtube.com/watch?v=4CvjKqUOEzM.
Name: Miss Manners Main class: org.drools.benchmark.manners.MannersBenchmark Module: drools-examples Type: Java application Rules file: manners.drl Objective: Advanced walkthrough on the Manners benchmark, covers Depth conflict resolution in depth.
Miss Manners is throwing a party and, being a good host, she wants to arrange good seating. Her initial design arranges everyone in male-female pairs, but then she worries about people have things to talk about. What is a good host to do? She decides to note the hobby of each guest so she can then arrange guests not only pairing them according to alternating sex but also ensuring that a guest has someone with a common hobby, at least on one side.
Five benchmarks were established in the 1991 paper "Effects of Database Size on Rule System Performance: Five Case Studies" by David Brant, Timothy Grose, Bernie Lofaso and Daniel P. Miranker:
Manners uses a depth-first search approach to determine the seating arrangements alternating women and men and ensuring one common hobby for neighbors.
Waltz establishes a three-dimensional interpretation of a line drawing by line labeling by constraint propagation.
WaltzDB is a more general version of Waltz, supporting junctions of more than three lines and using a database.
ARP is a route planner for a robotic air vehicle using the A* search algorithm to achieve minimal cost.
Weaver VLSI router for channels and boxes using a black-board technique.
Manners has become the de facto rule engine benchmark. Its behavior, however, is now well known and many engines optimize for this, thus negating its usefulness as a benchmark which is why Waltz is becoming more favorable. These five benchmarks are also published at the University of Texas http://www.cs.utexas.edu/ftp/pub/ops5-benchmark-suite/.
After the first seating arrangement has been assigned, a
        depth-first recursion occurs which repeatedly assigns correct
        seating arrangements until the last seat is assigned. Manners
        uses a Context instance to control execution flow.
        The activity diagram is partitioned to show the relation of the
        rule execution to the current Context state.
Before going deeper into the rules, let's first take a look at the asserted data and the resulting seating arrangement. The data is a simple set of five guests who should be arranged so that sexes alternate and neighbors have a common hobby.
The Data
The data is given in OPS5 syntax, with a parenthesized list of name and value pairs for each attribute. Each person has only one hobby.
(guest (name n1) (sex m) (hobby  h1)  )
(guest (name n2) (sex f) (hobby  h1)  )
(guest (name n2) (sex f) (hobby  h3)  )
(guest (name n3) (sex m) (hobby  h3)  )
(guest (name n4) (sex m) (hobby  h1)  )
(guest (name n4) (sex f) (hobby  h2)  )
(guest (name n4) (sex f) (hobby  h3)  )
(guest (name n5) (sex f) (hobby  h2)  )
(guest (name n5) (sex f) (hobby  h1)  )
(last_seat (seat 5)  )
The Results
Each line of the results list is printed per execution of the
        "Assign Seat" rule. They key bit to notice is that each line has
        a "pid" value one greater than the last. (The significance of this
        will be explained in the discussion of the rule "Assign Seating".)
        The "ls", "rs", "ln" and "rn" refer to the left and right
        seat and neighbor's name, respectively. The actual implementation
        uses longer attribute names (e.g., leftGuestName,
        but here we'll stick to the notation from the original
        implementation.
[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5] 
[Seating id=2, pid=1, done=false, ls=1, ln=n5, rs=2, rn=n4] 
[Seating id=3, pid=2, done=false, ls=2, ln=n4, rs=3, rn=n3] 
[Seating id=4, pid=3, done=false, ls=3, rn=n3, rs=4, rn=n2] 
[Seating id=5, pid=4, done=false, ls=4, ln=n2, rs=5, rn=n1]
Manners has been designed to exercise cross product joins and Agenda activities. Many people not understanding this tweak the example to achieve better performance, making their port of the Manners benchmark pointless. Known cheats or porting errors for Miss Manners are:
Using arrays for a guests hobbies, instead of asserting each one as a single fact massively reduces the cross products.
Altering the sequence of data can also reduce the amount of matching, increasing execution speed.
It's possible to change the not Conditional
            Element so that the test algorithm only uses the
            "first-best-match", which is, basically, transforming
            the test algorithm to backward chaining. The results are only
            comparable to other backward chaining rule engines or ports of
            Manners.
Removing the context so the rule engine matches the guests and seats prematurely. A proper port will prevent facts from matching using the context start.
It's possible to prevent the rule engine from performing combinatorial pattern matching.
If no facts are retracted in the reasoning cycle, as a
            result of the not CE, the port is incorrect.
The Manners benchmark was written for OPS5 which has two conflict resolution strategies, LEX and MEA. LEX is a chain of several strategies including salience, recency and complexity. The recency part of the strategy drives the depth first (LIFO) firing order. The CLIPS manual documents the Recency strategy as follows:
| Every fact and instance is marked internally with a "time tag" to indicate its relative recency with respect to every other fact and instance in the system. The pattern entities associated with each rule activation are sorted in descending order for determining placement. An activation with a more recent pattern entity is placed before activations with less recent pattern entities. To determine the placement order of two activations, compare the sorted time tags of the two activations one by one starting with the largest time tags. The comparison should continue until one activation’s time tag is greater than the other activation’s corresponding time tag. The activation with the greater time tag is placed before the other activation on the agenda. If one activation has more pattern entities than the other activation and the compared time tags are all identical, then the activation with more time tags is placed before the other activation on the agenda. | ||
| --CLIPS Reference Manual | ||
However Jess and CLIPS both use the Depth strategy, which is simpler and lighter, which Drools also adopted. The CLIPS manual documents the Depth strategy as:
| Newly activated rules are placed above all rules of the same salience. For example, given that fact-a activates rule-1 and rule-2 and fact-b activates rule-3 and rule-4, then if fact-a is asserted before fact-b, rule-3 and rule-4 will be above rule-1 and rule-2 on the agenda. However, the position of rule-1 relative to rule-2 and rule-3 relative to rule-4 will be arbitrary. | ||
| --CLIPS Reference Manual | ||
The initial Drools implementation for the Depth strategy would not work for Manners without the use of salience on the "make_path" rule. The CLIPS support team had this to say:
| The default conflict resolution strategy for CLIPS, Depth, is different than the default conflict resolution strategy used by OPS5. Therefore if you directly translate an OPS5 program to CLIPS, but use the default depth conflict resolution strategy, you're only likely to get the correct behavior by coincidence. The LEX and MEA conflict resolution strategies are provided in CLIPS to allow you to quickly convert and correctly run an OPS5 program in CLIPS. | ||
| --Clips Support Forum | ||
Investigation into the CLIPS code reveals there is undocumented functionality in the Depth strategy. There is an accumulated time tag used in this strategy; it's not an extensively fact by fact comparison as in the recency strategy, it simply adds the total of all the time tags for each activation and compares.
Once the context is changed to START_UP,
        activations are created for all asserted guest. Because all
        activations are created as the result of a single Working Memory
        action, they all have the same Activation time tag. The last
        asserted Guest object would have a higher fact
        time tag, and its Activation would fire because it has the highest
        accumulated fact time tag. The execution order in this rule has little
        importance, but has a big impact in the rule "Assign Seat". The
        activation fires and asserts the first Seating
        arrangement and a Path, and then sets the
        Context attribute state to create 
        an activation for rule findSeating.
rule assignFirstSeat
    when
        context : Context( state == Context.START_UP )
        guest : Guest()
        count : Count()
    then
        String guestName = guest.getName();
        
        Seating seating =
          new Seating( count.getValue(), 1, true, 1, guestName, 1, guestName);
        insert( seating );
        
        Path path = new Path( count.getValue(), 1, guestName );
        insert( path );
        
        modify( count ) { setValue ( count.getValue() + 1 )  }
    System.out.println( "assign first seat :  " + seating + " : " + path );
        modify( context ) {
            setState( Context.ASSIGN_SEATS )
        } 
endThis rule determines each of the Seating
        arrangements. The rule creates cross product solutions for
        all asserted Seating arrangements
        against all the asserted guests except 
        against itself or any already assigned chosen solutions.
rule findSeating
   when 
       context : Context( state == Context.ASSIGN_SEATS )
       $s      : Seating( pathDone == true )
       $g1     : Guest( name == $s.rightGuestName )
       $g2     : Guest( sex != $g1.sex, hobby == $g1.hobby )
       count   : Count()
       not ( Path( id == $s.id, guestName == $g2.name) )
       not ( Chosen( id == $s.id, guestName == $g2.name, hobby == $g1.hobby) )
   then
       int rightSeat = $s.getRightSeat();
       int seatId = $s.getId();
       int countValue = count.getValue();
       
       Seating seating =
         new Seating( countValue, seatId, false, rightSeat,
                      $s.getRightGuestName(), rightSeat + 1, $g2.getName() );
       insert( seating );
                            
       Path path = new Path( countValue, rightSeat + 1, $g2.getName()  );
       insert( path );
       
       Chosen chosen = new Chosen( seatId, $g2.getName(), $g1.getHobby() );
       insert( chosen  );
       System.err.println( "find seating : " + seating + " : " + path +
                           " : " + chosen);
       modify( count ) {setValue(  countValue + 1 )}
       modify( context ) {setState( Context.MAKE_PATH )}
endHowever, as can be seen from the printed results shown earlier,
        it is essential that only the Seating with the highest
        pid cross product be chosen. How can this be possible
        if we have activations, of the same time tag, for nearly all
        existing Seating and Guest objects? For
        example, on the third iteration of findSeating the
        produced activations will be as shown below. Remember, this is from
        a very small data set, and with larger data sets there would be many
        more possible activated Seating solutions, with multiple
        solutions per pid:
=>[ActivationCreated(35): rule=findSeating 
[fid:19:33]:[Seating id=3, pid=2, done=true, ls=2, ln=n4, rs=3, rn=n3] 
[fid:4:4]:[Guest name=n3, sex=m, hobbies=h3] 
[fid:3:3]:[Guest name=n2, sex=f, hobbies=h3]
=>[ActivationCreated(35): rule=findSeating 
[fid:15:23]:[Seating id=2, pid=1, done=true, ls=1, ln=n5, rs=2, rn=n4] 
[fid:5:5]:[Guest name=n4, sex=m, hobbies=h1] 
[fid:2:2]:[Guest name=n2, sex=f, hobbies=h1] 
=>[ActivationCreated(35): rule=findSeating 
[fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5] 
[fid:9:9]:[Guest name=n5, sex=f, hobbies=h1] 
[fid:1:1]:[Guest name=n1, sex=m, hobbies=h1]
The creation of all these redundant activations might seem
        pointless, but it must be remembered that Manners is not about good
        rule design; it's purposefully designed as a bad ruleset to fully
        stress-test the cross product matching process and the Agenda, which
        this clearly does. Notice that each activation has the same time tag
        of 35, as they were all activated by the change in the
        Context object to ASSIGN_SEATS. With OPS5
        and LEX it would correctly fire the activation with the
        Seating asserted last. With Depth, the accumulated fact
        time tag ensures that the activation with the last asserted
        Seating fires.
Rule makePath must always fire before
        pathDone. A Path object is asserted for
        each Seating arrangement, up to the last asserted 
        Seating. Notice that the conditions in
        pathDone are a subset of those in
        makePath - so how do we ensure that makePath
        fires first?
rule makePath
    when 
        Context( state == Context.MAKE_PATH )
        Seating( seatingId:id, seatingPid:pid, pathDone == false )
        Path( id == seatingPid, pathGuestName:guestName, pathSeat:seat )
        not Path( id == seatingId, guestName == pathGuestName )
    then
        insert( new Path( seatingId, pathSeat, pathGuestName ) );
endrule pathDone
    when
        context : Context( state == Context.MAKE_PATH ) 
        seating : Seating( pathDone == false ) 
    then
        modify( seating ) {setPathDone( true )} 
        
    modify( context ) {setState( Context.CHECK_DONE)}
endBoth rules end up on the Agenda in conflict and with identical activation time tags. However, the accumulate fact time tag is greater for "Make Path" so it gets priority.
Rule areWeDone only activates when the last seat
        is assigned, at which point both rules will be activated. For the
        same reason that makePath always wins over
        path Done, areWeDone will take
        priority over rule continue.
rule areWeDone
    when
        context : Context( state == Context.CHECK_DONE ) 
        LastSeat( lastSeat: seat )
        Seating( rightSeat == lastSeat ) 
    then
        modify( context ) {setState(Context.PRINT_RESULTS )}
end
rule continue
    when
        context : Context( state == Context.CHECK_DONE ) 
    then
        modify( context ) {setState( Context.ASSIGN_SEATS )}
end
Assign First seat
=>[fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5]
=>[fid:14:14]:[Path id=1, seat=1, guest=n5]
==>[ActivationCreated(16): rule=findSeating
[fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5]
[fid:9:9]:[Guest name=n5, sex=f, hobbies=h1]
[fid:1:1]:[Guest name=n1, sex=m, hobbies=h1]
==>[ActivationCreated(16): rule=findSeating
[fid:13:13]:[Seating id=1 , pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5]
[fid:9:9]:[Guest name=n5, sex=f, hobbies=h1]
[fid:5:5]:[Guest name=n4, sex=m, hobbies=h1]*
Assign Seating
=>[fid:15:17] :[Seating id=2 , pid=1 , done=false, ls=1, lg=n5, rs=2, rn=n4]
=>[fid:16:18]:[Path id=2, seat=2, guest=n4]
=>[fid:17:19]:[Chosen id=1, name=n4, hobbies=h1]
=>[ActivationCreated(21): rule=makePath 
[fid:15:17] : [Seating id=2, pid=1, done=false, ls=1, ln=n5, rs=2, rn=n4]
[fid:14:14] : [Path id=1, seat=1, guest=n5]*
==>[ActivationCreated(21): rule=pathDone
[Seating id=2, pid=1, done=false, ls=1, ln=n5, rs=2, rn=n4]*
Make Path
=>[fid:18:22:[Path id=2, seat=1, guest=n5]]
Path Done
Continue Process
=>[ActivationCreated(25): rule=findSeating
[fid:15:23]:[Seating id=2, pid=1, done=true, ls=1, ln=n5, rs=2, rn=n4]
[fid:7:7]:[Guest name=n4, sex=f, hobbies=h3]
[fid:4:4] : [Guest name=n3, sex=m, hobbies=h3]*
=>[ActivationCreated(25): rule=findSeating
[fid:15:23]:[Seating id=2, pid=1, done=true, ls=1, ln=n5, rs=2, rn=n4]
[fid:5:5]:[Guest name=n4, sex=m, hobbies=h1]
[fid:2:2]:[Guest name=n2, sex=f, hobbies=h1], [fid:12:20] : [Count value=3]
=>[ActivationCreated(25): rule=findSeating
[fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5]
[fid:9:9]:[Guest name=n5, sex=f, hobbies=h1]
[fid:1:1]:[Guest name=n1, sex=m, hobbies=h1]
Assign Seating
=>[fid:19:26]:[Seating id=3, pid=2, done=false, ls=2, lnn4, rs=3, rn=n3]]
=>[fid:20:27]:[Path id=3, seat=3, guest=n3]]
=>[fid:21:28]:[Chosen id=2, name=n3, hobbies=h3}]
=>[ActivationCreated(30): rule=makePath
[fid:19:26]:[Seating id=3, pid=2, done=false, ls=2, ln=n4, rs=3, rn=n3]
[fid:18:22]:[Path id=2, seat=1, guest=n5]*
=>[ActivationCreated(30): rule=makePath 
[fid:19:26]:[Seating id=3, pid=2, done=false, ls=2, ln=n4, rs=3, rn=n3]
[fid:16:18]:[Path id=2, seat=2, guest=n4]*
=>[ActivationCreated(30): rule=done 
[fid:19:26]:[Seating id=3, pid=2, done=false, ls=2, ln=n4, rs=3, rn=n3]*
Make Path
=>[fid:22:31]:[Path id=3, seat=1, guest=n5]
Make Path 
=>[fid:23:32] [Path id=3, seat=2, guest=n4]
Path Done
Continue Processing
=>[ActivationCreated(35): rule=findSeating
[fid:19:33]:[Seating id=3, pid=2, done=true, ls=2, ln=n4, rs=3, rn=n3]
[fid:4:4]:[Guest name=n3, sex=m, hobbies=h3]
[fid:3:3]:[Guest name=n2, sex=f, hobbies=h3], [fid:12:29]*
=>[ActivationCreated(35): rule=findSeating 
[fid:15:23]:[Seating id=2, pid=1, done=true, ls=1, ln=n5, rs=2, rn=n4] 
[fid:5:5]:[Guest name=n4, sex=m, hobbies=h1]
[fid:2:2]:[Guest name=n2, sex=f, hobbies=h1]
=>[ActivationCreated(35): rule=findSeating 
[fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5] 
[fid:9:9]:[Guest name=n5, sex=f, hobbies=h1], [fid:1:1] : [Guest name=n1, sex=m, hobbies=h1]
Assign Seating
=>[fid:24:36]:[Seating id=4, pid=3, done=false, ls=3, ln=n3, rs=4, rn=n2]]
=>[fid:25:37]:[Path id=4, seat=4, guest=n2]]
=>[fid:26:38]:[Chosen id=3, name=n2, hobbies=h3]
==>[ActivationCreated(40): rule=makePath 
[fid:24:36]:[Seating id=4, pid=3, done=false, ls=3, ln=n3, rs=4, rn=n2]
[fid:23:32]:[Path id=3, seat=2, guest=n4]*
==>[ActivationCreated(40): rule=makePath 
[fid:24:36]:[Seating id=4, pid=3, done=false, ls=3, ln=n3, rs=4, rn=n2] 
[fid:20:27]:[Path id=3, seat=3, guest=n3]*
=>[ActivationCreated(40): rule=makePath 
[fid:24:36]:[Seating id=4, pid=3, done=false, ls=3, ln=n3, rs=4, rn=n2]
[fid:22:31]:[Path id=3, seat=1, guest=n5]*
=>[ActivationCreated(40): rule=done 
[fid:24:36]:[Seating id=4, pid=3, done=false, ls=3, ln=n3, rs=4, rn=n2]*
Make Path 
=>fid:27:41:[Path id=4, seat=2, guest=n4]
Make Path
=>fid:28:42]:[Path id=4, seat=1, guest=n5]]
Make Path
=>fid:29:43]:[Path id=4, seat=3, guest=n3]]
Path Done
Continue  Processing
=>[ActivationCreated(46): rule=findSeating 
[fid:15:23]:[Seating id=2, pid=1, done=true, ls=1, ln=n5, rs=2, rn=n4] 
[fid:5:5]:[Guest name=n4, sex=m, hobbies=h1], [fid:2:2]
[Guest name=n2, sex=f, hobbies=h1]
=>[ActivationCreated(46): rule=findSeating 
[fid:24:44]:[Seating id=4, pid=3, done=true, ls=3, ln=n3, rs=4, rn=n2]
[fid:2:2]:[Guest name=n2, sex=f, hobbies=h1]
[fid:1:1]:[Guest name=n1, sex=m, hobbies=h1]*
=>[ActivationCreated(46): rule=findSeating 
[fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5]
[fid:9:9]:[Guest name=n5, sex=f, hobbies=h1]
[fid:1:1]:[Guest name=n1, sex=m, hobbies=h1]
Assign Seating
=>[fid:30:47]:[Seating id=5, pid=4, done=false, ls=4, ln=n2, rs=5, rn=n1]
=>[fid:31:48]:[Path id=5, seat=5, guest=n1]
=>[fid:32:49]:[Chosen id=4, name=n1, hobbies=h1]