Monday, September 24, 2012

Rules Engine Patterns - Part 1: How to employ a Rules Engine

While many engineers may understand (at least conceptually) what a Rules Engine is, I feel like Rules Engines are used so infrequently because people don't know how to employ them.  In the next few posts, I'm going to discuss design patterns for using a Rules Engine, as well as, deployment strategies for integrating the technology into your architecture.

In this post, I want to lay the ground for setting up your Java development environment for using a Rules Engine.  I will also introduce some basic features of the Drools API.  We will conclude with a brief discussion about the strategies (patterns) you will use for tying your business model to the rules engine.


General Development Environment


To effectively use the JBoss Rules framework, you should at least have the following tools installed in your Java Development environment.

Setting up a Drools Project


1.  Add the dependencies to JBoss Rules in the Maven pom.xml.
<dependency>
 <groupId>org.drools</groupId>
 <artifactId>drools-core</artifactId>
 <version>5.5.0-SNAPSHOT</version>
</dependency>
<dependency>
 <groupId>org.drools</groupId>
 <artifactId>drools-compiler</artifactId>
 <version>5.5.0-SNAPSHOT</version>
</dependency>


2.  Instantiate a Rules Session (provided a rule file). This class will serve as my base class that the other demonstrations will inherit from to initialize their rule sessions ("knowledge sessions" in Drools terminology).

This implementation will limit you to using one rule file, but abstracts the common scaffolding we will rely on in examples in the next few posts.  There are much better ways to setup a Drools environment, like using Guvnor for a dynamic rules repository and wiring up the session using Spring.  We will discuss some of this in the final (5th) post on the topic.

BaseApp.java
package com.berico;

import org.drools.KnowledgeBase;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.io.Resource;
import org.drools.io.ResourceFactory;
import org.drools.logger.KnowledgeRuntimeLoggerFactory;
import org.drools.runtime.StatefulKnowledgeSession;

/**
 * A simple class that will initialize a Drools
 * knowledge session for deriving classes.  This
 * class forces deriving classes to supply a rule
 * file as a requirement of building the session.
 * 
 * @author Richard Clayton (Berico Technologies)
 */
public abstract class BaseApp 
{
 /**
  * A preinitialized knowledge session derived
  * classes will use to perform their tasks.
  */
 private StatefulKnowledgeSession session = null;
 
 /**
  * Force derived classes to use a getter just in 
  * case we decide to do something special on access
  * in the future.
  * @return initialized knowledge session.
  */
 protected StatefulKnowledgeSession getSession(){
  return this.session;
 }
 
 /**
  * Deriving classes must supply a rule file
  * that will be used by the knowledge builder.
  * @return Name of the rule file
  */
 protected abstract String getRuleFile();

 /**
  * Instantiate the application, initializing
  * the knowledge session.
  */
 public BaseApp(){
  
  initializeSession();
 }
 
 /**
  * Initialize the Rules Engine Session.
  */
 private void initializeSession(){
  
  // The knowledge builder is used to compile rule and
  // workflow (BPM) resources into executable code.
  // If types are declared in the rule file, they
  // will also be compiled as Java classes.
  KnowledgeBuilder kbuilder = 
    KnowledgeBuilderFactory.newKnowledgeBuilder();
  
  // Get the rule file supplied by deriving classes.
  // This file will be pulled from the class path.
  Resource ruleFile = ResourceFactory.newClassPathResource(
    this.getRuleFile());
  
  // Add the rule file to the knowledge builder.
  kbuilder.add(ruleFile, ResourceType.DRL);
  
  // Initialize a knowledge base from the knowledge builder.
  // The knowledge base is a container for the known logic 
  // of the rules engine.
  KnowledgeBase knowledgeBase = kbuilder.newKnowledgeBase();
  
  // Initialize a rules/workflow session from the knowledge
  // base.  This is the construct we will use to insert
  // "facts" in the rules engine, apply/evaluate
  // rules, and then react to the results.
  this.session = knowledgeBase.newStatefulKnowledgeSession();
  
  // Log actions occurring in the session to the console.
  KnowledgeRuntimeLoggerFactory.newConsoleLogger(session);
  
  // Log actions occurring in the session to a file.
  KnowledgeRuntimeLoggerFactory.newFileLogger(session, 
    String.format(
     "rule_session_%s.xml", 
     System.currentTimeMillis()));
 }
}

Using the Scaffold

To use the scaffolding we wrote above, simply extend the class, providing the name a rules file located somewhere on your classpath.

A rules file is simply a text file with rules defined in the Drools Expert syntax.  You can find the Drools Expert syntax documentation here: http://docs.jboss.org/drools/release/5.4.0.Final/drools-expert-docs/html_single/.

Perhaps the easiest way to create the rules file is to use the Drools Eclipse Plugin mentioned above.  The plugin provides syntax highlighting and a diagram of the Rete tree for rule files:



I use the standard Maven project structure, storing rule files in the "src/main/resources" directory.  If you do the same, you should be able to refer to the file using the scaffold by filename.

Example Using the Scaffold


To demonstrate how this works, we need a simple POJO that notionally represents our model.  In this case, I'm going to use the obligatory "User" object.

User.java

package com.berico.scaffold;

public class User {

 private String firstName;
 private String lastName;
 private int age;
 
 public User(
  String firstName, String lastName, int age) {
  
  this.firstName = firstName;
  this.lastName = lastName;
  this.age = age;
 }

 public String getFirstName() {
  return firstName;
 }

 public String getLastName() {
  return lastName;
 }

 public int getAge() {
  return age;
 }
}


ScaffoldExample.drl
package com.berico.scaffold

rule "User seen"
  when
    user: User()
  then
    System.out.println("Hello " + user.getFirstName());
end

ScaffoldExampleApp.java
package com.berico.scaffold;

import com.berico.BaseApp;

public class ScaffoldExampleApp extends BaseApp {

 @Override
 protected String getRuleFile() {
  
  return "ScaffoldExample.drl";
 }

 public ScaffoldExampleApp() {
  super();
  
  User richard = new User("Richard", "Clayton", 31);
  
  getSession().insert(richard);
  
  getSession().fireAllRules();
  
  getSession().dispose();
 }
 
 public static void main(String[] args){
  
  new ScaffoldExampleApp();
 }
}

Understanding the Drools API


The extremely simplistic example above demonstrates the general flow of how the Drools engine is used.  Developers will instantiate or collect (say from a queue, or web service) model objects and insert those objects into a Drools Knowledge Session.  Once the rules have been added to the session, you instruct the engine to fireAllRules (evaluate) its ruleset against the objects.

In the demonstration, we simply print to the console when one of our rules successfully evaluates.  Clearly, we need to be able to do more.  So how would you react to a rule being evaluated, especially outside of the context of the rules engine (getting the results out).

Patterns for Rule Engine Employment


I have found that you typically use one of three patterns when solving a problem with a Rules Engine.  Each pattern has its own benefits and disadvantages, and I will emphasize that there isn't one pattern generally considered better than the others.  More importantly, you might mix and match these patterns depending on how the problem you are trying to solve.

Each pattern is distinguished by how they handle the "end state" of a rules session.  On one extreme, you can choose to not keep any state and simply react to conditions that occur when rules are met (e.g.: email admin on error).  On the other extreme, you can choose to record which rule conditions are met and react to the results of those conditions outside of the rules engine (e.g.: validation scenario).

I will detail three following patterns in the next few posts:
  • Direct-Action
  • Request Adjudication
  • Result Compilation

Conclusion


This admittedly is not a very exciting post, but should serve as the basis for understanding the following posts.  In the next post (which I wrote at the same time of this post!), we will discuss the Direct-Action pattern for utilizing a Rules Engine in your middle tier.

No comments:

Post a Comment

Note: Only a member of this blog may post a comment.