Learn Drools (Part 5): Truth Maintenance
Drools handles truth tables via its Truth Maintenance System. As Drools facts change, the system logically inserts the new data, automatically keeping it updated.
Join the DZone community and get the full member experience.
Join For FreeIn our previous article, we saw how Inferences are useful when we need to repetitively use a condition across our Drools rules. With an Inference, we insert the condition as a POJO and use that fact across the rules. It's a perfect way to separate the condition from its usage.
But think about a condition where two rules are mutually exclusive. In that scenario, it is far better to use Truth maintenance, which is nothing but a truth table. Let's walk through it with an example.
Suppose, for our intrepid IT department, that if someone is designated as a Manager, we give them laptop. But if an employee is not a Manager, we give them a desktop. So in Drools, we have to create two rules:
The first rule checks to see if someone's designation is "Manager", then gives them a laptop if they are.
The second rule checks to see if the designation is not a manager, then gives them a desktop if they aren't one.
These two rules are mutually exclusive. For an employee/Drools fact, only one rule can be true — not both. So, we can maintain a truth table for that. In Drools, we call that the Drools Truth Maintenance System (TMS). Through the TMS, we can logically insert the condition — note that it's not just inserted, but logically inserted. The advantage of logical insertion is that when the condition is false, the fact/POJO is automatically retracted.
To understand the advantage of the TMS, think of a scenario where an employee's current designation is not a manager. So, according to the rule, they got a desktop, and we logically insert the "Not a Manager" condition in the Drools knowledge base for that employee or fact. But say that employee just got a promotion! After some hard work, the employee has been recognized as a leader and is now managing other workers. Now, that fact/condition that we inserted into the Drools knowledge base for that employee has to be retracted, and a new fact/condition ("is Manager") has to be inserted.
Of course, that makes the employee eligible for a laptop. Using the TMS, all that will be done automatically. We don’t have to care about it. You can find the solution to the above problem statement in the code blocks below.
Employee.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
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;
}
}
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 org.kie.api.runtime.process.ProcessInstance;
import org.kie.api.runtime.rule.FactHandle;
import com.example.drools.listener.Activation;
import com.example.drools.listener.TrackingAgendaEventListener;
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();
//String ruleFile = "/com/rules/employee.drl";
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();
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();
}
Output
Shamik Mitra: IT:Give Laptop
Swastika Mitra: IT:Give Desktop
Published at DZone with permission of Shamik Mitra, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Comments