Skip to content

POJO Rules

Clayton Long edited this page Aug 14, 2017 · 8 revisions

POJO Rules give the developer a ton of flexibility. Whereas, RuleBooks built with the Java DSL can be a great way to quickly and easily construct well bounded RuleBooks, POJO Rules allow the developer to create a library of Rules in a package that defines a RuleBook.

What is a POJO Rule?

Put simply, a POJO Rule is a Rule that is annotated so that a Plain Old Java Object (POJO) can be converted into a Rule. Unlike Rules defined with the Java DSL, POJO Rules are defined on a per class basis. So, one POJO Rule can only define one Rule.

POJO Rules can be converted to Rules on an individual basis, using RuleAdapter. Or more commonly, they can be combined into a RuleBook using all of the POJO Rules contained in a particular package using a RuleBookRunner.

Creating POJO Rules

All POJO Rules must be annotated by @Rule. A method annotated with @When that has no parameters and a boolean return type is mapped to the Rule's condition. One or more methods annotated with @Then that have no parameters are mapped to the Rule's action(s).

@Then annotated action methods can be in one of the two forms:

  • @Then annotated method that returns RuleState - If RuleState.BREAK is returned, the Rule chain is broken and the RuleBook is stopped.
  • @Then annotated method that returns nothing - simply executes the method.

@Given annotated instance variables are injected in the following ways:

  • Instance variables are injected with the value of the fact that has the name of the @Given value property.
  • A FactMap or NameValueReferableMap instance variable with no @Given value property is injected with all the facts for the RuleBook.
  • NaveValueReferable or Fact instance variables are injected with the fact that has the name of the @Given value property.
  • A collection (Set, Map, List) instance variable with no @Given value property is injected with all facts of the collection's generic type.
@Rule
public class MyPojoRule {

  @Given("a fact name")
  String strFact; //injects a fact value of type String with the name "a fact name"

  @Given("a fact name")
  Fact fact; //injects a fact with the name "a fact name"

  @Given
  FactMap factMap; //injects a FactMap with all facts applied to the RuleBook
 
  @Given
  List<Integer> intFactList; //injects a List of all Integer facts applied to the RuleBook

...

A POJO Rule can also have a single @Result annotated instance variable. The value of the @Result instance variable will be the value of the result of the Rule following the successful execution of any @Then annotated method.

Example of a complete POJO Rule

package com.example.rulebook.pet

import com.deliveredtechnologies.rulebook.annotation.*;

@Rule
public class MyPojoRule {
  @Given("sound")
  String sound;

  @Given("primary action")
  String action;

  @Result
  String petType;

  @Then
  public void then() {
    if (sound.equals("meow") && action.equals("sleep")) {
      petType = "cat";
    }
  }
}

Using POJO Rules

The most common way to use POJO Rules is via the RuleBookRunner. As stated above, RuleBookRunner combines all of the Rules in a package into a RuleBook. Let's look at an example.

RuleBook ruleBook = new RuleBookRunner("com.example.rulebook.pet");
ruleBook.run(facts); //assume facts were previously defined

And that's all there is to it! RuleBookRunner scans the specified package for POJO Rules and combines them into a RuleBook.

Ordering of POJO Rules

In the above example, only a single POJO Rule was used. So, order didn't really matter. But what if there are multiple POJO Rules in a package and we want to ensure that ordering of those Rules is enforced. Fortunately, RuleBook has us covered. The @Rule annotation allows for an order property to be specified. Rules that have the same order property value may be run in any order. However, Rules with distinct order property values will be run in the order in which they are specified by the order property.

Let's check out an example.

package com.example.rulebook.pet

import com.deliveredtechnologies.rulebook.annotation.*;

@Rule(order = 1)
public class CatRule {
  @Given("sound")
  String sound;

  @Given("primary action")
  String action;

  @Result
  String petType;

  @Then
  public void then() {
    if (sound.equals("meow") && action.equals("sleep")) {
      petType = "cat";
    }
  }
}
package com.example.rulebook.pet

import com.deliveredtechnologies.rulebook.annotation.*;

@Rule(order = 2)
public class PersianRule {
  @Given("face shape")
  String faceShape;

  @Result
  String petType;

  @Then
  public void then() {
    if (petType.equals("cat") && faceShape.equals("smushed")) {
      petType = "Persian cat";
    }
  }
}
RuleBook ruleBook = new RuleBookRunner("com.example.rulebook.pet");
ruleBook.run(facts); //assume facts were previously defined

In the above example, CatRule runs first in the Rule chain and then PersianRule runs next.

Clone this wiki locally