Human-readable Rules with Drools JBoss Rules 5.0(Part 2)

Exclusive offer: get 50% off this eBook here
Drools JBoss Rules 5.0 Developer's Guide

Drools JBoss Rules 5.0 Developer's Guide — Save 50%

Develop rules-based business logic using the Drools platform

$29.99    $15.00
by Michal Bali | July 2009 | Java Open Source

In this two-part article by Michal Bali, we will see the techniques to create human-readable rules. In the first part of this article we saw Domain Specific Languages (DSLs) and decision tables.

In this part, we'll learn about Drools Flow, ruleflow, and others.

Drools Agenda

Before we talk about how to manage rule execution order, we have to understand Drools Agenda. When an object is inserted into the knowledge session, Drools tries to match this object with all of the possible rules. If a rule has all of its conditions met, its consequence can be executed. We say that a rule is activated. Drools records this event by placing this rule onto its agenda (it is a collection of activated rules). As you may imagine, many rules can be activated, and also deactivated, depending on what objects are in the rule session. After the fireAllRules method call, Drools picks one rule from the agenda and executes its consequence. It may or may not cause further activations or deactivations. This continues until the Drools Agenda is empty.

The purpose of the agenda is to manage the execution order of rules.

Methods for managing rule execution order

The following are the methods for managing the rule execution order (from the user's perspective). They can be viewed as alternatives to ruleflow. All of them are defined as rule attributes.

  • salience: This is the most basic one. Every rule has a salience value. By default it is set to 0. Rules with higher salience value will fire first. The problem with this approach is that it is hard to maintain. If we want to add new rule with some priority, we may have to shift the priorities of existing rules. It is often hard to figure out why a rule has certain salience, so we have to comment every salience value. It creates an invisible dependency on other rules.
  • activation-group: This used to be called xor-group. When two or more rules with the same activation group are on the agenda, Drools will fire just one of them.
  • agenda-group: Every rule has an agenda group. By default it is MAIN. However, it can be overridden. This allows us to partition Drools Agenda into multiple groups that can be executed separately.

Human-readable Rules with Drools JBoss Rules 5.0(Part 2)

The figure above shows partitioned Agenda with activated rules. The matched rules are coming from left and going into Agenda. One rule is chosen from the Agenda at a time and then executed/fired.

At runtime, we can programmatically set the active Agenda group (through the getAgenda().getAgendaGroup(String agendaGroup).setFocus() method of KnowledgeRuntime), or declaratively, by setting the rule attribute auto-focus to true. When a rule is activated and has this attribute set to true, the active agenda group is automatically changed to rule's agenda group. Drools maintains a stack of agenda groups. Whenever the focus is set to a different agenda group, Drools adds this group onto this stack. When there are no rules to fire in the current agenda group, Drools pops from the stack and sets the agenda group to the next one. Agenda groups are similar to ruleflow groups with the exception that ruleflow groups are not stacked.

Note that only one instance of each of these attributes is allowed per rule (for example, a rule can only be in one ruleflow-group ; however, it can also define salience within that group).

Ruleflow

As we've already said, ruleflow can externalize the execution order from the rule definitions. Rules just define a ruleflow-group attribute, which is similar to agenda-group. It is then used to define the execution order. A simple ruleflow (in the example.rf file) is shown in the following screenshot:

Human-readable Rules with Drools JBoss Rules 5.0(Part 2)

The preceding screenshot shows a ruleflow opened with the Drools Eclipse plugin. On the lefthand side are the components that can be used when building a ruleflow. On the righthand side is the ruleflow itself. It has a Start node which goes to ruleflow group called Group 1. After it finishes execution, an Action is executed, then the flow continues to another ruleflow group called Group 2, and finally it finishes at an End node.

Ruleflow definitions are stored in a file with the .rf extension. This file has an XML format and defines the structure and layout for presentational purposes.

Another useful rule attribute for managing which rules can be activated is lock-on-active. It is a special form of the no-loop attribute. It can be used in combination with ruleflow-group or agenda-group. If it is set to true, and an agenda/ruleflow group becomes active/focused, it discards any further activations for the rule until a different group becomes active. Please note that activations that are already on the agenda will be fired.

A ruleflow consists of various nodes. Each node has a name, type, and other specific attributes. You can see and change these attributes by opening the standard Properties view in Eclipse while editing the ruleflow file. The basic node types are as follows:

  • Start
  • End
  • Action
  • RuleFlowGroup
  • Split
  • Join

They are discussed in the following sections.

Start

It is the initial node. The flow begins here. Each ruleflow needs one start node. This node has no incoming connection—just one outgoing connection.

End

It is a terminal node. When execution reaches this node, the whole ruleflow is terminated (all of the active nodes are canceled). This node has one incoming connection and no outgoing connections.

Action

Used to execute some arbitrary block of code. It is similar to the rule consequence—it can reference global variables and can specify dialect.

RuleFlowGroup

This node will activate a ruleflow-group, as specified by its RuleFlowGroup attribute. It should match the value in ruleflow-group rule attribute.

 

Drools JBoss Rules 5.0 Developer's Guide Develop rules-based business logic using the Drools platform
Published: July 2009
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

Split

This node splits the execution flow into one or many branches. It has two properties—name and type. Name is just for display purposes. Type can have three values: AND, OR, and XOR:

  • AND: The execution continues through all of the branches.
  • OR: Each branch has a condition. The condition is basically same as a rule condition. If the condition is true, the ruleflow continues through this branch. There must be at least one condition that is true; otherwise, an exception will be thrown.
  • XOR: Similar to OR type, each branch has a condition, but in this case, with a priority. The ruleflow continues through just one branch, whose condition is true and it has the lowest value in the priority field. There must be at least one condition that is true; otherwise, an exception will be thrown.

The dialog for defining OR and XOR split types looks like the following screenshot:

Human-readable Rules with Drools JBoss Rules 5.0(Part 2)

The screenshot above shows Drools Eclipse plugin ruleflow constraint editor. It is accessible from the standard Eclipse Properties view.

Join

It joins multiple branches into one. It has two properties—name and a type. Name is for display purposes. Type decides when the execution will continue. It can have the following values:

  • AND: Join waits for all of the incoming branches. The execution then continues.
  • XOR: Join node waits for one incoming branch.

Please consult the Drools manual for further node types.

Example

In some rules, we can use salience to define rule execution order. For example, all of the addresses needed to be normalized (that is, converted to enum) before we could report the unknown countries. The unknown country rule used salience of -10, which meant that it would fire only after all of the address normalization rules. We'll now extract this execution order logic into a ruleflow to demonstrate how it works. The ruleflow might look like the following screenshot:

Human-readable Rules with Drools JBoss Rules 5.0(Part 2)

When the execution starts, it goes through the Start node straight into the Split node. In this case, it is an and type split node. It basically creates two parallel branches that will be executed concurrently (note that this doesn't mean multiple threads). We can see that the flow is explicitly specified. address normalization happens before unknown country reporting. Parallel to this branch is a default ruleflow group. It contains the other rules. Finally, a join node of type and is used to block until all branches complete and then the flow continues to the terminal node. We had to use the Join node (instead of going straight to the End node), because as soon as some branch in a ruleflow reaches the End node, it terminates the whole ruleflow (that is, our branches may be canceled before competition, which is not what we want).

The process ID is set to dataTransformation. Click on the canvas in the ruleflow editor and then in the Properties view (Eclipse Properties plugin), set the ID to this value.

Rules

Next, we'll create copy of dataTransformation.drl file and we'll name it dataTransformation-ruleflow.drl. We'll make the following changes:

  • Each rule gets new attribute: ruleflow-group "default"
  • Except the address normalization rules for example:
    rule addressNormalizationUSA
    ruleflow-group "address normalization"

    Code listing 37: Top part of the USA address normalization rule.

  • Unknown country rule gets the "unknown country" ruleflow group.

KnowledgeBase setup

We can now create a knowledge base out of the ruleflow file and the .drl file.

static KnowledgeBase createKnowledgeBaseFromRuleFlow()
throws Exception {
KnowledgeBuilder builder = KnowledgeBuilderFactory
.newKnowledgeBuilder();
builder.add(ResourceFactory.newClassPathResource(
"dataTransformation-ruleflow.drl"), ResourceType.DRL);
builder.add(ResourceFactory.newClassPathResource(
"dataTransformation.rf"), ResourceType.DRF);
if (builder.hasErrors()) {
throw new RuntimeException(builder.getErrors()
.toString());
}

KnowledgeBase knowledgeBase = KnowledgeBaseFactory
.newKnowledgeBase();
knowledgeBase.addKnowledgePackages(builder
.getKnowledgePackages());
return knowledgeBase;
}

Code listing 38: Method that creates KnoweldgeBase with a ruleflow.

A knowledge base is created from both files .drl and .rf. To achieve true isolation of unit tests, consider constructing the knowledge base only from the .drl file or .rf file. That way, the unit tests can focus only on the relevant part.

Tests

The test setup needs to be changed as well. Ruleflows are fully supported only for stateful sessions. Stateful sessions can't be shared across tests because they maintain state. We need to create a new stateful session for each test. We'll move the session initialization logic from the setUpClass method that is called once per test class into the intialize method that will be called once per test method:

static KnowledgeBase knowledgeBase;  
StatefulKnowledgeSession session;

@BeforeClass
public static void setUpClass() throws Exception {
knowledgeBase = createKnowledgeBaseFromRuleFlow();
}

@Before
public void initialize() throws Exception {
session = knowledgeBase.newStatefulKnowledgeSession();

Code listing 39: Excerpt from the unit test initialization.

Once the stateful session is initialized, we can use it.

We'll write a test that will create a new address map with an unknown country. This address map will be inserted into the session. We'll start the ruleflow and execute all of the rules. The test will verify that the unknownCountry rule has been fired:

@Test
public void unknownCountryUnknown() throws Exception {
Map addressMap = new HashMap();
addressMap.put("_type_", "Address");
addressMap.put("country", "no country");

session.insert(addressMap);
session.startProcess("dataTransformation");
session.fireAllRules();

assertTrue(validationReport.contains("unknownCountry"));
}

Code listing 40: Test for the unknown country rule with an unknown country.

Note that the order of the three session methods is important. All of the facts need to be in the session before the ruleflow can be started and rules can be executed.

Please note that in order to test this scenario, we didn't use any agenda filter. This test is more like an integration test where we need to test more rules cooperating together.

Another test verifies that the ruleflow works with a known country:

@Test
public void unknownCountryKnown() throws Exception {
Map addressMap = new HashMap();
addressMap.put("_type_", "Address");
addressMap.put("country", "Ireland");

session.startProcess("dataTransformation");
session.insert(addressMap);
session.fireAllRules();

assertFalse(validationReport.contains("unknownCountry"));
}

Code listing 41: Test for the unknown country rule with a known country.

As a stateful session is being used, every test should call the dispose method on the session after it finishes. It can be done in the following manner:

@After 
public void terminate() {
session.dispose();
}

Code listing 42: Calling the session.dispose method after every test.

Summary

In this two-part article series, we saw the various techniques to create human-readable rules. In the first part of this article looked at Domain Specific Languages (DSLs), decision tables, and others. In this part, we've learned about Drools Flow, Agenda, ruleflows, and various ways of managing rule execution order. Drools Flow managed the execution order in a nice human-readable graphical representation.

If you have read this article you may be interested to view :

Drools JBoss Rules 5.0 Developer's Guide Develop rules-based business logic using the Drools platform
Published: July 2009
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:

About the Author :


Michal Bali

Michal Bali, freelance software developer, has more than 8 years of experience working with Drools and has an extensive knowledge of Java, JEE. He designed and implemented several systems for a major dental insurance company. He is an active member of the Drools community and can be contacted at michalbali@gmail.com.

Books From Packt

JBoss Drools Business Rules
JBoss Drools Business Rules

Flex 3 with Java
Flex 3 with Java

JBoss Tools 3 Developers Guide
JBoss Tools 3 Developers Guide

WordPress 2.7 Cookbook
WordPress 2.7 Cookbook

Spring Web Flow 2 Web Development
Spring Web Flow 2 Web Development

JBoss Portal Server Development
JBoss Portal Server Development

Apache Struts 2 Web Application Development
Apache Struts 2 Web Application Development

Alfresco 3 Enterprise Content Management Implementation
Alfresco 3 Enterprise Content Management Implementation

No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
G
b
q
n
R
c
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software