Saturday, November 26, 2016

Learn Drools: Part VI: Rules and Statistics

Learn Drools: Part VI: Rules and Statistics

In this Article, we will learn How to find few important statistics for backtracking. Consider a Complex System where Many rules are presents and they fired on a huge number of facts , and it is a common scenario where a fact is matched with many rules condition. So How can we get those statistics like?

  1. How many Rules are fired for a given facts?
  2. Which Rules are Activated?


Say for a live system, a bug has been raised for particular facts say Employee whose designation is  Manager but not received Laptop?

How can we analyze this bug?
May we replicate the same scenario in Dev environment and debug the Drools Rules to check where it fails.

This is OK but time-consuming, Developer should work on better task than this. So if we maintain a statistics which rules are fired for each fact and show this is in UI. Then easily we search for the given facts and tell where it fails.


I will try to write Primer program How to track these statistics. Of Course, you can make enhance this program according to your need and fit into your module with advanced capabilities. But I want to show you how it could be done?

In Drools there are Two Listeners interfaces

WorkingMemoryEventListener
AgendaEventListener

Use of these two interfaces we can find different statistical information like,


When an Object/fact is created,updated,retracted by WorkingMemoryEventListener.
Before Rule activation after rule activation etc by AgendaEventListener.


Here we will use AgendaEventListener and track  which rules are activated or fired for a fact and print them.
Drools Use Skeletal Implementation so It has a class called DefaultAgendaEventListener which provides default implementation (left blank) of all methods define in AgendaEventListener interface.

So we will create our own Listener which extends DefaultAgendaEventListener and override the afterActivationFired method.

And register this listener in Working memory so whenever facts compared with Rules this can be triggered.

Step 1. Creating 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 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


Here we can see for Facts Shamik and Swastika which rules are fired.