Spring Framework Drools tutorial with Maven

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.

 

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;
}

}


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 interface ApplicantDao {
Applicant findApplicant(Integer applicantId);
}


The implementation of the above interface.

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;

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;
}
}

Step 5: Define the business rules via mvel expression language in a separate external file -- applicant-license.drl.

 

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
}

}