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:
In your pom.xml
, you need to add the below dependencies:
knowledge-api
– this provides the interfaces and factoriesdrools-core
– this is the core engine, runtime component. This is the only runtime dependency if you are pre-compiling rules.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.jbpm-flow
,jbpm-flow-builder
andjbpm-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’.
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:
- Start Event – Start the flow with start event
- Gateway[diverge] – Add Diverging Gateway
- Script Task – Add two script tasks
- Gateway[converge] – Add Converging Gateway
- Script Task – Add another script task
- RuleTask – Add Rule Task
- End Event – End the flow with an end event
4. Work Flow Example
Once you do that, the flow you look similar to the below screenshot.
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’:
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.
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.
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.
9. Rule Task Properties
The important attribute is the ‘RuleFlowGroup’. All the rules belonging to this group will get executed.
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.
-
Gateway Diverging: XOR Gateway Diverging: AND
Action1 Constraint: TRUE
Action2 Constraint: TRUEOutput:
Action1
-
Gateway Diverging: OR Gateway Diverging: AND
Action1 Constraint: TRUE
Action2 Constraint: TRUEOutput:
Action1 Action2 Action3 Rule is fired Goodbye cruel world
-
Gateway Diverging: AND Gateway Diverging: AND
Action1 Constraint: TRUE
Action2 Constraint: TRUEOutput:
Action1 Action2 Action3 Rule is fired Goodbye cruel world
-
Gateway Diverging: XOR Gateway Diverging: XOR
Action1 Constraint: TRUE
Action2 Constraint: TRUEOutput:
Action1 Action3 Rule is fired Goodbye cruel world
-
Gateway Diverging: XOR Gateway Diverging: XOR
Action1 Constraint: FALSE
Action2 Constraint: TRUEOutput:
Action2 Action3 Rule is fired Goodbye cruel world
-
Gateway Diverging: XOR Gateway Diverging: XOR
Action1 Constraint: FALSE
Action2 Constraint: FALSEOutput:
Action2 Action3 Rule is fired Goodbye cruel world
-
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
-
Gateway Diverging: OR Gateway Diverging: XOR
Action1 Constraint: TRUE
Action2 Constraint: TRUEOutput:
Action1 Action3 Action2 Action3 Rule is fired Goodbye cruel world
13. Download the Eclipse Project
This was an example about Drools Work Flow.
You can download the full source code of this example here: DroolsWorkflowExample.zip