Learn Drools (Part 6): Rules and Statistics
So, you've got your Drools system running, but then you run into a bug. Fortunately, you can track your facts and rules with Java and Drools to see where it went wrong.
Join the DZone community and get the full member experience.
Join For FreeIn this article, we will learn how to find a few important statistics for backtracking. Consider a complex system where you have a lot of Drools rules present and firing on a huge number of facts. In that setup, you're going to see facts matched with a lot of rules. So:
How many rules are fired for given fact?
Which rules are activated?
Say for a live system that a bug has been raised for a few particular facts. Say you have an employee whose designation is Manager in the system but hasn't received a laptop, as per our original setup?
How can we analyze this bug?
We can replicate the same scenario in the dev environment and debug the Drools rules to check where it fails.
This is OK, but it's time-consuming. A developer should work on better stuff than this. So, if we maintain statistics on which rules are fired for each fact and show this is in a UI, then we can easily search for the given facts and see where it fails.
I will try to write a primer program to track these statistics. Of course, you can enhance this program according to your needs and fit it into your module with advanced capabilities. But I just want to show you how it could be done.
In Drools, there are two Listener
interfaces:
WorkingMemoryEventListener
AgendaEventListener
Using these two interfaces, we can find different statistical information like:
When an object/fact is created, updated, or retracted by WorkingMemoryEventListener.
Before or after rule activation by AgendaEventListener.
Here, we will use AgendaEventListener and track which rules are activated or fired for a fact — then print them.
Drools uses skeletal implementation, so it has a class called DefaultAgendaEventListener, which provides the default implementation (left blank) of all methods defined in the AgendaEventListener interface.
So we will create our own Listener, which extends DefaultAgendaEventListener and overrides the afterActivationFired method.
Then, we'll register this listener in working memory so that whenever facts are compared with rules, this can be triggered.
Step 1: Creating the Listener
package com.example.drools.listener;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.drools.core.WorkingMemory;
import org.drools.core.event.AfterActivationFiredEvent;
import org.drools.core.event.DefaultAgendaEventListener;
import org.drools.core.rule.Rule;
public class TrackingRuleFiredEventListener extends DefaultAgendaEventListener{
private List<String> activationList = new ArrayList<String>();
@Override
public void afterActivationFired(AfterActivationFiredEvent event,final WorkingMemory workingMemory) {
Rule rule = event.getActivation().getRule();
String ruleName = rule.getName();
activationList.add(ruleName);
System.out.println("Rule fired: " + ruleName);
}
public boolean isRuleFired(String ruleName) {
for (String rule : activationList) {
if (ruleName.equalsIgnoreCase(rule)) {
return true;
}
}
return false;
}
public void reset() {
activationList.clear();
}
public final List<String> getActivationList() {
return activationList;
}
}
Step 2: The Drools File (employeeTruthTable.drl)
package com.rules
import com.example.droolsExample.pojo.Employee
import com.example.droolsExample.pojo.Department
import com.example.droolsExample.pojo.ITManager
import com.example.droolsExample.pojo.ITEmployee
rule "IT Manager Inference1"
when
$dept: Department(name=="IT");
$emp: Employee(dept == $dept,manager==true);
then
insertLogical(new ITManager($emp));
end
rule "IT Employee Inference1"
when
$dept: Department(name=="IT");
$emp: Employee(dept == $dept,manager==false);
then
insertLogical(new ITEmployee($emp));
end
rule "give Manager Laptop"
when
$emp: Employee();
$itManager: ITManager(emp == $emp);
then
$emp.setMessage("Give Laptop");
System.out.println($emp.getName()+ ": "+$emp.getDept().getName()+ ":"+$emp.getMessage());
end
rule "give Employee Desktop"
when
$emp: Employee();
$itEmployee: ITEmployee(emp == $emp);
then
$emp.setMessage("Give Desktop");
System.out.println($emp.getName()+ ": "+$emp.getDept().getName()+ ":"+$emp.getMessage());
end
Step 3: Create Domain Objects
Employee.java
package com.example.droolsExample.pojo;
public class Employee {
String name;
boolean manager;
String message;
Department dept;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isManager() {
return manager;
}
public void setManager(boolean manager) {
this.manager = manager;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Department getDept() {
return dept;
}
public void setDept(Department dept) {
this.dept = dept;
}
@Override
public String toString() {
return "Employee [name=" + name + ", manager=" + manager + ", message="
+ message + ", dept=" + dept + "]";
}
}
Department.java
package com.example.droolsExample.pojo;
public class Department {
String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
ITManager.java
package com.example.droolsExample.pojo;
public class ITManager {
private Employee emp;
public ITManager(Employee emp)
{
this.emp=emp;
}
public Employee getEmp() {
return emp;
}
public void setEmp(Employee emp) {
this.emp = emp;
}
}
ITEmployee.java
package com.example.droolsExample.pojo;
public class ITEmployee {
private Employee emp;
public ITEmployee(Employee emp)
{
this.emp=emp;
}
public Employee getEmp() {
return emp;
}
public void setEmp(Employee emp) {
this.emp = emp;
}
}
Step 4: Registering the Listener
DroolTest.java
package com.example.droolsExample;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.List;
import org.drools.compiler.compiler.DroolsParserException;
import org.drools.compiler.compiler.PackageBuilder;
import org.drools.core.RuleBase;
import org.drools.core.RuleBaseFactory;
import org.drools.core.WorkingMemory;
import org.drools.core.event.AgendaEventListener;
import com.example.drools.listener.TrackingRuleFiredEventListener;
import com.example.droolsExample.pojo.Department;
import com.example.droolsExample.pojo.Employee;
public class DroolsTest {
public static void main(String[] args) throws DroolsParserException,
IOException {
DroolsTest droolsTest = new DroolsTest();
//droolsTest.executeDrools();
droolsTest.executeDroolsEmployee("/com/rules/employeeTruthTable.drl");
}
public void executeDroolsEmployee(String ruleFile) throws DroolsParserException, IOException {
PackageBuilder packageBuilder = new PackageBuilder();
InputStream resourceAsStream = getClass().getResourceAsStream(ruleFile);
Reader reader = new InputStreamReader(resourceAsStream);
packageBuilder.addPackageFromDrl(reader);
org.drools.core.rule.Package rulesPackage = packageBuilder.getPackage();
RuleBase ruleBase = RuleBaseFactory.newRuleBase();
ruleBase.addPackage(rulesPackage);
WorkingMemory workingMemory = ruleBase.newStatefulSession();
AgendaEventListener employeeRuleTracktListener = new TrackingRuleFiredEventListener();
workingMemory.addEventListener(employeeRuleTracktListener);
Department dep = new Department();
dep.setName("Civil");
Department dep1 = new Department();
dep1.setName("IT");
Employee emp = new Employee();
emp.setName("Shamik Mitra");
emp.setManager(true);
emp.setDept(dep1);
Employee emp2 = new Employee();
emp2.setName("Swastika Mitra");
emp2.setManager(false);
emp2.setDept(dep1);
Employee emp1 = new Employee();
emp1.setName("Samir Mitra");
emp1.setManager(true);
emp1.setDept(dep);
workingMemory.insert(dep);
workingMemory.insert(dep1);
workingMemory.insert(emp);
workingMemory.insert(emp1);
workingMemory.insert(emp2);
workingMemory.fireAllRules();
System.out.println("===================================");
System.out.println("No of Activated Rule for all facts");
System.out.println("===================================");
List<String> activations = ((TrackingRuleFiredEventListener)employeeRuleTracktListener).getActivationList();
for(String rule : activations)
{
System.out.println(rule);
}
}
}
Step 5: Output
Rule fired: IT Manager Inference1
Rule fired: IT Employee Inference1
Shamik Mitra: IT:Give Laptop
Rule fired: give Manager Laptop
Swastika Mitra: IT:Give Desktop
Rule fired: give Employee Desktop
===================================
No of Activated Rule for all facts
===================================
IT Manager Inference1
IT Employee Inference1
give Manager Laptop
give Employee Desktop
And here's your data of the rules that were fired and the facts involved.
Published at DZone with permission of Shamik Mitra, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments