Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Learn Drools (Part 6): Rules and Statistics

DZone's Guide to

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.

· Java Zone
Free Resource

Bitbucket is for the code that takes us to Mars, decodes the human genome, or drives your next car. What will your code do? Get started with Bitbucket today, it's free.

In 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:

  1. How many rules are fired for given fact?

  2. 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.

Bitbucket is the Git solution for professional teams who code with a purpose, not just as a hobby. Get started today, it's free.

Topics:
drools ,rule engine ,java ,drools solver ,debugging

Published at DZone with permission of Shamik Mitra, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}