Book Image

Instant Drools Starter

By : Jeremy Ary
Book Image

Instant Drools Starter

By: Jeremy Ary

Overview of this book

<p>Drools is a popular business rule management system. The book introduces the concept of rules separation, from what to do to how to do it. This Starter guide supports your development to keep pace with your system’s ever-changing needs, making things simple and easy by taking the rigidity out of complex codes.<br /><br />"Instant Drools Starter" is a practical, hand-on guide that provides you with a number of clear and concise explanations with relatable examples. This book offers step-by-step exercises to help you understand business rule management systems. Learn how they work, how they're best used, and how to perform frequently used tasks and techniques.<br /><br />This Starter guide helps you get familiar with the Drools concept. You will learn to evaluate rules engines and cover all the basics, from rules authoring to troubleshooting. This book highlights the capabilities of the Drools modules.</p> <p><br />After discovering exactly what rules are and what a rule engine brings to the table, we will quickly learn how to install Drools. The guide then explores different tools of the trade and gets you writing your first set of rules instantly. We'll then take those rules, write vital codes piece by piece, and put them into action. In addition, with this guide, learn how to document and troubleshoot everything behind the scenes, as well as developing your rules to the next level. "Instant Drools Starter" will cover everything you need to know to get started, so if you are looking for a complete guide that provides simple solutions to complex problems, look no further.</p>
Table of Contents (7 chapters)

Top 5 features you need to know about


Let's discuss some of the more useful features of Drools and how to accomplish a few common tasks that you'll find yourself needing time and time again. We'll also discuss the five core modules that make up Drools so that when you're ready to expand your horizons and dig deeper into what Drools can do for you, you'll know what you're looking for.

Reading and writing Drools Rule Language syntax

Until now, we've only discussed the syntax of rules to the point of getting us off the ground. To continue working with the Drools Rule Language, there's more about the syntax you'll need to know and understand. To start off, let's reexamine a few of the basics we've seen thus far.

Some basics we've used

By now, hopefully I've nailed home the construct of when-then conditions. I think it's important to note here that while the taco shop rules we worked with were quite simple in nature, rules conditions can actually be very complex, meaning a rule may have more than one pattern. The syntax for each is basically the same:

Purchase ( $t : total > 15 )

This condition consists of a type, Purchase, a property that we're accessing, total, which we're binding to a variable, $t, and a Boolean evaluation on that property checking that the total of the purchase is greater than 15. Constraints can be made up of anything that evaluates to a true or false, or a Boolean value. Here we've done a simple "greater than" check, but patterns can also include Boolean properties, method calls, or other more complex Boolean expressions. Now's also a good time to point out that variables can either be bound to properties of an object, or the entire object itself:

rule "example rule"
when
   Purchase ( $t : total )
  $p :  Purchase ( total > $t )
then
  System.out.println("amount " + $p.getTotal() + " > " + $t);
end

As seen here, variables can be accessed in both, a condition and consequence of a rule. If used within a condition, the variable must have been declared and bound previously, and must be used on the right-hand side of the evaluator (that is you'll always use the order property, operator, then variable), otherwise our rule would expect that Purchase has a method get$t() to call, which would cause an exception to be thrown.

Some basics we haven't seen yet

In order to introduce a few additional things you'll want to be familiar with, let's consider this example rule file:

package org.tacoshop

import org.tacoshop.model.Purchase

global int comboCount

// hey here's a rule!
rule "example rule"
when
  $p : Purchase ( tacoCount >= comboCount, ( tacoCount % 2 == 0 ) )
then
  /* this consequence is pretty simple,
    but let's give it a comment anyways */
  $p.setDiscount(addDiscount($p, 0.15));
end

function double addDiscount(Purchase p, double discount) {
  return (p.getDiscount + discount);
}

We're familiar with the package declaration and import syntax, nothing new there. However, we've not seen a global variable declaration yet. Normally in programming, the term "global variable" has been engrained into our minds as a bad thing, but with Drools that's not the case. Global variables are tied to our rule session, meaning they're only global within limitation and available inside all of our rule conditions and consequences. In the previous code, we've referenced the global comboCount variable inside of a constraint to check if we've bought enough tacos to qualify for a combo discount. So where did the value of comboCount come from? Our rule session provides us with methods for handling globals similar to those we have for facts. To get a value in there, we'd want to call session.setGlobal("comboCount", 2) prior to executing our rules with fireAllRules(). If we didn't set a value beforehand, Drools would throw an exception. Also keep in mind that globals should be immutable as to avoid any surprises in unexpected behavior.

Another new concept I've demonstrated in this file is commenting. Any good code should include comments. Rules files can often grow to be very complex and should be no exception. DRL files allow single-line comments using either the # or // prefix, as well as multi-line comments using the /* comment */ construct.

If you take a closer look at the rule condition, you'll notice that we've formed an evaluative statement checking that the number of tacos in the purchase is evenly divisible by two. We're able to use any Java expression within a pattern as part of the condition, so long as the return is a Boolean value contributing to the truth of that condition.

Lastly, notice the function declared in this file. Functions within rule files carry the same functionality and syntax you'd expect from any Java class method, and we've referenced our function from within the rule consequence. If you find yourself repeating a lot of code, then a function can help to eliminate redundancy and simplify refactoring.

Rule attributes

Drools provides us with several ways to modify the behavior of a rule. Most are pretty complex in nature and beyond the scope of this book. We'll examine a few of the basic rule attributes here and recommend referring to the documentation later where the rest are covered in great detail. Rule attributes are added to the rule definition after the rule name, but before the when keyword.

  • no-loop: Sometimes a rule will modify a fact within the consequence and inadvertently cause a scenario where that exact same rule could fire again, starting an infinite loop. Using no-loop assures that the rule cannot trigger itself.

  • salience: We've already seen one example of salience in practice. It's used to determine the priority of a rule. By default, all rules have a salience of zero, but can be given a positive or negative value.

  • dialect: This specifies the syntax used in the rule. This can be specified per-rule, or it can be set on the package level and overridden on a per-rule basis. Currently, the options available are MVEL and Java.

Operators for constraints

Drools provides us with several operators for use within our rule constraints. Some of the most commonly used operators include the following:

  • &&: Both Booleans must be true. Previously we've used && implicitly by including multiple property evaluations in a comma-separated series and by including multiple conditions:

    Purchase ( tacoCount >= 2, discount < 0.15 )
    Purchase ( total > 15 && tacoCount >= 2 )
  • ||: One of the two Booleans must be true. Note that when using && or || on a single property, the syntax can be shortened as follows:

    Purchase ( total < 15 || total > 25 )
    Purchase ( total < 15 || > 25 )
  • ( ) : We can form more complex patterns using parentheses:

    Purchase ( ( total < 15 || > 25 ) && drinkIncluded )
  • this: This operator allows us to reference a fact within the condition of the pattern, which, among other uses, will disallow the same fact from satisfying multiple conditions:

    $p : Purchase ( total > 15 )
    Purchase ( this != $p, tacoCount > 2 )
  • in: This checks if a value exists within a comma-separated list of values for potentially matching (can be paired with not):

    Person ( name in ("Steve", "James", "Everett") )
    Person ( name not in ("Jane", "Becca", "Mary") )
  • matches: This matches against a regular expression (can be paired with not):

    Person ( name matches "^[abc]" )
    Person ( name not matches "^[xyz]" )
  • memberOf: This checks if a field exists within a collection (can be paired with not):

    Child ( name memberOf $niceList )
    Child ( name not memberOf $naughtyList )
  • contains: This is much like memberOf, but allows us to check the inverse—that a collection field contains a value (can be paired with not):

    NiceList ( names contains $childName )
    NaughtyList ( names not contains $childName )
  • Common operators that are likely to be familiar to you from Java are ==, !=, >, >=, <, <=, and so on

Conditional elements for patterns

There are also a handful of conditional elements that you should be aware of which allow for a more complex usage of patterns:

  • exists: At least one matching fact exists:

    exists Purchase ( total > 15 )
  • not: no matching facts exist:

    not Purchase ( total > 15 )
  • from: Allows access to nested collections:

    $school : School ( )
    Student ( name == "Joe" ) from $school.students
  • collect: Allows evaluation of multiple facts within the system as a group:

    $maleStudents : ArrayList ( )
        from collect ( Student ( gender == "male" ) )
  • accumulate: Functions similarly to collect in that we're grouping facts, but also allows for manipulation on the collection prior to returning the result:

    $savings : Number ( )
        from accumulate ( PiggyBank ( $total : total ),
                             sum ( $total ) )
  • eval: Allows for execution of any block that returns a Boolean value. Please note that eval elements are not kind on engine performance and thus should only be used when absolutely necessary.

    eval( isTheSame( $a, $b ) )
  • forall (single): All facts of a given type match all the patterns supplied:

    forall ( Student ( age >= 7 ) )
  • forall (multi): All facts matching the first pattern must also match remaining patterns:

    forall ( $dog : Dog ( breed == "dachshund" )
      Dog ( this == $dog, color == "red" ) )
  • and: Both patterns must be true. Previously we've used and implicitly by including multiple conditions, but these can also be grouped with parentheses:

    ( Purchase ( tacoCount >= 2, discount < 0.15 )
      and Purchase ( total > 15 && tacoCount >= 2 ) )
  • or: One of the two patterns must be true:

    ( Purchase ( total < 15 || total > 25 )
      or Purchase ( total < 15 || > 25 ) )

While I feel that these lists serve as a good basis for understanding various usages within DRL, note that it's not a list of all possible functionalities you will encounter in rules. For example, some words shown here, such as not, might be found within a constraint as a negation operator as well as outside of a pattern as shown in the previous example. As with any good software, Drools is also an ever-changing technology with a development team working hard to bring new functionalities to us with every release, so it's also safe to assume that new operators and conditional elements will be introduced. That being said, learning to yield those functionalities we just listed should provide for a very strong basis to author multitudes of business rules you'll find yourself in need of.

Working with facts

As we learned previously, rules engines reason over logic and facts. Logic, or in our case rules, serve as the bits of knowledge we know while facts serve as the objects that we're considering. Thus far, the rules and sessions we've worked with have been fairly simple. We defined some basic rules, loaded up a session, fired the rules, and that's it. In practice, knowledge bases will often grow in complexity and sessions will grow in size as we add more facts. The eventual need may arise for long living sessions and the ability to alter, add, or remove facts from the session. Drools allows us all of these capabilities. Let's take a look.

Manipulating facts in code

We've already seen how we insert a fact into a session from our executable code using session.insert(fact). However, if we need to modify or remove that fact from the session later, then we'll need a reference to correlate our changes to the proper object in memory. For this, Drools provides us with FactHandle:

Person person = new Person();
FactHandle personHandle = session.insert(new Person());

With a handle such as this, we could go on with our code, call various code blocks that alter our Person object, then update the object in session memory so that the two are in sync:

person.setGender("female");
session.update(personHandle, person);

If, for some reason, we decide in our code that we no longer want person to be reasoned over, then we could use our FactHandle to remove the Person object from the session's working memory:

session.retract(personHandle);

Manipulating facts in rules

As your rules become more evolved, the ability to perform similar fact manipulation actions from within rule consequences becomes desirable. Since rules operate within the realm of the session, we are no longer on the outside peering in and don't need to use fact handles to reference our objects. Inserting a fact from within a consequence looks like this:

then
  insert ( new Person() );
end

In order to alter a fact from within a rule consequence, you'll find that there are two options available to you, modify and update. Though similar in concept, there's one very important difference between the two. When you use modify, the fact is altered and the engine is notified at the same time, preventing a gap between the two steps that could potentially lead to indexing issues with the engine. Update, on the other hand, does not provide this same functionality, so it is recommended that from within a rule consequence, you always use modify. The syntax is as follows:

then
  modify( $person ) { setMood( "happy" ) };
end

And lastly, we can also remove a fact from working memory from within a rule consequence. Retract is identical to its code equivalent, except instead of passing a fact handle, we work directly with the fact itself:

then
  retract ( $person );
end

Testing your rules

Once you've put the work into developing a nice starter set of rules, you're going to want to make sure that as you develop more rules or change application behavior, what you've defined so far continues to do the job originally intended. By writing tests against rules, we can not only ensure their integrity, but also offer a roadmap of sorts to others who may want to examine how the rules behave and flag problem points when things have been refactored to a point of altering rule behavior. So how do we go about testing our rules? We'll use JUnit tests, much like the one we used previously to create a session for our taco shop rules. However, we can improve greatly upon the structure of our test class and do much more than just run a rule set. Let's start with defining our @Before and @After methods, both aptly named as JUnit will run them before and after each test method, respectively. We'll also go ahead and define a helper method to load up our rules using the same approach we did before.

public class UsefulRuleTest {

  private KnowledgeBase knowledgeBase;
  
  private StatefulKnowledgeSession session;
  
  @Before
  public void setUp() {
         
    try {
       // load a new knowledgeBase with our rules
       knowledgeBase = populateKnowledgeBase();
       
       // initialize a session for usage
       session = knowledgeBase.newStatefulKnowledgeSession();
       
    } catch ( Exception e ) {
      e.printStackTrace();
        Assert.fail( e.getMessage() );
    }
  }

  @After
  public void tearDown() {
    
    // if session exists, dispose of it for GC
    if ( session != null ) {
      session.dispose();
    }
  }
   
  private KnowledgeBase populateKnowledgeBase() {
         
    // seed a builder with our rules file from classpath
    KnowledgeBuilder builder = KnowledgeBuilderFactory.newKnowledgeBuilder();
    builder.add(ResourceFactory.newClassPathResource("discountRules.drl"), ResourceType.DRL);
    if (builder.hasErrors()) {
        throw new RuntimeException(builder.getErrors().toString());
    }

    // create a knowledgeBase from our builder packages
    KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase();
    knowledgeBase.addKnowledgePackages(builder.getKnowledgePackages());
    
    return knowledgeBase;
  }
}

Our setup method simply calls off to our helper method to get a knowledge base, then gets a session based on the knowledge base created. Notice that we're using the same rule set established for our taco shop scenario, discountRules.drl. The clean-up method checks if our session needs disposal, because as we mentioned before, we always want to ensure we free up session resources for proper garbage collection when we're done. So now that we have a shell to work with, let's start by adding a test that exercises two of our rules so that we can verify that no exceptions are incurred during rule compilation or execution:

  @Test
  public void testRulesCauseNoExceptions() throws Exception {
             
    // should trigger our first and last rule
    session.insert(new Purchase(new BigDecimal("16"), 2, true));
    
    Assert.assertEquals(2, session.fireAllRules(50));
     }

We've added a purchase that should satisfy the conditions for our purchase total between $15 and $25 and combo rules. To end the test, we've asserted two rules executed, as fireAllRules() will return the number of rules fired. Notice that unlike before, we've passed a parameter to fireAllRules() of 50. This number, when provided, indicates the maximum number of rule executions a session is allowed to perform before it stops and returns control to the calling code. The purpose of this is to handle infinite loops that may have been accidentally introduced in the rules. Without having some break-away mechanism, our thread would never end without intervention, thus it would not return to the test to fail the assertion.

As you can imagine, once you have a multitude of rules in place, the practice of getting tests to exercise a specific group and not triggering others becomes a tricky process. Rather than trying to balance these ever-changing expectations, we can selectively allow rules to fire, which means we can guarantee that only a single or specified set of rules will be exercised within a test. We do this using an implementation of AgendaFilter, which is a single-method interface we can use to filter out the rules we'll allow to fire. Let's add a class that will allow us to specify what rules we'd like to allow per test:

  class CustomAgendaFilter implements AgendaFilter {

    private List<String> rulesToAllow;
    
    public CustomAgendaFilter(String[] ruleNames) {
      this.rulesToAllow = Arrays.asList(ruleNames);
    }
    
    public boolean accept(Activation activation) {
      if (this.rulesToAllow.contains(activation.getRule().getName())) {
        return true;
      } else {
        return false;
      }
    }
  } 

We've set up a custom implementation of AgendaFilter with a constructor that accepts a string array of rule names that should be allowed to fire. The method that we need to extend, accept(), simply checks each activation's rule against the list of rule names, and if present, allows it to continue. Let's run a test with a Purchase variable identical to our first test that triggered two rules, but let's put our new filter to use and assert that only one rule fires:

  @Test
  public void testOnlyComboRuleFires() throws Exception {
    
    session.insert(new Purchase(new BigDecimal("16"), 2, true));
    
    String[] expectedRules = {"purchase contains combo"};
    Assert.assertEquals(1, session.fireAllRules(new CustomAgendaFilter(expectedRules)));
  }

Running this test will show that we did indeed trigger one and only one rule, but how can we be sure that it was the rule we intended to run? Well, the filter does the work for us here, but should we need to confirm rule names for any other reason or check the order in which multiple rules fired, we'd need a different approach. Before we get too far removed from filtering by rule name, it's worth mentioning that Drools provides a few filters for us to match single rules by various means:

  • RuleNameEndsWithAgendaFilter

  • RuleNameEqualsAgendaFilter

  • RuleNameMatchesAgendaFilter

  • RuleNameStartsWithAgendaFilter

Now for a final example; let's put a custom AgendaEventListener implementation into practice that will help solve our dilemma of asserting rule names and order. Here's our new class:

class CustomAgendaEventListener implements AgendaEventListener {

    private List<String> rulesFired = new LinkedList<String>();

    public List<String> getRulesFired() {
      return this.rulesFired;
    }
    
    public void afterActivationFired(AfterActivationFiredEvent event) {
      rulesFired.add(event.getActivation().getRule().getName());
    }
    
    }
}

There are many more methods that we must override in order to implement this interface, but for brevity, I've only included here what's important for this example. In real use, all other overridden methods could just have empty bodies. Now here's our test:

@Test
  public void testVerifyRulesViaListener() throws Exception {
    
    CustomAgendaEventListener listener = new CustomAgendaEventListener();
    session.addEventListener(listener);
    
    // should trigger our first and last rule
    session.insert(new Purchase(new BigDecimal("16"), 2, true));
    
    Assert.assertEquals(2, session.fireAllRules(50));
    Assert.assertEquals(listener.getRulesFired().get(0), "purchase over 15 and less than 25 dollars");
    Assert.assertEquals(listener.getRulesFired().get(1), "purchase contains combo");
  }

We've built our custom listener, attached it to the session, and inserted a fact that we know should trigger two rules. Using our listener, we can assert that not only were the two rules that fired exactly the ones that we anticipated, but also that they've fired in the order we expected due to their salience levels.

Debugging the rule evaluation process

Maurice Wilkes, 1967 Turing award recipient, said the following:

"As soon as we started programming, we found to our surprise that it wasn't as easy to get programs right as we had thought. Debugging had to be discovered. I can remember the exact instant when I realized that a large part of my life from then on was going to be spent in finding mistakes in my own programs."

Debugging is a necessity in most programming constructs, and working with rules is no exception. There will come a time when you need to understand exactly what's happening in a rule session and why something did (or didn't) occur. Thankfully, there are tools available to us that will aid us in our efforts.

Debugging with the Eclipse plugin

If you're taking advantage of the Eclipse plugin provided for working with Drools, then you'll find that the debugging process has not been overlooked. If you've worked with breakpoints and debugging Java code within Eclipse before, the process is very similar when working with rules. Start by opening your rule file and placing breakpoints where you'd like execution to pause for you to examine the current state. Note that only breakpoints placed in rule consequences are valid. You can add them in two ways:

  1. Double-click on the shaded bar (or the line number, if you have them enabled) next to the line where you'd like to place the breakpoint.

  2. Right-click on the shaded bar or line number where you'd like to place a breakpoint and select Toggle Breakpoint.

Now run your executable code as a Drools application by highlighting the method or class name, right-clicking on it, and navigating to Debug As | Drools Application. Execution should halt within your rule consequences at your breakpoints, provided the conditions for the rule have been satisfied and the rule has fired. Breakpoints placed in Java code will be respected as well, allowing us to examine what's happening both in and outside of rules in the same session.

Debugging with listeners

If you aren't using the Eclipse plugin, or perhaps you'd prefer to enhance your debugging by adding a bit of logging to allow for closer examination or auditing, then Drools also provides this capability. In order to better understand how we can do that, we first need to define a few things:

  • Working memory: Whenever we insert, modify, or retract a fact, we're adding, changing, or removing the instance within the working memory. You can think of working memory as the knowledge container; a place where all our facts reside.

  • Agenda: Drools benefits from excellent speeds partly due to the way the ReteOO algorithm is implemented. Without going into too much detail, we'll note that rather than waiting and making all considerations about our logic and data at the instant we give the go-ahead signal to fire rules, the engine does a portion of the work as facts are added, changed, and removed within the working memory. Since our rules are already known when we start to insert our data, the engine can go ahead and do some reasoning and determine, based on the new state of data, which rules would fire if nothing were to change between that point and our request to fire all rules. It does this by maintaining an agenda. When a rule's conditions are met based on the provided data, a rule activation is added to the agenda. If something changes causing that rule's conditions to no longer be satisfied, then the activation is removed from the agenda. When we finally get to the point that we call fireAllRules(), we're simply asking the engine to go down the list of activations currently on the agenda and trigger their consequences. Each time a fact is manipulated in working memory, the rule conditions are reconsidered and the agenda is updated appropriately.

  • Event listener: Both the working memory and the agenda offer us the opportunity to take advantage of the observer pattern and attach special classes to our session that will be notified when certain events occur. These classes, called event listeners, offer us an excellent mechanism for passively debugging what's going on inside of our session.

Drools offers interfaces we can implement to customize event listeners for the working memory and the agenda. They are called, as you might expect, the WorkingMemoryEventListener and the AgendaEventListener. Each contain a series of methods that encapsulate all of the events potentially interesting to us when looking to debug or audit internal processes.

AgendaEventListener

You'll see methods within the AgendaEventListener that go beyond the scope of what's covered in this book, but in particular, take note of the following:

  • activationCancelled

  • activationCreated

  • beforeActivationFired

  • afterActivationFired

Using these four methods, we can monitor the session's agenda as we manipulate facts and cause activations to be added and removed. We can also detect the point in execution when rule consequences are about to be triggered, as well as the point directly after triggering has occurred.

Out of the box, Drools offers an implementation called DebugAgendaEventListener that implements every method of the interface in the same way: by printing the event captured to the system.err stream, which will provide us with a good deal of information about each event. This implementation will prove more than helpful when attempting to quickly take a look behind the scenes at what's going on with your rules.

WorkingMemoryEventListener

Inside the WorkingMemoryEventListener, there are fewer methods present than in the AgendaEventListener, and all are of interest to us. They are as follows:

  • objectInserted

  • objectRetracted

  • objectUpdated

As the names imply, these capture events as facts are added, removed, and updated within working memory. Just as with the agenda listener, Drools has provided a debug implementation allowing detailed output about each event, the DebugWorkingMemoryEventListener.

Inference

Due to complexity, there's one additional functionality, inference, that I've withheld up to this point so that we would be better prepared with debugging and testing methods to fully dive in and understand the concept. Sometimes upon examining an object, we can infer some other piece of information that we may want to add to our rule session for the evaluation of further rules. Since this new information is closely related to the object, the information is typically only important as long as the object remains in session. When the object goes away, the new information is no longer valuable to us, and thus can also go away. Utilizing the support that Drools provides for inference, we can take some of the guess work out of tracking inferred relationships and reduce the amount of maintenance rules needed to remove an inferred fact when the related fact goes away. Let's start with a simple, non-inference example that we can build on and put some of what we've learned so far to use:

rule "fire detected, turn on alarm"
when
    Fire ( $r : room )
    Sprinkler ( room == $r, !on )
then
    System.out.println("fire detected, starting alarm!");
    insert( new Alarm($r) );
end

rule "alarm should turn on sprinklers, extinguishing fire"
when
    Alarm ( $r : room )
    $s : Sprinkler ( room == $r )
then
    System.out.println("alarm sounding, turning on sprinklers");
    modify( $s ){ setOn(true) };
end

rule "sprinkler came on, that puts out the fire"
when
    $f : Fire ( $r : room )
    Sprinkler ( room == $r, on )
then
    System.out.println("sprinklers put out the fire in " + $r);
    retract ( $f );
end

rule "fire's been put out, we should remove the alarm now"
when
    $a : Alarm ( $r : room )
    not Fire ( room == $r )
then
    System.out.println("fire's out, we can turn off the alarm now");
    retract ( $a );
end

Here we have a basic set of rules to take us through the simple steps of sounding an alarm for a fire, turning on the sprinklers, and removing the alarm when the fire is extinguished. Let's write up a test to show that our logic is sound. For brevity, the session initialization and the test structure have been omitted as we've seen them before; feel free to reference the accompanying code for this book, FireAlarmTest.java, to see the test in its entirety as well as the model POJOs in use:

// we need to indicate that room "kitchen" has a sprinkler in it
session.insert(new Sprinkler("kitchen"));

// now let's indicate that a fire has started in the kitchen
session.insert(new Fire("kitchen"));

// we expect all 3 of our rules to fire in this scenario
Assert.assertEquals(3, session.fireAllRules(50));

// fire's detected
Assert.assertEquals(listener.getRulesFired().get(0), "fire detected, turn on alarm");

// sprinkler's turned on
Assert.assertEquals(listener.getRulesFired().get(1), "alarm should turn on sprinklers, extinguishing fire");

// fire gets extinguished
Assert.assertEquals(listener.getRulesFired().get(2), "sprinkler came on, that puts out the fire");

// and finally, we remove the alarm
Assert.assertEquals(listener.getRulesFired().get(3), "fire's been put out, we should remove the alarm now"); 

Everything looks good. All of our assertions passed and we can see from our console output that things progressed as intended:

fire detected, starting alarm!
alarm sounding, turning on sprinklers
sprinklers put out the fire in kitchen
fire's out, we can turn off the alarm now

So, now examine what these rules look like when using inference. Let's change our first rule to use insertLogical, reading as follows:

rule "fire detected, turn on alarm"
when
    Fire ( $r : room )
    Sprinkler ( room == $r, !on )
then
    System.out.println("fire detected, starting alarm!");
    insertLogical( new Alarm($r) );
end

Thanks to inference, we'll no longer have to keep up with removing the alarm ourselves, as it will be removed for us when the rule that initially put it in place is no longer satisfied. That means we can remove our last rule, "fire's been put out, we should remove the alarm now" entirely. Let's write another test that will exercise these rules and show that the alarm is removed from the session via inference:

// we need to indicate that room "kitchen" has a sprinkler in it
session.insert(new Sprinkler("kitchen"));

// now let's indicate that a fire has started in the kitchen
session.insert(new Fire("kitchen"));

// we expect all 3 of our rules to fire in this scenario
Assert.assertEquals(3, session.fireAllRules(50));

// fire's detected
Assert.assertEquals(listener.getRulesFired().get(0), "fire detected, turn on alarm");

// sprinkler's turned on
Assert.assertEquals(listener.getRulesFired().get(1), "alarm should turn on sprinklers, extinguishing fire");

// fire gets extinguished
Assert.assertEquals(listener.getRulesFired().get(2), "sprinkler came on, that puts out the fire");

// assert that working memory contains only the sprinkler, meaning alarm was removed
Assert.assertTrue( session.getObjects().size() == 1);
Assert.assertTrue( session.getObjects().toArray()[0] instanceof Sprinkler);

The console output reflects what we've asserted that our three rules fire, and we've also asserted that the session only contains one object, a Sprinkler, meaning that the Alarm object was successfully removed from the session via inference.

fire detected, starting alarm!
alarm sounding, turning on sprinklers
sprinklers put out the fire in kitchen

There's one remaining caveat to using inference that I should mention before we move on. You'll need to ensure that your classes properly override the equals and hashCode methods in order to properly handle collisions between "equal" of both regularly and logically inserted facts.

The five core modules that make up Drools

Before we dive in, I need to confess a little white lie. These days, Drools only has four modules. The humanity! I know, I'm sorry. So why would I mislead you, the honest, caring, innocent reader into thinking there's five? Well, there used to be five, but one module turned into something much bigger. jBPM, having grown from the roots of the module once known as Drools Flow, has become a project of its very own. However, given that it's completely integrated with Drools and the two are often used together, I felt it very relevant and worthy of inclusion, so let's start there.

jBPM

Business Process Management (BPM) is a means of describing some process that occurs within your organization as a series of steps involved, how those steps are connected, and who is involved in performing them. jBPM, which is based on the BPMN 2.0 specification (a standard for graphical representation), serves as a tool for describing such processes in a flowchart format, giving visibility, clarity, and organization to processes that might otherwise be lacking. jBPM supports the entire process lifecycle, from development to execution and management up to retirement. Let's take a look at the graphical representation of a simple process.

Oscar's Oatmeal Cookies is a small successful business, delivering baked deliciousness worldwide. Its process is simple; whenever a new order is received, Oscar needs to bake some cookies, calculate and print an invoice, and ship the cookies out to the customer. The baking and invoicing cannot happen until an order is received and must be completed prior to shipping, but these two steps can possibly occur simultaneously. With jBPM, that process looks something like this:

Drools Expert

By now we've learned quite a bit about what makes up rules, how to compile knowledge packages, and how to evaluate our rules with rule sessions. All the functionality that we've exercised in our examples belongs to the Expert module, which is the core engine of the system. We tell the engine about our logic and facts, and then we ask it to reason over what we've told it about and make some decisions for us. All other modules extend or rely upon the functionality of the core module, Expert.

Drools Fusion

Fusion gives the rules engine the ability to perform event processing, often referred to as complex event processing (CEP). What's that, you say? Best to start with asking, "What is an event?" When something occurs in a point of time, that's an event. It may be instantaneous, or it may have a duration. What's important is that it has some temporal association. Using that definition of events, we can define CEP as the ability to take in masses of events, determine which are likely to be important, and figure out relationships and patterns between them. A proper example will go a long way in demonstrating what CEP is.

Ever got a call from your bank stating that some potentially fraudulent activity has been detected on your account? They're likely calling to verify a transaction or two with you as something suspicious has been flagged in their system. It could be something like an unusual amount, or maybe the transaction occurred outside of the geographical areas that you typically stay in. Your bank likely monitors massive amounts of transactions in a day. Each of these transactions is an event; something that occurred at a specific point in time. It's likely impossible for the bank to pore over each transaction manually and connect the dots and detect oddities in their records for every customer, so what is the best way to detect and overcome fraudulence? CEP to the rescue! By defining relationships between transactions that we're interested in, such as sudden spikes in amounts or purchases in two different countries within a short timeframe, the bank can feed all their incoming events into a rules engine and allow it to flag or notify someone when something seems a bit off.

Drools Guvnor

The Guvnor module provides us with a centralized repository with a layer of user authentication in which we can store and manage our rules, processes (like the one we just discussed), models, and other entities related to knowledge bases. Guvnor offers a collection of graphical editors with which we can view and edit our rules, and when combined with authentication, provides an excellent avenue for non-techie types to access and work with rules in a guided and controlled way. This allows us to bring the people with the domain expertise into the rule creation and maintenance processes. If you're worried that exposing your rules to business users creates a potential black hole in which rules could disappear forever, worry not! Guvnor provides versioning for all of our rules, so we'll not only be able to recover when necessary, but we'll also be able to audit the change trail and take steps to prevent future recurrences.

Drools Planner

Planner helps us (the name's a hint!) carry out automated planning. In order to optimize how we distribute and utilize resources in an organization, sometimes we need to consider multiple possible solutions and choose which works best for us. If Oscar's Oatmeal Cookies needs to make 100 deliveries in a day and only has 20 trucks to use, they'll probably want to figure out the best way to do it. While they could easily send out one truck to make all 100 stops, or all 20 trucks each with five stops each, these probably aren't the most effective options available. Instead, Oscar could plug his information into Planner, make some decisions about what factors are most important to him (for example, fuel cost versus number of stops any one truck must make) and let the rules engine figure out the optimal solution for his needs.

That's a wrap! Hopefully over the course of this book, I've armed you with the basics you need to get yourself started with Drools. We've only scratched the surface of syntax, features, and uses of Drools. There are many great resources out there when you're ready to start digging deeper, including some great reads from Packt Publishing and many blogs, tutorials, forums, and documentation available across the Internet. I implore you to check them out. Good luck and happy coding!