Drools is a popular open source business rules and work flow engine. It helps you externalize the rules as opposed to embedding within the Java code. This tutorial contains a a number of artifacts highlighted below. |
Step 1: Create a new Maven project with the standard src/main/java, src/main/resources, src/test/java, etc source folder directories. Create a new src/main/rules folder to define the rules using the "mvel" expresion language.
Update the pom.xml file as shown below with relevant dependencies and also add the build/resources to tell maven that sr/main/rules is a source folder.
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.myapp</groupId>
<artifactId>drools-poc</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<properties>
<drools.version>5.3.1.Final</drools.version>
<junit.version>4.7</junit.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
</dependency>
<!-- drools library -->
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</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>
<!-- required for drools and spring integration -->
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-spring</artifactId>
<version>${drools.version}</version>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.2.2</version>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/rules</directory>
</resource>
</resources>
</build>
</project>
Step 2: Define the domain classes like Applicant.java.
Step 3: Define the data access interface and implementation classes that retrieves data from a persistent source like file or database. The implementation is simplified to read from a pre-populated in-memory map.
package com.company.license;
public class Applicant {
private Integer id;
private String name;
private int age;
public Applicant(Integer id, String name, int age) {
super();
this.id = id;
this.name = name;
this.age = age;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "name=" + name + ", age=" + age;
}
}
The implementation of the above interface.
package com.company.license;
public interface ApplicantDao {
Applicant findApplicant(Integer applicantId);
}
package com.company.license;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class ApplicantDaoImpl implements ApplicantDao {
private static final Map<Integer, Applicant> data = new ConcurrentHashMap<Integer, Applicant>();
static {
data.put(1, new Applicant(1, "John", 16));
data.put(2, new Applicant(2, "Peter", 20));
}
public Applicant findApplicant(Integer applicantId) {
Applicant applicant = data.get(applicantId);
System.out.println("fetched applicant: " + applicant);
return applicant;
}
}
Step 4: Define the value object class that captures the user entry via a GUI. For example, ApplicantForm.java as shown below.
package com.company.license;Step 5: Define the business rules via mvel expression language in a separate external file -- applicant-license.drl.
public class ApplicantForm {
private Integer applicantId;
private boolean eligible = true; // eligible by default
public Integer getApplicantId() {
return applicantId;
}
public void setApplicantId(Integer applicantId) {
this.applicantId = applicantId;
}
public boolean isEligible() {
return eligible;
}
public void setEligible(boolean eligible) {
this.eligible = eligible;
}
@Override
public String toString() {
return "applicantId=" + applicantId + ", eligible=" + eligible;
}
}
package com.company.license;
import com.company.license.ApplicantForm;
import com.company.license.Applicant;
import com.company.license.ApplicantDao;
import com.company.license.ApplicantDaoImpl;
global ApplicantDao applicantDao;
rule "is eligible for licence"
when
$a : ApplicantForm();
$applicant: Applicant( age < 18 ) from applicantDao.findApplicant($a.getApplicantId());
then
$a.setEligible( false );
end
Step 6: Finally, the junit test class that makes use of drools to validate applicants. We will create 2 applicants, one with age >= 18, and another one with age < 18, to test the business rules engine (aka expert systems) of drools in action.
package com.company.license;
import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderError;
import org.drools.builder.KnowledgeBuilderErrors;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.io.ResourceFactory;
import org.drools.logger.KnowledgeRuntimeLogger;
import org.drools.logger.KnowledgeRuntimeLoggerFactory;
import org.drools.runtime.StatefulKnowledgeSession;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
public class ApplicantLicenseTest {
private KnowledgeBase kbase;
@Before
public void setup() {
KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
kbuilder.add(ResourceFactory.newClassPathResource("com/company/license/applicant-license.drl"),
ResourceType.DRL);
KnowledgeBuilderErrors errors = kbuilder.getErrors();
if (errors.size() > 0) {
for (KnowledgeBuilderError error : errors) {
System.err.println(error);
}
throw new IllegalArgumentException("Could not parse knowledge.");
}
kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
}
@Test
public void testApplicantLicense() {
StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
KnowledgeRuntimeLogger logger = KnowledgeRuntimeLoggerFactory.newFileLogger(ksession, "test");
KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
kbase.newStatefulKnowledgeSession();
ApplicantForm applicant1 = new ApplicantForm();
applicant1.setApplicantId(1);
ApplicantForm applicant2 = new ApplicantForm();
applicant2.setApplicantId(2);
ksession.setGlobal("applicantDao", new ApplicantDaoImpl()); // assign the global dao
ksession.insert(applicant1);
ksession.insert(applicant2);
ksession.fireAllRules();
ksession.dispose();
Assert.assertTrue(applicant1.isEligible() == false);//John is not eligible
Assert.assertTrue(applicant2.isEligible() == true); //Peter is eligible
}
}