Monday, September 24, 2012

Rules Engine Patterns - Part 2: Direct-Action

In this pattern, the Rules Engine will react to the state of objects by directly acting on those objects utilizing services injected into the rules context.

  • Very little scaffolding necessary to implement pattern.

  • Does not record which rules fired and why.
  • May not scale well unless the underlying service reacting to objects is a proxy to some remote service (e.g. a web service, message bus).

Example:  Bill Lumbergh, VP of Initech, needs to know when one of his engineers has failed to submit a coversheet with his or her TPS Reports.  He has requested the automated system send him an email when this event occurs.

Our model will consist of a TPSReport object and an EmailService (interface).  We will simply print the email to the console when the rule fires.

Simple POJO we will use to model the TPS report.

package com.berico.da;

public class TPSReport {

 protected boolean hasCoverSheet = false;
 protected String author = null;

 public TPSReport(
  boolean hasCoverSheet, String author) {

  this.hasCoverSheet = hasCoverSheet; = author;

 public boolean isHasCoverSheet() {
  return hasCoverSheet;

 public String getAuthor() {
  return author;

Service interface describing the functionality of our email service.  This is what we will reference inside the rule file (instead of a concrete implementation!).

package com.berico.da;

public interface EmailService {

 void email(
  String to, String from, String subject, String message);

Instead of sending out an email, we'll print the email we were going to send to the console.

package com.berico.da;

public class EmailPrinter implements EmailService {

 public void email(
   String to, String from, String subject, String message) {
    "To: %s \nFrom: %s \nSubject: %s\n----------------------\n%s", 
    to, from, subject, message));


This is a Drools Rule Language file describing the actions to take when objects appear in the knowledge session.  In our case, if we see a TPSReport with no coversheet, we email Bill.

package com.berico.da

global EmailService emailService;

rule "Email Bill Lumbergh when no coversheet"
  tpsReport : TPSReport( hasCoverSheet == false )
    "No Coversheet!!!!!",
    tpsReport.getAuthor() + 
    " failed to supply a coversheet!");

This is the application that will drive the session.  You will notice we instantiate the EmailService and register it in the knowledge session.  We then create some TPSReport objects and "insert" them into the session.  Finally, we call "fireAllRules" on the session to have the rules evaluated.

package com.berico.da;

import com.berico.BaseApp;

public class DirectActionApp extends BaseApp {

 protected String getRuleFile() {
  return "DirectAction.drl";

 public DirectActionApp(){
  // Instantiate the email service.
  EmailService emailService = new EmailPrinter();
  // Register the service on the session as a global.
  getSession().setGlobal("emailService", emailService);
  // Create a report that should not fire rule.
  TPSReport michaelTpsReport 
   = new TPSReport(true, "Michael Bolton");
  // Insert object into session.
  // Create a report that should fire rule.
  TPSReport peterTpsReport 
   = new TPSReport(false, "Peter Gibbons");
  // Insert object into session.
  // Evaluate the rules against our objects.
  // Dispose the rule session.
 public static void main(String[] args){
  new DirectActionApp();

On the console, you should see the following message:
Subject: No Coversheet!!!!!
Peter Gibbons failed to supply a coversheet!
There's nothing else to do in this pattern, since the service registered with the Rules Engine directly handles the event.

No comments:

Post a Comment

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