Using Drools rules with jBPM

This tutorial has been updated to show how to invoke Drool Rules from within a jBPM process, and how to handle the interactions between Process and rules.

Business processes and rules are two core concepts which should be stressed out:

Business processes: Represent what the business does.
Business rules: Represent decisions that the business does.

So, although processes and rules are two different thing, there is a clear advantage if your end users are allowed to combine processes and rules. This means for example that:

  • rules can define which processes to invoke,
  • rules can specify decisions in that process
  • rules can augment (or even override) the behaviour specified in the process (for example to handle exceptional cases)
  • assignment rules can be used to assign actors to (human) tasks
  • rules can be used to dynamically alter the behaviour of your process

So, by delegating important decision to be taken into your rules system, your business processes become much more resilient to change.

Installing the Eclipse plugins to create Drools/jBPM projects

There are several options to design your process with jBPM. In this tutorial we will be using the Eclipse plugins to create Drools rules and to design BPMN2 processes.

In order to handle Drools and jBPM projects from Eclipse first we need to install their plug-in.

In order to design BPMN2 diagrams from Eclipse first we need to install as well a plug-in.

You will need to reboot Eclipse for changes to take effect.

Create a new Drools Rule Engine Project

Start by creating a new Drools project:

drools tutorial with jbpm

Next, choose to build the Project with Maven, which means you don’t need to download the Drools Runtime by yourself. Choose the Maven settings for your project:

drools tutorial jboss jbpm 6

In this very simple rule, we will check a model class (Account) against some validation properties. The first rule is trivial: we just check that the Account instance has some money in it:

import com.sample.Account;

rule "nameRequired"
   no-loop true
   ruleflow-group "helloworldgroup"
        when 
      $account : Account( money <= 0 )
        then
        System.out.println( "Account needs to have some money in it!");
    
end

Now add the Account class to your project:

package com.sample;

public class Account {
    private long money;
    private String name;

    // getters and setters omitted for brevity

    @Override
    public String toString() {
        return "Account [money=" + money + ", name=" + name + "]";
    }

    public Account() {    
    }

}

Good. Now add a new Drools jBPM Process Diagram to your Project:

jbpm diagram drools tutorial

Choose a name to your Process.  In our case we will set the name attribute to “HelloWorld“.

Then, let’s add a Business Rule Task to it:

process drools jbpm 6 tutorial

The Business Rule Task will be in charge to fire our Rule. How do we connect to our Rule ? we can do this through the ruleflow-group attribute by setting it to “helloworldgroup“:

drools jbpm 6 tutorial example

In the META-INF folder we will add a kmodule.xml file which will define our KieSession. In our example it will define a single KieSession where both the Rule and the Process are available:

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
   <kbase name="processrules" packages="com.sample.process">
        <ksession name="sample-process-rules"/>
    </kbase>
</kmodule>

Coding class to Test our Process

Now let’s add a Main class to test our project. An example class follows here:

import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;

public class ProcessMain {

	public static void main(String[] args) throws Exception {	
	
		KieContainer kc = KieServices.Factory.get().getKieClasspathContainer();
	        KieSession ksession2 = kc.newKieSession("sample-process-rules");
		Account account = new Account();
                account.setMoney(0);
		ksession2.insert(account);
		ksession2.startProcess("com.sample.HelloWorld");
		ksession2.fireAllRules();

	}

}

Here is a tree view of our project:

src
└── main
    ├── java
    │   └── com
    │       └── sample
    │           ├── Account.java
    │           └── ProcessMain.java
    └── resources
        ├── com
        │   └── sample
        │       ├── dtables
        │       ├── process
        │       │   ├── HelloWorld.bpmn2
        │       │   └── Rule.drl
        │       └── rules
        ├── logback-test.xml
        └── META-INF
            ├── kmodule.xml
            └── maven
                └── pom.properties

Before running the class, let’s have a look at the dependencies. When you generate a Drools Rule Engine project with the Eclipse plugin, the dependencies are versioned based on the runtime.version of Drools and JBPM. It is preferrable to use BOM files to manage the dependencies, so let’s change them in the pom.xml to look like this:

 <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.drools</groupId>
                <artifactId>drools-bom</artifactId>
                <type>pom</type>
                <version>${runtime.version}</version>
                <scope>import</scope>
            </dependency>
            <dependency>
                <groupId>org.jbpm</groupId>
                <artifactId>jbpm-bom</artifactId>
                <type>pom</type>
                <version>${runtime.version}</version>
                <scope>import</scope>
            </dependency>
        </dependencies>
  </dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.kie</groupId>
      <artifactId>kie-api</artifactId>
    </dependency>
    <dependency>
      <groupId>org.drools</groupId>
      <artifactId>drools-core</artifactId>
    </dependency>
    <dependency>
      <groupId>org.drools</groupId>
      <artifactId>drools-decisiontables</artifactId>
    </dependency>
    <dependency>
      <groupId>org.jbpm</groupId>
      <artifactId>jbpm-test</artifactId>
    </dependency>
     <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-api</artifactId>
      <version>${slf4j.version}</version>
    </dependency>
  </dependencies>

In your properties section you will include the Runtime version of Drools and jBPM:

 <runtime.version>7.42.0.Final</runtime.version>

Please note that some dependencies such as jbpm-persistence-jpa and jbpm-human-task-core are not needed in this basic example, however as expand your project you will probably need to use Human Tasks

When launched the Console, the Drools Rule engine will start and you should see on your console the following message:

Account needs to have some money in it!
Of course verify that setting a positive value for money, the message will not be displayed.

The source code for this example is available here: https://github.com/fmarchioni/mastertheboss/tree/master/drools/drools-jbpm


Using Global variables in your rules

Global variables are variables assigned to a session. They can be used for various reasons:

  • You can use tham as constants in your session
  • You can use them as output of your rule (think about a reporting—a rule could write some message to a global report variable)
  • You can use it as entry points for some services such as logging, which are used within rules

Now let’s create our global object named RiskyAccounts which is a vector containing all accounts which have money less than zero:

package com.sample;

import java.util.ArrayList;

public class RiskyAccounts {
    private ArrayList accounts = new ArrayList();
    
  public void add(Account acc) {
      accounts.add(acc);
  }
  public void listRiskyAccounts() {
      for (Account acc : accounts)
      System.out.println(acc);
  }
}

Now we need to import it as global into our rule:

import com.sample.Account;
import com.sample.RiskyAccounts;

global RiskyAccounts risky;
rule "enoughMoney"
ruleflow-group "helloworldgroup"
	when
	$account : Account( money < 0 )
	then
	  System.out.println( "Not enough money on the account!");
	  risky.add($account);

end

This rule simply adds the rule into the vector if money < 0. We will modify our main class to include in the KSession the global variable RiskyAccount:

package com.sample;

import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;

public class App {
	public static void main(String[] args) throws Exception {
		KieServices ks = KieServices.Factory.get();
		KieContainer kContainer = ks.getKieClasspathContainer();
		KieSession kSession = kContainer.newKieSession();
		Account account = new Account();
		account.setName("frank");
		account.setMoney(-10);
		RiskyAccounts risky = new RiskyAccounts();
		kSession.setGlobal("risky", risky);
		kSession.insert(account);
		kSession.startProcess("demo.test");
		kSession.fireAllRules();
	}

}

Now your process will use the RiskyAccounts to store all Accounts which have a negative value for money attribute. Pretty simple, isn’t it ?