Drools With Mule 4
Implementing Drools requires two things: authoring and runtime.
Join the DZone community and get the full member experience.
Join For FreeDrools 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:
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 : "com.drools.example.Job" }]" 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.)
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!
Opinions expressed by DZone contributors are their own.
Comments