JBoss Drools Complex example

Hello All, this example give a clear idea about the jboss drools concept in writing the rules configuration files.
Advantages of a Rule Engine

  • Declarative Programming
  • Rule engines allow you to say “What to do”, not “How to do it”.

Logic and Data Separation

Your data is in your domain objects, the logic is in the rules. This is fundamentally breaking the OO coupling of data and logic, which can be an advantage or a disadvantage depending on your point of view also this approach gives Speed and Scalability, Centralization of Knowledge, Strong and Loose Coupling

Following example applies discount on product purchased based on customer from particular city.
discount_drools

following is the rule file with ext ‘.drl’

//created on: 22 Sep, 2013
package com.spark.discount.rules

//list any import classes here.
import com.spark.jbpm.drools.product.Purchase
import com.spark.jbpm.drools.person.Customer
import com.spark.jbpm.drools.product.Product

//declare any global variables here
function void displayDetails(String name,double discount) {
    System.out.println( "name: "+name+", discount: "+discount); 
}

rule "discount rule 1"
    when 
    	$customer : Customer( city == "hyderabad")
    	$purchase : Purchase(customer == $customer,qty >= 10)
    then
		$customer.setDiscount((($purchase.getQty()*$purchase.getProduct().getUnitPrice())*10)/100);
		displayDetails($customer.getName(),$customer.getDiscount());
end

rule "discount rule 2"
    when 
    	$customer : Customer( city == "bangalure")
    	$purchase : Purchase(customer == $customer,qty >= 10)
    then
		$customer.setDiscount((($purchase.getQty()*$purchase.getProduct().getUnitPrice())*25)/100);
		displayDetails($customer.getName(),$customer.getDiscount());
end

rule "discount rule 3"
    when 
    	$customer : Customer( city == "mumbai")
    	$purchase : Purchase(customer == $customer,qty >= 10)
    then
		$customer.setDiscount(($purchase.getQty()*$purchase.getProduct().getUnitPrice())*30/100);
		displayDetails($customer.getName(),$customer.getDiscount());
end

rule "discount rule 4"
    when 
    	$customer : Customer( city == "delhi")
    	$purchase : Purchase(customer == $customer,qty >= 10)
    then
		$customer.setDiscount(($purchase.getQty()*$purchase.getProduct().getUnitPrice())*40/100);
		displayDetails($customer.getName(),$customer.getDiscount());
end

rule "discount rule 5"
    when 
    	$purchase : Purchase(qty < 10 )
    then
    	System.out.println("qunatity less than 10 no discount");
end

lets code the domain objects as follows Customer.java,Product.java,Purchase.java

package com.spark.jbpm.drools.person;

public class Customer {
	private String name;
	private double discount;
	private String city;
	
	public Customer(String name, int discount) {
		super();
		this.name = name;
		this.discount = discount;
	}
	
	public Customer() {
		super();
	}

	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public double getDiscount() {
		return discount;
	}
	public void setDiscount(double discount) {
		this.discount = discount;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}

	@Override
	public boolean equals(Object obj) {
		if(!(obj instanceof Customer)) return false;
		else 
			return this.name.equalsIgnoreCase(((Customer)obj).name);
	}

	@Override
	public int hashCode() {
		return 36*name.hashCode();
	}

	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return "Customer : " + name;
	}	
}
/**
 * 
 */
package com.spark.jbpm.drools.product;

/**
 * @author Sony
 * 
 */
public class Product {

	private String productCode;
	private String productName;
	private int unitPrice;
	private String name;
	private float price;

	public Product() {
		// TODO Auto-generated constructor stub
	}

	public Product(String name, float price) {
		super();
		this.name = name;
		this.price = price;
	}

	/**
	 * @return the productCode
	 */
	public String getProductCode() {
		return productCode;
	}

	/**
	 * @param productCode
	 *            the productCode to set
	 */
	public void setProductCode(String productCode) {
		this.productCode = productCode;
	}

	/**
	 * @return the productName
	 */
	public String getProductName() {
		return productName;
	}

	/**
	 * @param productName
	 *            the productName to set
	 */
	public void setProductName(String productName) {
		this.productName = productName;
	}

	/**
	 * @return the unitPrice
	 */
	public int getUnitPrice() {
		return unitPrice;
	}

	/**
	 * @param unitPrice
	 *            the unitPrice to set
	 */
	public void setUnitPrice(int unitPrice) {
		this.unitPrice = unitPrice;
	}

	/**
	 * @return the name
	 */
	public String getName() {
		return name;
	}

	/**
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * @return the price
	 */
	public float getPrice() {
		return price;
	}

	/**
	 * @param price
	 *            the price to set
	 */
	public void setPrice(float price) {
		this.price = price;
	}

	
	@Override
	public String toString() {
		return "Product [productCode=" + productCode + ", productName="
				+ productName + ", unitPrice=" + unitPrice + "]";
	}

	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((productCode == null) ? 0 : productCode.hashCode());
		result = prime * result
				+ ((productName == null) ? 0 : productName.hashCode());
		result = prime * result + unitPrice;
		return result;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (!(obj instanceof Product))
			return false;
		Product other = (Product) obj;
		if (productCode == null) {
			if (other.productCode != null)
				return false;
		} else if (!productCode.equals(other.productCode))
			return false;
		if (productName == null) {
			if (other.productName != null)
				return false;
		} else if (!productName.equals(other.productName))
			return false;
		if (unitPrice != other.unitPrice)
			return false;
		return true;
	}
}
package com.spark.jbpm.drools.product;

import com.spark.jbpm.drools.person.Customer;

public class Purchase {
	private Customer customer;
	private Product product;
	private int qty;
	
	public Purchase(Customer cus, Product prod) {
		this.customer = cus;
		this.product = prod;
	}
	
	
	public Purchase(Customer customer, Product product, int qty) {
		this.customer = customer;
		this.product = product;
		this.qty = qty;
	}


	public Customer getCustomer() {
		return customer;
	}
	public void setCustomer(Customer customer) {
		this.customer = customer;
	}
	public Product getProduct() {
		return product;
	}
	public void setProduct(Product product) {
		this.product = product;
	}
	
	public int getQty() {
		return qty;
	}
	public void setQty(int qty) {
		this.qty = qty;
	}	
}

following is the helper class for KnowledgeBase and StatefulKnowledgeSession


package com.spark.jbpm.drools.main;

import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderError;
import org.drools.builder.KnowledgeBuilderErrors;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.io.ResourceFactory;
import org.drools.runtime.StatefulKnowledgeSession;
import org.drools.runtime.StatelessKnowledgeSession;

/**
 * @author Sony
 * 
 */
public class KnowledgeBaseHelperUtil {

	/**
	 * @param fileName
	 * @return {@link KnowledgeBase}
	 * @throws Exception
	 */
	public static KnowledgeBase readKnowledgeBase(String fileName)
			throws Exception {
		KnowledgeBuilder knowledgeBuilder = KnowledgeBuilderFactory
				.newKnowledgeBuilder();
		knowledgeBuilder.add(ResourceFactory.newClassPathResource(fileName),
				ResourceType.DRL);
		KnowledgeBuilderErrors builderErrors = knowledgeBuilder.getErrors();
		if (builderErrors.size() > 0) {
			for (KnowledgeBuilderError knowledgeBuilderError : builderErrors) {
				System.err.println(knowledgeBuilderError);
			}
			throw new IllegalArgumentException("Cant parse KnowledgeBase");
		}

		KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase();
		knowledgeBase.addKnowledgePackages(knowledgeBuilder
				.getKnowledgePackages());
		return knowledgeBase;
	}

	/**
	 * @param fileName
	 * @return {@link StatefulKnowledgeSession}
	 * @throws Exception
	 */
	public static StatefulKnowledgeSession getStatefulKnowledgeSession(
			String fileName) throws Exception {
		KnowledgeBase kbase = readKnowledgeBase(fileName);
		return kbase.newStatefulKnowledgeSession();
	}

	/**
	 * @param fileName
	 * @return {@link StatelessKnowledgeSession}
	 * @throws Exception
	 */
	public static StatelessKnowledgeSession getStatelessKnowledgeSession(
			String fileName) throws Exception {
		KnowledgeBase kbase = readKnowledgeBase(fileName);
		return kbase.newStatelessKnowledgeSession();
	}
}

Finally here is the main class to fire all the rules defined


package com.spark.jbpm.drools.main;

import org.drools.runtime.StatefulKnowledgeSession;

import com.spark.jbpm.drools.person.Customer;
import com.spark.jbpm.drools.product.Product;
import com.spark.jbpm.drools.product.Purchase;

/**
 * @author Sony
 * 
 */
public class MainClass {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		try {
			StatefulKnowledgeSession knowledgeSession = KnowledgeBaseHelperUtil
					.getStatefulKnowledgeSession("discountRules.drl");
			Customer customer = new Customer();
			customer.setName("PavanKumar Mantha");
			customer.setCity("mumbai");
			knowledgeSession.insert(customer);
			
			Product product = new Product();
			product.setUnitPrice(35000);
			knowledgeSession.insert(product);
			
			Purchase purchase = new Purchase(customer, product);
			purchase.setQty(5);
			knowledgeSession.insert(purchase);
			
			knowledgeSession.fireAllRules();
			knowledgeSession.dispose();
		} catch (Exception e) {
			e.printStackTrace();
		}

	}

}

Happy Coding , Happy Drools 🙂

Advertisements

Configure jBPM5.4 with Drools

Hello guys as part of jBPM we should even stress a little on drools
Business processes: Represent what the business does.
Business rules: Represent decisions that the business does.

So, although processes and rules are two different thing, there is a clear advantage if your end users are allowed to combine processes and rules. This means for example that:

rules can define which processes to invoke,
rules can specify decisions in that process
rules can augment (or even override) the behaviour specified in the process (for example to handle exceptional cases)
assignment rules can be used to assign actors to (human) tasks
rules can be used to dynamically alter the behaviour of your process
so by delegating the decision to drools we can improve the business process execution.

Step1:
create a sample jBPM process.

bpmnProjectExplorer

HelloWorld.bpmn

now lets integrate drools code to make our process little resilient for that we need to create a drools rule file as below.

newrule

Now lets create two pojo classes as below.

Account.java

/**
 * 
 */
package com.spark.model;

/**
 * @author Sony
 * 
 */
public class Account {

	private long money;
	private String name;

	public Account() {

	}

	public long getMoney() {
		return money;
	}

	public void setMoney(long money) {
		this.money = money;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

}

RiskyAccounts.java

/**
 * 
 */
package com.spark.model;

import java.util.ArrayList;

/**
 * @author Sony
 *
 */
public class RiskyAccounts {

	private ArrayList<Account> accounts = new ArrayList<Account>();
	
	public void addAccount(Account account){
		accounts.add(account);
	}
	
	public void displayRiskyAccounts(){
		for(Account account : accounts){
			System.out.println("AccountName: "+account.getName() +", Money: "+account.getMoney());
		}
	}
}

now its time to code the drools code as follows.simplerule.drl

//created on: 15 Aug, 2013
package com.sample

//list any import classes here.
import com.spark.model.Account;
import com.spark.model.RiskyAccounts;

//declare any global variables here

global RiskyAccounts riskyAccounts;


rule "balanceRequiredInAccount"

    when
        $accountObject : Account(money <= 0)
    then
    	System.out.println("Not enough money in the account !");
   		riskyAccounts.addAccount($accountObject);
		
end

Now its time to trigger our process combined with rules(drools) that we coded as follows.

package com.sample;

import org.drools.KnowledgeBase;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.io.ResourceFactory;
import org.drools.runtime.StatefulKnowledgeSession;

import com.spark.model.Account;
import com.spark.model.RiskyAccounts;

/**
 * This is a sample file to launch a process.
 */
public class ProcessMain {

	public static final void main(String[] args) throws Exception {
		// load up the knowledge base
		KnowledgeBase kbase = readKnowledgeBase();
		StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
		
		// start a new process instance
		RiskyAccounts risky = new RiskyAccounts();
		ksession.setGlobal( "riskyAccounts", risky );
		ksession.startProcess("com.sample.bpmn.hello");
		
		Account account = new Account();
		account.setMoney(0);
		account.setName("Current Account");
		
		ksession.insert(account);
		ksession.fireAllRules();
		risky.displayRiskyAccounts();
	}

	private static KnowledgeBase readKnowledgeBase() throws Exception {
		KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
		kbuilder.add(ResourceFactory.newClassPathResource("simplerule.drl"), ResourceType.DRL);
		kbuilder.add(ResourceFactory.newClassPathResource("sample.bpmn"), ResourceType.BPMN2);
		if(kbuilder.hasErrors()){
			throw new RuntimeException(kbuilder.getErrors().toString());
		}
		KnowledgeBase knowledgeBase = kbuilder.newKnowledgeBase();
		knowledgeBase.addKnowledgePackages(kbuilder.getKnowledgePackages());
		return knowledgeBase;
	}
	
}

Happy coding Drools+jBPM 🙂