Google

Jul 10, 2014

Drools tutorial -- A non trivial example with step by step instructions

Drools allow you to externalize business rules to a database, excel spreadsheet, etc. In this tutorial let's look at an OrderItem scenario where  each OrderItem needs to be put through a couple of rules to determine its discounted price.

Step 0: Dependency jars an structure





Step 1: OrderItem.java.


package com.mytutorial;

import java.math.BigDecimal;

public class OrderItem {

 public enum PROD_TYPE {
  BASIC, LUXURY
 }

 private PROD_TYPE type;
 private int units;
 private BigDecimal unitPrice;
 private BigDecimal effectiveDiscountRate;
 private BigDecimal discountedUnitPrice;

 public PROD_TYPE getType() {
  return type;
 }

 public void setType(PROD_TYPE type) {
  this.type = type;
 }

 public int getUnits() {
  return units;
 }

 public void setUnits(int units) {
  this.units = units;
 }

 public BigDecimal getUnitPrice() {
  return unitPrice;
 }

 public void setUnitPrice(BigDecimal unitPrice) {
  this.unitPrice = unitPrice;
 }
 
 public BigDecimal getEffectiveDiscountRate() {
  return effectiveDiscountRate;
 }

 public void setEffectiveDiscountRate(BigDecimal effectiveDiscountRate) {
  this.effectiveDiscountRate = effectiveDiscountRate;
 }

 public BigDecimal getDiscountedUnitPrice() {
  return discountedUnitPrice;
 }

 public void setDiscountedUnitPrice(BigDecimal discountedUnitPrice) {
  this.discountedUnitPrice = discountedUnitPrice;
 }

 @Override
 public String toString() {
  return "OrderItem [type=" + type + ", units=" + units + ", unitPrice=" + unitPrice + ", effectiveDiscountRate="
    + effectiveDiscountRate + ", discountedUnitPrice=" + discountedUnitPrice + "]";
 }
}

Step 2: Define a map that stores the rule attributes as name/value pairs. Generally handy to load rules from a table say rule_attributes, which stores the rules as name/value pairs.

package com.mytutorial;

import java.util.HashMap;

public class RuleMap<K, V>   extends HashMap<String, Object>  {
 
 private static final long serialVersionUID = 1L;

 @Override
     public Object put(String key, Object value) {
    return super.put(key, value);
     }
}


For example,

  RuleMap<String, Object> ruleSet1 = new RuleMap<String, Object>();
  ruleSet1.put(KEY_ID, 1);
  ruleSet1.put(KEY_PRODUCT_TYPE, OrderItem.PROD_TYPE.BASIC);
  ruleSet1.put(KEY_DISCOUNT_RATE, 0.1);
  ruleSet1.put(KEY_BULK_DISCOUNT_QTY, 5);
  ruleSet1.put(KEY_BULK_DISCOUNT_RATE, 0.2);


Each rule has a unique id, and bulk discount is applied when number of items in OrderItem is >= than the KEY_BULK_DISCOUNT_QTY.  The KEY_DISCOUNT_RATE is applied to all the product types. This will be covered later in the main class.


Step 3: The drools template file product.drl. This is defined under com.mytutorial.

template header
id
normalDiscountRate
productType
bulkDiscountQty
bulkDiscountRate

package com.mytutorial

import com.mytutorial.*;
import java.math.*;
import java.text.*;
import java.util.*;

template "product-discount"


rule "@{id}"
    when $item : OrderItem(type == OrderItem.PROD_TYPE.@{productType});

    then
      BigDecimal disc = new BigDecimal("@{normalDiscountRate}");
      if($item.getUnits() >= @{bulkDiscountQty}){
           disc = disc.add(BigDecimal.valueOf(@{bulkDiscountRate}));
      }
    
      $item.setEffectiveDiscountRate(disc);
      $item.setDiscountedUnitPrice($item.getUnitPrice().multiply(BigDecimal.ONE.subtract(disc)));
end
end template


Step 4: The TemplateExpander.java that  compiles the com/mytutorial/product.drl with rule attributes defined via the  RuleMap.

package com.mytutorial;

import java.io.IOException;
import java.io.Reader;
import java.io.StringReader;
import java.util.Collection;

import org.drools.io.Resource;
import org.drools.io.ResourceFactory;
import org.drools.template.ObjectDataCompiler;

public final class TemplateExpander {
    
  
    private TemplateExpander() {

    }

    public static Resource expand(Resource template, Collection<?> rules) throws IOException {

        ObjectDataCompiler converter = new ObjectDataCompiler();
        String drl = converter.compile(rules, template.getInputStream());
        Reader rdr = new StringReader(drl);
        return ResourceFactory.newReaderResource(rdr);
    }

}

Step 5: The "KnowledgeBaseBuilder.java" that build the knowledge base. Depends on the TemplateExpander as well.

package com.mytutorial;

import java.io.IOException;
import java.util.List;

import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderError;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.io.Resource;
import org.drools.io.ResourceFactory;

public class KnowledgeBaseBuilder {

 public static KnowledgeBase build(List<RuleMap<String, Object>> ruleAttributes) throws IOException {

  KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
  KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();

  Resource rules = loadRuleFile("com/mytutorial/product.drl");
  rules = TemplateExpander.expand(rules, ruleAttributes);
  kbuilder.add(rules, ResourceType.DRL);

  //handle errors
  if (kbuilder.hasErrors()) {
   for (KnowledgeBuilderError err : kbuilder.getErrors()) {
    System.out.println("Errors: " + err);
   }
   throw new IllegalArgumentException("Could not parse knowledge.");
  }
  
  
  kbase = KnowledgeBaseFactory.newKnowledgeBase();
  //add packages to the knowledgebase
  kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());

  return kbase;

 }

 protected static Resource loadRuleFile(String ruleFile) {
  if (ruleFile.contains("://")) {
   return ResourceFactory.newUrlResource(ruleFile);
  } else {
   return ResourceFactory.newClassPathResource(ruleFile, KnowledgeBaseBuilder.class);
  }
 }

}

Step 6: Finally, the runnable main class DroolsMain.java. Constructs dummy rule attributes, dummy OrderItems, and then fire the rules and displays the discountedUnitPrice and the  effectiveDiscountRate.

package com.mytutorial;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import org.drools.KnowledgeBase;
import org.drools.logger.KnowledgeRuntimeLogger;
import org.drools.logger.KnowledgeRuntimeLoggerFactory;
import org.drools.runtime.StatefulKnowledgeSession;

public class DroolsMain {
 
 private static final String KEY_ID = "id";
 private static final String KEY_PRODUCT_TYPE = "productType";
 private static final String KEY_DISCOUNT_RATE = "normalDiscountRate";
 private static final String KEY_BULK_DISCOUNT_QTY = "bulkDiscountQty";
 private static final String KEY_BULK_DISCOUNT_RATE = "bulkDiscountRate";
 

 public static void main(String[] args) throws IOException {
  List<RuleMap<String, Object>> ruleAttributes = createDummyRuleMap();
  KnowledgeBase kbase = KnowledgeBaseBuilder.build(ruleAttributes);
  
  
  StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
  KnowledgeRuntimeLogger logger = KnowledgeRuntimeLoggerFactory.newFileLogger(ksession, "test");
  
  
  List<OrderItem> orderItems = getOrderItems();
  
  for (OrderItem orderItem : orderItems) {
   System.out.println("Before firing the rules:" + orderItem);
   
   ksession.insert(orderItem);
   ksession.fireAllRules();
   System.out.println("After firing the rules:" + orderItem);
  }
  
  ksession.dispose();
  
 }
 
 
 private static List<RuleMap<String, Object>> createDummyRuleMap(){
  RuleMap<String, Object> ruleSet1 = new RuleMap<String, Object>();
  ruleSet1.put(KEY_ID, 1);
  ruleSet1.put(KEY_PRODUCT_TYPE, OrderItem.PROD_TYPE.BASIC);
  ruleSet1.put(KEY_DISCOUNT_RATE, 0.1);
  ruleSet1.put(KEY_BULK_DISCOUNT_QTY, 5);
  ruleSet1.put(KEY_BULK_DISCOUNT_RATE, 0.2);
  
  
  RuleMap<String, Object> ruleSet2 = new RuleMap<String, Object>();
  ruleSet2.put(KEY_ID, 2);
  ruleSet2.put(KEY_PRODUCT_TYPE, OrderItem.PROD_TYPE.LUXURY);
  ruleSet2.put(KEY_DISCOUNT_RATE, 0.2);
  ruleSet2.put(KEY_BULK_DISCOUNT_QTY, 10);
  ruleSet2.put(KEY_BULK_DISCOUNT_RATE, 0.3);
  
  
  List<RuleMap<String, Object>> rules = new ArrayList<RuleMap<String,Object>>();
  rules.add(ruleSet1);
  rules.add(ruleSet2);
  
  return rules;
 }
 
 private static List<OrderItem> getOrderItems(){
  OrderItem orderItem1 = new OrderItem();
  orderItem1.setType(OrderItem.PROD_TYPE.BASIC);
  orderItem1.setUnits(3);
  orderItem1.setUnitPrice(BigDecimal.valueOf(2.50));
  
  OrderItem orderItem2 = new OrderItem();
  orderItem2.setType(OrderItem.PROD_TYPE.LUXURY);
  orderItem2.setUnits(12);
  orderItem2.setUnitPrice(BigDecimal.valueOf(5.00));
  
  List<OrderItem> listOfItems = new ArrayList<OrderItem>();
  listOfItems.add(orderItem1);
  listOfItems.add(orderItem2);
  
  return listOfItems;
 }

}

Step 7: Output

Before firing the rules:OrderItem [type=BASIC, units=3, unitPrice=2.5, effectiveDiscountRate=null, discountedUnitPrice=null]
After firing the rules:OrderItem [type=BASIC, units=3, unitPrice=2.5, effectiveDiscountRate=0.1, discountedUnitPrice=2.25]
Before firing the rules:OrderItem [type=LUXURY, units=12, unitPrice=5.0, effectiveDiscountRate=null, discountedUnitPrice=null]
After firing the rules:OrderItem [type=LUXURY, units=12, unitPrice=5.0, effectiveDiscountRate=0.5, discountedUnitPrice=2.50]



Labels:

Nov 25, 2013

Drools example with spreadsheets tutorial

This is an  industrial strength extension to Drools with Decision tables (Excel spreadsheet).

The drool file: @{variable_name} read from spreadsheet values. Others are from Java classes and objects.


//read from an excel spreadsheet
template header
rules
group
inCashTransactionNm
inAnalysisCd
inTransactionTypeCd
inTransactionSubTypeCd
transactionTypeCd
transactionSubTypeCd
incomeSubTypeCd
analysisCd
audAmount
fxGainAndLoss

package com.myapp.calc;

//... 

global DroolsHelper droolsHelper;
global BankAccountService bankAccountService;
global StockTransactionService stockTransactionService;

template "CashTransaction"

rule "@{rules}_@{row.rowNumber}"
    activation-group "@{group}"
    salience -@{row.rowNumber}
    when
       cashTransaction : CashTransaction(true
                                         ,cashTransactionNm in (@{inCashTransactionNm})
                                         ,analysisCd in (@{inAnalysisCd})
                                         )
       bankAccount : BankAccount() from bankAccountService.getBankAccount(cashTransaction.getBankAccountId())
       stockTransaction : StockTransaction(getTransactionTypeCd() != null, transactionTypeCd.getTypeCode() in (@{inTransactionTypeCd}), transactionSubTypeCd.getTypeCode() in (@{inTransactionSubTypeCd})) from stockTransactionService.findByPortfolio(cashTransaction.portfolioId, cashTransaction.sourceTransactionId)
    then
       Transaction transaction = new Transaction(); // create a new object
       transactionsExtract.add(transaction);
       transaction.setRuleActivated("@{rules}_@{row.rowNumber}");
       transaction.setAssetCode(bankAccount.getAccountCd());
       transaction.setTransactionId(Long.valueOf(cashTransaction.getSourceTransactionId()));
       transaction.setTransactionDtTm(cashTransaction.getAccountingDt());
       transaction.setValueAud(droolsHelper.@{audAmount}(cashTransaction));
       deTransaction.setForeignExchangeGainsAndLosses(@{fxGainAndLoss}(cashTransaction));
        
end

end template


Define the method that invokes the rules.

  //....
  public List<Transaction> calcCash(int accountId)
    {
        LOG.info(String.format("Calculating : calcCash"));
        
        List<Transaction> transactions = new ArrayList<Transaction>();
        
  //read cash transactions from the database
        List<CashTransaction> cashTransactions = cashTransactionService.findByAccountId(accountId);
        
        
        for (CashTransaction cashTransaction : cashTransactions)
        {
            if (cashTransaction.getAnalysisCd() != null)
            {
                @SuppressWarnings("rawtypes")
                List<Command> cmds = new ArrayList<Command>();
                
    //make things available to drools file defined above
                cmds.add(CommandFactory.newSetGlobal("transactionsExtract", new ArrayList<Transaction>(), true));
                cmds.add(CommandFactory.newSetGlobal("bankAccountService", bankAccountService));
                cmds.add(CommandFactory.newSetGlobal("stockTransactionService", stockTransactionService));
                cmds.add(CommandFactory.newSetGlobal("droolsHelper", DroolsHelper));
                cmds.add(CommandFactory.newInsert(cashTransaction));
                
    //execute drools rules
                ExecutionResults results = ksession.execute(CommandFactory.newBatchExecution(cmds));
                
    List<Transaction> trans = (List<Transaction>) results.getValue("transactionsExtract");
                
                transactions.addAll(trans);
            }
        }


Finally, if the value of audAmount = "cash" the DroolsHelper static method invoked will be as shown below

    public static BigDecimal cash(CashTransaction transaction)
    {
        return cashTransaction.getCashValuePortfolio();
    }

Labels:

Oct 23, 2013

Drools with Decision tables (Excel spreadsheet) tutorial

This is an extension to Drools tutorial with Maven. This externalize the rules criteria to an Excel spreadsheet license.xls. This means the age condition is supplied in the xls file under src/main/resources/drools/license.xls.

Step 1: Maven pom.xml file dependencies. Add dependencies including the decision tables.


 
<dependency>
 <groupId>org.drools</groupId>
 <artifactId>drools-core</artifactId>
 <version>5.5.0.Final</version>
</dependency>
<dependency>
 <groupId>org.drools</groupId>
 <artifactId>drools-compiler</artifactId>
 <version>5.5.0.Final</version>
</dependency>
<dependency>
 <groupId>org.drools</groupId>
 <artifactId>drools-decisiontables</artifactId>
 <version>5.5.0.Final</version>
</dependency>

Step 2: The license.xls file where rule name and age criteria are defined.



Step 3: The template file drools/license.drt under src/main/resources

 
template header
rules
age

package com.mycompany.app.drools;

import com.mycompany.app.drools.ApplicantForm;
import com.mycompany.app.drools.Applicant;
import com.mycompany.app.drools.ApplicantDao;
import com.mycompany.app.drools.ApplicantDaoImpl;

global ApplicantDao applicantDao;

template "license-rules"

  
rule "@{rules}_@{row.rowNumber}"
when
   $a : ApplicantForm();
   $applicant: Applicant( age < @{age} ) from applicantDao.findApplicant($a.getApplicantId());
then
   $a.setEligible( false );
end

end template

As you can see, @{age} and @{rules} were determined from the excel spreadsheet. The @{row.rownNumber} is excel spreadsheet row number.
Step 4: The Applicant.java, ApplicationForm.java, ApplicantDao.java, and ApplicantDaoImpl.java are same as before, but moved to a different package com.mycompany.app.drools.

Step 5: Finally, the test class ApplicantLicenseTest class that bootstraps with the xls and drt files.

 
package com.mycompany.app.drools;

import java.io.IOException;
import java.util.Iterator;

import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
import org.drools.builder.KnowledgeBuilder;
import org.drools.builder.KnowledgeBuilderError;
import org.drools.builder.KnowledgeBuilderFactory;
import org.drools.builder.ResourceType;
import org.drools.decisiontable.ExternalSpreadsheetCompiler;
import org.drools.io.ResourceFactory;
import org.drools.io.impl.ByteArrayResource;
import org.drools.runtime.StatefulKnowledgeSession;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.mvel2.optimizers.OptimizerFactory;

public class ApplicantLicenseTest
{
    
    private KnowledgeBase kbase;
    
    @Before
    public void setup() throws IOException
    {
        OptimizerFactory.setDefaultOptimizer("reflective");
        
        KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
        loadKnowledgeBase("drools/license.xls", "drools/license.drt",
                "license", 2, 1, kbuilder);
        
        kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());
        
    }
    
    private void loadKnowledgeBase(String aXlsName, String aDrlName,
            String aWorksheetName, int aStartRow, int aStartCol, KnowledgeBuilder aKbuilder) throws IOException
    {
        ExternalSpreadsheetCompiler sc = new ExternalSpreadsheetCompiler();
        
        String drlstr = sc.compile(ResourceFactory.newClassPathResource(aXlsName).getInputStream(),
                aWorksheetName,
                ResourceFactory.newClassPathResource(aDrlName).getInputStream(), aStartRow, aStartCol);
        
        System.out.println(drlstr);
        
        aKbuilder.add(new ByteArrayResource(drlstr.getBytes()), ResourceType.DRL);
        
        Iterator<KnowledgeBuilderError> errors = aKbuilder.getErrors().iterator();
        if (errors.hasNext())
        {
            StringBuilder errorMsg = new StringBuilder("Error compiling rules:");
            while (errors.hasNext())
            {
                errorMsg.append("\t" + errors.next().getMessage());
            }
            
            System.out.println(errorMsg.toString());
        }
    }
    
    @Test
    public void testApplicantLicense()
    {
        StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
        KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
        kbase.newStatefulKnowledgeSession();
        
        ApplicantForm applicant1 = new ApplicantForm();
        applicant1.setApplicantId(1);
        
        ApplicantForm applicant2 = new ApplicantForm();
        applicant2.setApplicantId(2);
        
        ksession.setGlobal("applicantDao", new ApplicantDaoImpl()); // assign the global dao
        
        ksession.insert(applicant1);
        ksession.insert(applicant2);
        ksession.fireAllRules();
        
        ksession.dispose();
        
        Assert.assertTrue(applicant1.isEligible() == false);//John is not eligible
        Assert.assertTrue(applicant2.isEligible() == true); //Peter is eligible
    }
    
}

Step 6: The output:

 
package com.mycompany.app.drools;
import com.mycompany.app.drools.ApplicantForm;
import com.mycompany.app.drools.Applicant;
import com.mycompany.app.drools.ApplicantDao;
import com.mycompany.app.drools.ApplicantDaoImpl;
global ApplicantDao applicantDao;

rule "License criteria_1"
when
   $a : ApplicantForm();
   $applicant: Applicant( age < 18 ) from applicantDao.findApplicant($a.getApplicantId());
then
   $a.setEligible( false );
end



fetched applicant: name=John, age=16
fetched applicant: name=Peter, age=20



Labels:

Dec 14, 2012

Drools tutorial with Maven

Drools is a popular open source business rules and work flow engine. It helps you externalize the rules as opposed to embedding within the Java code. The rules are executed in the form of when given a($condition) then execute the ($consequence) This tutorial contains a number of artifacts highlighted below.


Step 1: Create a new Maven project with the standard src/main/java, src/main/resources, src/test/java, etc source folder directories. Create a new src/main/rules folder to define the rules using the "mvel" expresion language.
Update the pom.xml file as shown below with relevant dependencies and also add the build/resources to tell maven that sr/main/rules is a source folder.


 

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>com.myapp</groupId>
 <artifactId>drools-poc</artifactId>
 <version>0.0.1-SNAPSHOT</version>
 <packaging>war</packaging>

 <properties>
  <drools.version>5.3.1.Final</drools.version>
  <junit.version>4.7</junit.version>
 </properties>

 <dependencies>
  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>${junit.version}</version>
  </dependency>
  <!-- drools library -->
  <dependency>
   <groupId>org.drools</groupId>
   <artifactId>drools-core</artifactId>
   <version>${drools.version}</version>
  </dependency>
  <dependency>
   <groupId>org.drools</groupId>
   <artifactId>drools-core</artifactId>
   <version>${drools.version}</version>
  </dependency>
  <dependency>
   <groupId>org.drools</groupId>
   <artifactId>drools-compiler</artifactId>
   <version>${drools.version}</version>
  </dependency>
  <!-- required for drools and spring integration -->
  <dependency>
   <groupId>org.drools</groupId>
   <artifactId>drools-spring</artifactId>
   <version>${drools.version}</version>
  </dependency>
  <dependency>
   <groupId>com.thoughtworks.xstream</groupId>
   <artifactId>xstream</artifactId>
   <version>1.2.2</version>
  </dependency>
 </dependencies>
 
 
 <build>
    <resources>
        <resource>
           <directory>src/main/rules</directory>
        </resource>
    </resources>
 </build>

</project>



Step 2: Define the domain classes like Applicant.java.

 

package com.company.license;

public class Applicant {

 private Integer id;
 private String name;
 private int age;
 
 public Applicant(Integer id, String name, int age) {
  super();
  this.id = id;
  this.name = name;
  this.age = age;
 }

 public Integer getId() {
  return id;
 }

 public void setId(Integer id) {
  this.id = id;
 }

 public String getName() {
  return name;
 }

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

 public int getAge() {
  return age;
 }

 public void setAge(int age) {
  this.age = age;
 }

 
 @Override
 public String toString() {
  return "name=" + name + ", age=" + age;
 }

}


Step 3: Define the data access interface and implementation classes that retrieves data from a persistent source like file or database. The implementation is simplified to read from a pre-populated in-memory map.

 

package com.company.license;

public interface ApplicantDao {
    Applicant findApplicant(Integer applicantId);
}


The implementation of the above interface.

package com.company.license;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class ApplicantDaoImpl implements ApplicantDao {
 
 private static final Map<Integer, Applicant> data = new ConcurrentHashMap<Integer, Applicant>();
 
 static {
  data.put(1, new Applicant(1, "John", 16));
  data.put(2, new Applicant(2, "Peter", 20));
 }

 public Applicant findApplicant(Integer applicantId) {
  Applicant applicant = data.get(applicantId);
  System.out.println("fetched applicant: " + applicant);
  return applicant;
 }
}



Step 4: Define the value object class that captures the user entry via a GUI. For example, ApplicantForm.java as shown below.

package com.company.license;

public class ApplicantForm {
 
 private Integer applicantId;
 private boolean eligible = true; // eligible by default

 public Integer getApplicantId() {
  return applicantId;
 }

 public void setApplicantId(Integer applicantId) {
  this.applicantId = applicantId;
 }

 public boolean isEligible() {
  return eligible;
 }

 public void setEligible(boolean eligible) {
  this.eligible = eligible;
 }
 
 @Override
 public String toString() {
  return "applicantId=" + applicantId + ", eligible=" + eligible;
 }
}

Step 5: Define the business rules via mvel expression language in a separate external file -- applicant-license.drl.

 

package com.company.license;

import com.company.license.ApplicantForm;
import com.company.license.Applicant;
import com.company.license.ApplicantDao;
import com.company.license.ApplicantDaoImpl;

global ApplicantDao applicantDao;
 
rule "is eligible for licence"
when
   $a : ApplicantForm();
   $applicant: Applicant( age < 18 ) from applicantDao.findApplicant($a.getApplicantId());
then
   $a.setEligible( false );
end


Step 6: Finally, the junit test class that makes use of drools to validate applicants. We will create 2 applicants, one with age >= 18, and another one with age < 18, to test the business rules engine (aka expert systems) of drools in action.


 

package com.company.license;

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.logger.KnowledgeRuntimeLogger;
import org.drools.logger.KnowledgeRuntimeLoggerFactory;
import org.drools.runtime.StatefulKnowledgeSession;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;

public class ApplicantLicenseTest {

 private KnowledgeBase kbase;

 @Before
 public void setup() {
  KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder();
  kbuilder.add(ResourceFactory.newClassPathResource("com/company/license/applicant-license.drl"),
    ResourceType.DRL);
  KnowledgeBuilderErrors errors = kbuilder.getErrors();
  if (errors.size() > 0) {
   for (KnowledgeBuilderError error : errors) {
    System.err.println(error);
   }
   throw new IllegalArgumentException("Could not parse knowledge.");
  }
  kbase = KnowledgeBaseFactory.newKnowledgeBase();
  kbase.addKnowledgePackages(kbuilder.getKnowledgePackages());

 }

 @Test
 public void testApplicantLicense() {
  StatefulKnowledgeSession ksession = kbase.newStatefulKnowledgeSession();
  KnowledgeRuntimeLogger logger = KnowledgeRuntimeLoggerFactory.newFileLogger(ksession, "test");
  KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase();
  kbase.newStatefulKnowledgeSession();

  ApplicantForm applicant1 = new ApplicantForm();
  applicant1.setApplicantId(1);
  
  ApplicantForm applicant2 = new ApplicantForm();
  applicant2.setApplicantId(2);
    
  ksession.setGlobal("applicantDao", new ApplicantDaoImpl()); // assign the global dao
  
  ksession.insert(applicant1);
  ksession.insert(applicant2);
  ksession.fireAllRules();
  
  ksession.dispose();

  Assert.assertTrue(applicant1.isEligible() == false);//John is not eligible
  Assert.assertTrue(applicant2.isEligible() == true); //Peter is eligible
 }

}



Yo may also like:

Drools with Decision tables (Excel spreadsheet)
Drools example with spreadsheets

Labels: