-
Notifications
You must be signed in to change notification settings - Fork 126
POJO Rules
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.
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.
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";
}
}
}
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.
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.