Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Drools With Mule 4

DZone's Guide to

Drools With Mule 4

Implementing Drools requires two things: authoring and runtime.

· Integration Zone ·
Free Resource

SnapLogic is the leading self-service enterprise-grade integration platform. Download the 2018 GartnerMagic Quadrant for Enterprise iPaaS or play around on the platform, risk free, for 30 days.

Drools is a business rule management system with a forward-chaining and backward-chaining inference-based rules engine, allowing fast and reliable evaluation of business rules and complex event processing. A rule engine is also a fundamental building block to create an expert system, which, in artificial intelligence, is a computer system that emulates the decision-making ability of a human expert.

Implementing Drools requires two things: authoring and runtime. Authoring is the creation of rules files, and runtime involves working memory to execute the rules.

In Mule 3.x, embedded Drools rule definitions (*.drl) could be executed by bpm:drools component however in Mule 4, Drools module has been dropped.

Ref: https://docs.mulesoft.com/mule-runtime/4.1/mule-4-changes

The recommendation is to use Drools as a standalone installation and expose the rules execution interface as REST APIs to integrate with the Mule flow, however, that requires a separate infrastructure and installation efforts. As an alternative, we can implement a custom Java code to execute the embedded Drools rule definitions and call it from our Mule flow. The implementation can be done as described in the diagram:

Image title

Please note: to update the rules definition, the Mule application needs to be redeployed. Here's an example of the Java implementation in the Mule 4 flow:

Mule Runtime - 4.1.4

Anypoint Studio - 7.3.0

Purpose: To implement the below rules in Drools to assign a Job to a particular supplier:

Rule 1: If Job is of type "Food Supply" and status is "New" then assign to "Farmer's Food Supply Pvt Ltd." and set status to "Assigned"

Rule 2: If Job is of type "Water Supply" and status is "New" then assign to "Ocean Water  Supply Pvt Ltd." and set status to "Assigned"

Step 1: Create a new Mule 4 project in Anypoint Studio, name it drools-example

Step 2: Create the Java class representing the POJO for Job to be assigned.

Create package com.drools.example in folder: drools-example\src\main\java

Create Job.java POJO inside the package: com.drools.example

package com.drools.example;

public class Job {

String jobName;
String jobType;
String jobLocation;
String jobDateTime;
long jobCost;
String jobOwner;
String jobStatus;

public String getJobStatus() {
return jobStatus;
}
public void setJobStatus(String jobStatus) {
this.jobStatus = jobStatus;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
public String getJobType() {
return jobType;
}
public void setJobType(String jobType) {
this.jobType = jobType;
}
public String getJobLocation() {
return jobLocation;
}
public void setJobLocation(String jobLocation) {
this.jobLocation = jobLocation;
}
public String getJobDateTime() {
return jobDateTime;
}
public void setJobDateTime(String jobDateTime) {
this.jobDateTime = jobDateTime;
}
public long getJobCost() {
return jobCost;
}
public void setJobCost(long jobCost) {
this.jobCost = jobCost;
}
public String getJobOwner() {
return jobOwner;
}
public void setJobOwner(String jobOwner) {
this.jobOwner = jobOwner;
}
}

Step 3: Create the drools rule definition and add to the project

Create a new file job.drl in folder: drools-example\src\main\resources\rules\job

package rules.job

import com.drools.example.Job;

dialect "mvel"

rule "Food Supply Rule"
when
j : Job( jobType.equals("Food Supply") , jobStatus.equals("New"), jn : jobName )
then
j.setJobOwner( "Farmer's Food Supply Pvt Ltd." );
j.setJobStatus( "Assigned" );
update( j );
end

rule "Water Supply Rule"
when
j : Job( jobType.equals("Water Supply") , jobStatus.equals("New"), jn : jobName )
then
j.setJobOwner( "Ocean Water Supply Pvt Ltd." );
j.setJobStatus( "Assigned" );
update( j );
end

Additionally, create a file called kmodule.xml inside drools-example\src\main\resources\META-INF (create the META-INF folder inside resources). The kmodule.xml file is the descriptor that selects resources to knowledge bases and configures those knowledge bases and sessions.

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

Step 4: We need to edit the POM.xml file generated for the Mule project to include the dependencies for Drools; we are going to use Drools version 7.0.0.Final

Add properties in the <properties> block:

<drools.version>7.0.0.Final</drools.version>

Add dependencies in the <dependencies> block:

    <!-- Drools Dependencies -->
    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-workbench-models-guided-dtable</artifactId>
        <version>${drools.version}</version>
    </dependency>
    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-compiler</artifactId>
        <version>${drools.version}</version>
    </dependency>
    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-core</artifactId>
        <version>${drools.version}</version>
    </dependency>
    <dependency>
        <groupId>org.kie</groupId>
        <artifactId>kie-api</artifactId>
        <version>${drools.version}</version>
    </dependency>
    <dependency>
        <groupId>org.kie</groupId>
        <artifactId>kie-ci</artifactId>
        <version>${drools.version}</version>
    </dependency>

Add Jboss Repository in <repositories> block:

<repository>
    <id>jboss-public-repository-group</id>
    <name>JBoss Public Repository Group</name>
    <url>http://repository.jboss.org/nexus/content/groups/public/</url>
    <releases>
        <enabled>true</enabled>
        <updatePolicy>never</updatePolicy>
    </releases>
    <snapshots>
        <enabled>true</enabled>
        <updatePolicy>daily</updatePolicy>
    </snapshots>
</repository>

Step 5: Now we are ready to implement the Rules engine, which will create KIESession to execute the drools rule definition.

For that, create the RulesEngine.java in the package com.drools.example

package com.drools.example;

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

import com.etech.drools.knowledge.Job;

public class RulesEngine {

public static void executeJobRules(Job inputJob)
{
try {
// load up the knowledge base
KieServices ks = KieServices.Factory.get();
KieContainer kContainer = ks.getKieClasspathContainer();
KieSession kSession = kContainer.newKieSession("ksession-job-rules");

kSession.insert(inputJob);
kSession.fireAllRules();
kSession.dispose();

} catch (Throwable t) {
t.printStackTrace();
}

}
}

Step 6: In this step, we will invoke the static method in RulesEngine class from the Mule flow and pass the Job POJO instance as argument to it. The flow takes JSON input payload and transforms it into a Job POJO instance and assigns it to a flow variable. Then, the RuleEngine is called, which updates the flow variable accordingly with the result Finally, the flow variable is transformed back to JSON and returned as output.

<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns:ee="http://www.mulesoft.org/schema/mule/ee/core" xmlns:java="http://www.mulesoft.org/schema/mule/java" xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd http://www.mulesoft.org/schema/mule/ee/core http://www.mulesoft.org/schema/mule/ee/core/current/mule-ee.xsd http://www.mulesoft.org/schema/mule/java http://www.mulesoft.org/schema/mule/java/current/mule-java.xsd">
    <http:listener-config name="HTTP_Listener_config" doc:name="HTTP Listener config" doc:id="bd540e0b-0f60-44fe-9d33-c176a5827250">
        <http:listener-connection host="0.0.0.0" port="8081" />
    </http:listener-config>
    <flow name="drools-exampleFlow" doc:id="d544ce83-d6d6-41d5-a0d9-cfb6af089540">
        <http:listener doc:name="Listener" doc:id="0d88ede3-b2c4-4fe9-8a94-1f3cb1e160d0" config-ref="HTTP_Listener_config" path="/drools-example" allowedMethods="POST" outputMimeType="application/json" />
        <ee:transform doc:name="Transform Message" doc:id="81815fdf-2f64-4015-85ca-422b86309eda">
            <ee:message>
                <ee:set-payload><![CDATA[%dw 2.0
output application/java
---
{
jobCost: payload.jobCost as Number,
jobDateTime: payload.jobDateTime,
jobLocation: payload.jobLocation,
jobName: payload.jobName,
jobOwner: payload.jobOwner,
jobStatus: payload.jobStatus,
jobType: payload.jobType
} as Object {
class : "com.drools.example.Job"
}]]></ee:set-payload>
            </ee:message>
        </ee:transform>
        <set-variable value="#[payload as Object {  class : &quot;com.drools.example.Job&quot; }]" doc:name="Set Variable" doc:id="88cce3e3-3373-4c8f-ace3-280467feb78b" variableName="flowJob" mimeType="application/java" />
        <java:invoke-static doc:name="Invoke static" doc:id="b7ddcbd9-8d9a-43fb-aa72-bf0205dc1f65" class="com.drools.example.RulesEngine" method="executeJobRules(Job)">
            <java:args><![CDATA[#[{
arg0 : vars.flowJob
}]]]></java:args>
        </java:invoke-static>
        <ee:transform doc:name="Transform Message" doc:id="e271d4aa-4f8b-4ab1-a65a-0e07060c7892">
            <ee:message>
                <ee:set-payload><![CDATA[%dw 2.0
output application/json
---
{
jobName: vars.flowJob.jobName as String,
jobType: vars.flowJob.jobType as String,
jobLocation: vars.flowJob.jobLocation as String,
jobDateTime: vars.flowJob.jobDateTime as String,
jobCost: vars.flowJob.jobCost as String,
jobOwner: vars.flowJob.jobOwner as String,
jobStatus: vars.flowJob.jobStatus as String
}]]></ee:set-payload>
            </ee:message>
        </ee:transform>
    </flow>
</mule>

Note the invoke static method component of the Java module; it takes arguments as argx (arg0, arg1 etc.) 

Mule Flow

Image title

Testing:

URL: http://localhost:8081/drools-example

Input:

{
"jobName": "My Test Job",
"jobType": "Food Supply",
"jobLocation": "Kolkata India",
"jobDateTime": "24-12-18 14:00:00 PM",
"jobCost": "100.00",
"jobOwner": "To Be Assigned",
"jobStatus": "New"
}

Output: 

{
"jobName": "My Test Job",
"jobType": "Food Supply",
"jobLocation": "Kolkata India",
"jobDateTime": "24-12-18 14:00:00 PM",
"jobCost": "100.00",
"jobOwner": "Farmer's Food Supply Pvt Ltd.",
"jobStatus": "Assigned"
}

The Owner and Status of the Job have been updated as per the Drools rule definition. 

Conclusion

Drools is a collection of tools that allows us to separate and reason over logic and data found within business processes. The two important keywords we need to notice are Logic and Data. The logic can be implemented in the Drools rules definitions while the data can be passed into the rules from the Mule Flow.

To know more about Drools, please go through the Drools documentation here.

Please share your comments or feedback. Cheers!

Download A Buyer's Guide to Application and Data Integration, your one-stop-shop for research, checklists, and explanations for an application and data integration solution.

Topics:
mule 4 ,drools ,integration ,tutorial ,integration tutorial ,mule tutorial

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}