JBoss Drools

Drools Workflow Example

Workflow says exactly what will happen at each stage in the process. As soon as the workflow reaches a step, we will fire the actions associated with it. Ruleflow does something similar but we don’t have any control on the actual rules that may fire as they are selected by the rule engine.

A business process or workflow describes the order in which a series of steps need to be executed, using a flow chart. This makes it much easier to describe a complex composition of various tasks.
 
 
 
 

 
Drools 5.0 introduces a powerful (extensible) workflow engine. It allows users to specify their business logic using both rules and processes (where powerful interaction between processes and rules is possible) and offers a unified enviroment.

If you want to more know about Drools Introduction or its setup, read here.

This example uses the following frameworks:

  1. Maven 3.2.3
  2. Java 8
  3. Drools 6.2
  4. Eclipse  as the IDE, version Luna 4.4.1.

In your pom.xml, you need to add the below dependencies:

  1. knowledge-api – this provides the interfaces and factories
  2. drools-core – this is the core engine, runtime component. This is the only runtime dependency if you are pre-compiling rules.
  3. drools-complier – this contains the compiler/builder components to take rule source, and build executable rule bases. You don’t need this during runtime, if your rules are pre-compiled.
  4. jbpm-flow, jbpm-flow-builder and jbpm-bpmn2 – Business Process Management (BPM) Suite

1. Dependencies

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>DroolsWorkFlowExample</groupId>
  <artifactId>com.javacodegeeks.drools</artifactId>
  <version>1.0</version>
  	<dependencies>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>knowledge-api</artifactId>
			<version>${drools.version}</version>
		</dependency>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-core</artifactId>
			<version>${drools.version}</version>
		</dependency>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-compiler</artifactId>
			<version>${drools.version}</version>
		</dependency>
		<dependency>
			<groupId>org.jbpm</groupId>
			<artifactId>jbpm-flow</artifactId>
			<version>${jbpm.version}</version>
		</dependency>
		<dependency>
			<groupId>org.jbpm</groupId>
			<artifactId>jbpm-flow-builder</artifactId>
			<version>${jbpm.version}</version>
		</dependency>
		<dependency>
			<groupId>org.jbpm</groupId>
			<artifactId>jbpm-bpmn2</artifactId>
			<version>${jbpm.version}</version>
		</dependency>
	</dependencies>
	<properties>
		<drools.version>6.2.0.Final</drools.version>
		<jbpm.version>6.2.0.Final</jbpm.version>
	</properties>
</project>

2. Create Flow File

Lets create our Flow file for the Drools Flow engine. Select New -> Other on the project node and under the Drools folder select ‘Flow File’.

Drools Flow File
Drools Flow File

3. Work Flow Palette

In order to configure the work flow properties, you must open the ‘Drools Perspective’ and open the work flow file (*.bpmn) in BPMN2 Process Editor. Next, you need to drop the following onto the designer window:

  1. Start Event – Start the flow with start event
  2. Gateway[diverge] – Add Diverging Gateway
  3. Script Task – Add two script tasks
  4. Gateway[converge] – Add Converging Gateway
  5. Script Task – Add another script task
  6. RuleTask – Add Rule Task
  7. End Event – End the flow with an end event

Drools Work Flow Pallete
Drools Work Flow Pallete

4. Work Flow Example

Once you do that, the flow you look similar to the below screenshot.

Drools Flow Diagram
Drools Flow Diagram

5. Process Properties

Once you have selected ‘Drools Perspective’, click on the whitespace anywhere on the diagram designer and then click on the properties tab. You need to provide the process ID attribute. We have set it to ‘com.javacodegeeks.drools’:

BPMN Properties
BPMN Properties

6. Diverging Gateway Properties

Now on the designer select the first Gateway (diverge) and under properties tab set its type to ‘XOR’. You can also play with other different types ‘OR’, ‘AND’ and then see how the final output changes.

Diverging Gateway  Properties
Diverging Gateway Properties

7. Script Task Properties

If the gateway, allows, the control will reach the script task. In script task, we will set the action to be executed. Here we have kept it simple, it prints ‘Action1’. We will do the same for other actions nodes.

Script Task Properties
Script Task Properties

8. Converging Gateway Properties

For the second converging Gateway, the allowed types are ‘XOR’ and ‘AND’. If we select ‘AND’ then we will be able to proceed further ONLY when the flow comes through the top and bottom branches. If one of them is missing then Action3 will not be executed. If we select ‘XOR’, it ensures that we have to get the flow only from one of the nodes to proceed.

 

gateway_converging_properties

9. Rule Task Properties

The important attribute is the ‘RuleFlowGroup’. All the rules belonging to this group will get executed.

 

RuleTask Properties
RuleTask Properties

10. Knowledge Module Configuration

We need a basic configuration for Drools to pickup the flow file and the drool rules file.

kmodule.xml:

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

11. Sample Drools Rule

Message is simple bean which is refereed to by the drools rule file.

Message:

package com.javacodegeeks.drools;

/**
 * This is a sample class to launch a rule.
 */
public class Message {

	public static final int HELLO = 0;
	public static final int GOODBYE = 1;

	private String message;

	private int status;

	public String getMessage() {
		return this.message;
	}

	public void setMessage(String message) {
		this.message = message;
	}

	public int getStatus() {
		return this.status;
	}

	public void setStatus(int status) {
		this.status = status;
	}

}

Here is a simple drools file which has two rule, both belonging to ‘Group1’.

Sample.drl:

package com.javacodegeeks.drools
 
import com.javacodegeeks.drools.Message;
 
rule "One"
ruleflow-group "Group1"
    when
        m : Message( status == Message.HELLO, myMessage : message )
    then
        System.out.println( myMessage );
        m.setMessage( "Goodbye cruel world" );
        m.setStatus( Message.GOODBYE );
        update( m );
end

rule "Two"
ruleflow-group "Group1"
    when
        Message( status == Message.GOODBYE, myMessage : message )
    then
        System.out.println( myMessage );
end

The workflow can also be opened in plain text mode and what you will see is an XML file.

workFlowExample.bpmn:

<?xml version="1.0" encoding="UTF-8"?> 
<definitions id="Definition"
             targetNamespace="http://www.jboss.org/drools"
             typeLanguage="http://www.java.com/javaTypes"
             expressionLanguage="http://www.mvel.org/2.0"
             xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://www.omg.org/spec/BPMN/20100524/MODEL BPMN20.xsd"
             xmlns:g="http://www.jboss.org/drools/flow/gpd"
             xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
             xmlns:dc="http://www.omg.org/spec/DD/20100524/DC"
             xmlns:di="http://www.omg.org/spec/DD/20100524/DI"
             xmlns:tns="http://www.jboss.org/drools">

  <process processType="Private" isExecutable="true" id="com.javacodegeeks.drools" name="Drools Work Flow Example" tns:packageName="com.javacodegeeks.drools" >

    <!-- nodes -->
    <scriptTask id="_2" name="Action1" >
      <script>System.out.println("Action1");</script>
    </scriptTask>
    <startEvent id="_1"  isInterrupting="true">
    </startEvent>
    <endEvent id="_3" >
        <terminateEventDefinition />
    </endEvent>
    <inclusiveGateway id="_jbpm-unique-1" name="Gateway" gatewayDirection="Diverging" >
    </inclusiveGateway>
    <parallelGateway id="_jbpm-unique-3" name="Gateway" gatewayDirection="Converging" >
    </parallelGateway>
    <scriptTask id="_jbpm-unique-4" name="Action2" >
      <script>System.out.println("Action2");</script>
    </scriptTask>
    <scriptTask id="_jbpm-unique-5" name="Action3" >
      <script>System.out.println("Action3")</script>
    </scriptTask>
    <businessRuleTask id="_jbpm-unique-6" name="A" g:ruleFlowGroup="Group1" >
      <ioSpecification>
        <inputSet>
        </inputSet>
        <outputSet>
        </outputSet>
      </ioSpecification>
    </businessRuleTask>

    <!-- connections -->
    <sequenceFlow id="_jbpm-unique-1-_2" sourceRef="_jbpm-unique-1" targetRef="_2" name="constraint" tns:priority="1" >
      <conditionExpression xsi:type="tFormalExpression" language="http://www.java.com/java" >return true;</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="_jbpm-unique-6-_3" sourceRef="_jbpm-unique-6" targetRef="_3" />
    <sequenceFlow id="_1-_jbpm-unique-1" sourceRef="_1" targetRef="_jbpm-unique-1" />
    <sequenceFlow id="_2-_jbpm-unique-3" sourceRef="_2" targetRef="_jbpm-unique-3" />
    <sequenceFlow id="_jbpm-unique-4-_jbpm-unique-3" sourceRef="_jbpm-unique-4" targetRef="_jbpm-unique-3" />
    <sequenceFlow id="_jbpm-unique-1-_jbpm-unique-4" sourceRef="_jbpm-unique-1" targetRef="_jbpm-unique-4" name="constraint" tns:priority="1" >
      <conditionExpression xsi:type="tFormalExpression" language="http://www.java.com/java" >return true;</conditionExpression>
    </sequenceFlow>
    <sequenceFlow id="_jbpm-unique-3-_jbpm-unique-5" sourceRef="_jbpm-unique-3" targetRef="_jbpm-unique-5" />
    <sequenceFlow id="_jbpm-unique-5-_jbpm-unique-6" sourceRef="_jbpm-unique-5" targetRef="_jbpm-unique-6" />

  </process>

  <bpmndi:BPMNDiagram>
    <bpmndi:BPMNPlane bpmnElement="com.javacodegeeks.drools" >
      <bpmndi:BPMNShape bpmnElement="_2" >
        <dc:Bounds x="222" y="110" width="80" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_1" >
        <dc:Bounds x="140" y="38" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_3" >
        <dc:Bounds x="669" y="204" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_jbpm-unique-1" >
        <dc:Bounds x="141" y="201" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_jbpm-unique-3" >
        <dc:Bounds x="339" y="198" width="48" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_jbpm-unique-4" >
        <dc:Bounds x="230" y="279" width="80" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_jbpm-unique-5" >
        <dc:Bounds x="429" y="200" width="80" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNShape bpmnElement="_jbpm-unique-6" >
        <dc:Bounds x="553" y="204" width="80" height="48" />
      </bpmndi:BPMNShape>
      <bpmndi:BPMNEdge bpmnElement="_jbpm-unique-1-_2" >
        <di:waypoint x="165" y="225" />
        <di:waypoint x="262" y="134" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_jbpm-unique-6-_3" >
        <di:waypoint x="593" y="228" />
        <di:waypoint x="693" y="228" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_1-_jbpm-unique-1" >
        <di:waypoint x="164" y="62" />
        <di:waypoint x="165" y="225" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_2-_jbpm-unique-3" >
        <di:waypoint x="262" y="134" />
        <di:waypoint x="363" y="222" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_jbpm-unique-4-_jbpm-unique-3" >
        <di:waypoint x="270" y="303" />
        <di:waypoint x="363" y="222" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_jbpm-unique-1-_jbpm-unique-4" >
        <di:waypoint x="165" y="225" />
        <di:waypoint x="270" y="303" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_jbpm-unique-3-_jbpm-unique-5" >
        <di:waypoint x="363" y="222" />
        <di:waypoint x="469" y="224" />
      </bpmndi:BPMNEdge>
      <bpmndi:BPMNEdge bpmnElement="_jbpm-unique-5-_jbpm-unique-6" >
        <di:waypoint x="469" y="224" />
        <di:waypoint x="593" y="228" />
      </bpmndi:BPMNEdge>
    </bpmndi:BPMNPlane>
  </bpmndi:BPMNDiagram>

</definitions>

12. Run Drools Work Flow Example

Let’s run the Drools Work Flow example.

Now its the time to run the rule. In order to run the rule, Drools provides a configuration file called kmodule.xml. It acts as a descriptor that selects resources to knowledge bases and configures those knowledge bases and sessions.

KBase is a repository of all the application’s knowledge definitions. Sessions are created from it and then data is inserted into the session which in turn will be used to start the process.

Here is the configuration file which is located in META-INF directory.

In order to run the process, we need to insert the message bean as rule will need it, then start process using its ID and finally fire all the rules. We call kSession.fireAllRules() to run the rule.

DroolsWorkflowExample:

package com.javacodegeeks.drools;

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

/**
 * This is a sample file to launch a process.
 */
public class DroolsWorkflowExample {

    public static final void main(String[] args) {
        try {
            // load up the knowledge base
	        KieServices ks = KieServices.Factory.get();
    	    KieContainer kContainer = ks.getKieClasspathContainer();
        	KieSession kSession = kContainer.newKieSession("ksession-process");
        	Message message = new Message();
            message.setMessage("Rule is fired");
            message.setStatus(Message.HELLO);
            kSession.insert(message);
            
            // start a new process instance
            kSession.startProcess("com.javacodegeeks.drools", null);    
            kSession.fireAllRules();
        } catch (Throwable t) {
            t.printStackTrace();
        }
    }

}

Selecting ‘AND’ for the second gateway would require two branches to be executed. If it is ‘XOR’, ‘Action3’ will get executed if one of the branches gets executed. It won’t get invoked if both branches execute. Let’s play with values and see for our self.

Here are the various outputs.

  1. Gateway Diverging: XOR Gateway Diverging: AND
    Action1 Constraint: TRUE
    Action2 Constraint: TRUE

    Output:

    Action1
    
  2. Gateway Diverging: OR Gateway Diverging: AND
    Action1 Constraint: TRUE
    Action2 Constraint: TRUE

    Output:

    Action1
    Action2
    Action3
    Rule is fired
    Goodbye cruel world
    
  3. Gateway Diverging: AND Gateway Diverging: AND
    Action1 Constraint: TRUE
    Action2 Constraint: TRUE

    Output:

    Action1
    Action2
    Action3
    Rule is fired
    Goodbye cruel world
    
  4. Gateway Diverging: XOR Gateway Diverging: XOR
    Action1 Constraint: TRUE
    Action2 Constraint: TRUE

    Output:

    Action1
    Action3
    Rule is fired
    Goodbye cruel world
    
  5. Gateway Diverging: XOR Gateway Diverging: XOR
    Action1 Constraint: FALSE
    Action2 Constraint: TRUE

    Output:

    Action2
    Action3
    Rule is fired
    Goodbye cruel world
    
  6. Gateway Diverging: XOR Gateway Diverging: XOR
    Action1 Constraint: FALSE
    Action2 Constraint: FALSE

    Output:

    Action2
    Action3
    Rule is fired
    Goodbye cruel world
    
  7. If you have selected XOR and none of the branches work, you will get an exception

    org.jbpm.workflow.instance.WorkflowRuntimeException: [com.javacodegeeks.drools:1 - Gateway:4] -- XOR split could not find at least one valid outgoing connection for split Gateway
    
  8. Gateway Diverging: OR Gateway Diverging: XOR
    Action1 Constraint: TRUE
    Action2 Constraint: TRUE

    Output:

    Action1
    Action3
    Action2
    Action3
    Rule is fired
    Goodbye cruel world
    

13. Download the Eclipse Project

This was an example about Drools Work Flow.

Download
You can download the full source code of this example here: DroolsWorkflowExample.zip

Ram Mokkapaty

Ram holds a master's degree in Machine Design from IT B.H.U. His expertise lies in test driven development and re-factoring. He is passionate about open source technologies and actively blogs on various java and open-source technologies like spring. He works as a principal Engineer in the logistics domain.
Subscribe
Notify of
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments
Back to top button