Introduction
Business rules are constraints or conditions that define aspects of a business, they define how an organization or business operates; they include requirements, policies and conditional statements. Using Rules in an application provides an abstraction over changeable business logic; changing rules is faster than making changes to source codes in order to change application behaviours.
A business rule might state that the minimum age of an individual that would fill an online form should be 18, while another might be that the minimum balance in a bank account before withdrawal can occur is 100 Naira. Business rules are usually mined from use cases, systems specifications or user requirements
A business rules management system (BRMS) is a software that is used to define, execute and maintain business rules of an organization. At the heart of a BRMS is a business rules engine which executes business rules in a production environment. There are several BRMS in java which are matured and have been around for years, but Drools is widely used and most popular.
Drools BRMS
Drools is a BRMS tool written in Java and can be used in an Enterprise Java Application to manage business rules. It allows users to write and validate business rules. Drools provides a core Business Rules Engine (BRE), a web authoring and rules management application (Drools Workbench) and an Eclipse IDE plugin for core development.
Starting from version 6.0 drools implementation is based on PHREAK Algorithm which is a pattern matching algorithm for implementing production rules as opposed to an enhanced RETE algorithm that was used in the earlier versions. At the time of writing of this post, drools is currently in version 6.2 and can be downloaded
here, the codes used in this post is compatible with version 6.1 and later.
Rules in Drools can be organized or saved in files, in this case a .drl file or in database as well as having decision tables configured in excel files. Starting from version 6.0 drools introduced the KIE api which groups all the related projects together.
Drools introduces the concept of Stateless knowledge session which can be called like a function by passing data to it and getting response and it can be used for validation, calculation and route filtering. And Stateful knowledge session which allows iterative changes over time and can be used for monitoring, diagnostics, logistics and compliances.
Getting Started
I believe in explaining concepts using codes, lets start with an example that implements a business rule which states that the minimum age of an applicant should be 40.
The following libraries are to be added to the project classpath to be able to use drools.
- kie-api-6.1.0.Final.jar
- drools-compiler-6.1.0.Final.jar
- drools-core-6.1.0.Final.jar
- drools-decisiontables-6.1.0.Final.jar
- kie-internal-6.1.0.Final.jar
- slf4j-api-1.7.7.jar
- slf4j-log4j12-1.7.7.jar
- log4j-1.2.17.jar
- xstream-1.4.7.jar
- mvel2-2.2.1.Final.jar
- protobuf-java-2.5.0.jar
- antlr-runtime-3.5.jar
An Applicant POJO should be defined to hold the data to be processed.
package com.poc.drools;
public class Applicant {
private String name;
private int age;
private boolean valid;
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;
}
public boolean isValid() {
return valid;
}
public void setValid(boolean valid) {
this.valid = valid;
}
}
We can now define the rule to be processed by the rule eninge in a drools file and save it as ageRule.drl, this is a text file with .drl extension.
package com.poc.drools
import com.poc.drools.Applicant;
rule "Is applicant age valid"
dialect 'mvel'
when
$applicant : Applicant( age < 40 )
then
$applicant.setValid(false);
end
Package and
import kyewords work just like in a regular java class file, a rule name must follow the
rule keyword while mvel is the rule language dialect to use. The $applicant is the binding variable that gives access to object containing the data that would be inserted into the rule engine. Drools rules normally consist of the condition part which would be implemented inside
when block and the action to be performed if the condition is met which would be put inside
then block.
Once the rule has been defined in the drool file, a stateless or stateful session needs to be created using the Kie api and the drools file(s) passed to it for compilation along with the data to be processed before the rules would be fired. A stateful session is used in the codes below.
package com.poc.drools;
import java.io.File;
import org.drools.core.common.DefaultFactHandle;
import org.kie.api.KieBase;
import org.kie.api.KieBaseConfiguration;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.ReleaseId;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.KieSessionConfiguration;
import org.kie.api.runtime.rule.FactHandle;
import org.kie.internal.io.ResourceFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class RuleProcessor {
Logger logger=LoggerFactory.getLogger(RuleProcessor.class);
public Boolean isApplicantEligible(Applicant applicant) {
// String array was used because the rules can be splited into two or more files
String [] paths= new String[1];
paths[0]="C:\\Users\\AYOBAMI\\Documents\\workplace\\DroolsPOC\\src\\rules\\ageRule.drl";
KieServices kieServices = KieServices.Factory.get();
// Adds the drools file to the KieFileSystem for necessary compilation to occur
KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
for (String path : paths) {
File file= new File(path);
kieFileSystem.write(ResourceFactory.newFileResource(file));
}
// Create the builder for the resources of the File System
KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
kieBuilder.buildAll();
// Get the Release ID
ReleaseId relaseId = kieBuilder.getKieModule().getReleaseId();
// Create the Container, wrapping the KieModule with the given ReleaseId
KieContainer kiecontainer =kieServices.newKieContainer(relaseId);
// Configure and create a KieContainer that reads the drools files
KieBaseConfiguration kieBaseConfiguration = kieServices.newKieBaseConfiguration();
KieBase kieBase = kiecontainer.newKieBase(kieBaseConfiguration);
// Configure and create the KieSession
KieSessionConfiguration kieSessionConfiguration = kieServices.newKieSessionConfiguration();
KieSession kieSession= kieBase.newKieSession(kieSessionConfiguration, null);
//FactHandle holds reference to the inserted object
FactHandle applicantFactHandle= kieSession.insert(applicant);
//execute rule
kieSession.fireAllRules();
//get the processed data out of the rule engine
applicant= (Applicant) ((DefaultFactHandle)applicantFactHandle).getObject();
//release and free memory
kieSession.dispose();
return applicant.isValid();
}
}
Below is the Junit testcase to test the RuleProcessor class.
package com.poc.drools.test;
import static org.junit.Assert.*;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.poc.drools.Applicant;
import com.poc.drools.RuleProcessor;
public class RuleProcessorTest {
private RuleProcessor ruleProcessor;
@Before
public void setUp() throws Exception {
ruleProcessor= new RuleProcessor();
}
@After
public void tearDown() throws Exception {
ruleProcessor=null;
}
@Test
public void testIsApplicantEligible() {
Applicant applicant = new Applicant();
applicant.setAge(16);
applicant.setName("Ayobami Adewole");
assertTrue(ruleProcessor.isApplicantEligible(applicant));
}
}
Once the rules have been fired, the rule engine will execute the rules and process the data, then appropriate action can then be taken on the processed data depending on the application logic.
The full source codes can be downloaded
here. Subsequent blog posts would feature complex rules scenarios and their implementations in drools.