Skip to content

Moving Geneology to Drools 6.0

August 28, 2013

This blog is an excerpt from the new Drools Training Course that I am currently writing for Skills421 (www.skills421.com)

::

I set myself the challenge today of moving the Geneology Project to Drools 6.0.

For anyone interested in following the parts before this check out

The Project So Far

The Project so far comprises the following key files:

  • pom.xml
  • RuleRunner
  • TestRuleRunner
  • Person
  • ExtendedDate
  • Ancestry1.drl
  • Ancestry1.1.drl
  • Ancestry2.drl
  • Ancestry3.drl

Changing the Code

Of the files already coded we will need to change the following files to work with Drools 6.0.

  • pom.xml
    Change the Drools Version
  • RuleRunner
    Change to use the new org.kie api
  • Ancestry2.drl
    Our ExtendedDate needs to be Cast Explicitly
  • Ancestry3.drl
    Our ExtendedDate needs to be Cast Explicitly

With these changes implemented the System should work.

Changing the Pom

Let’s start first with the pom.xml.  The changes to this file are very simple.  Simply change the Drools Version to 6.0.0.CR1 as follows:

<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.skills421.training.drools</groupId>
<artifactId>Geneology</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>6.0.0.CR1</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>6.0.0.CR1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
</dependencies>
</project>

pom.xml

Immediately we save this we will see code error markers against the RuleRunner and TestRuleRunner classes.

Changing the RuleRunner

Changing the Rule Runner is quite a comprehensive change to use the new kie api.

Open the RuleRunner and change it to the following:

package com.skills421.training.geneology.rules;

import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieRepository;
import org.kie.api.builder.Message.Level;
import org.kie.api.io.KieResources;
import org.kie.api.io.Resource;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;

public class RuleRunner
{
	private KieContainer kContainer;
	private KieSession kSession;

	public KieContainer buildKnowledgeBaseWithRuleFiles(String... ruleFiles)
	{
		KieServices kieServices = KieServices.Factory.get();
		KieResources kieResources = kieServices.getResources();
		KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
		KieRepository kieRepository = kieServices.getRepository();

		for(String ruleFile : ruleFiles)
		{
			Resource resource = kieResources.newClassPathResource(ruleFile);

			// path has to start with src/main/resources
			// append it with the package from the rule
			kieFileSystem.write("src/main/resources/com/skills421/training/geneology/"+ruleFile, resource);
		}

		KieBuilder kb = kieServices.newKieBuilder(kieFileSystem);

        kb.buildAll();

        if (kb.getResults().hasMessages(Level.ERROR))
        {
            throw new RuntimeException("Build Errors:\n" + kb.getResults().toString());
        }

        kContainer = kieServices.newKieContainer(kieRepository.getDefaultReleaseId());

        return kContainer;
	}

	public KieSession runWithFacts(Object... facts)
	{
		this.kSession = this.kContainer.newKieSession();

		for (Object fact : facts)
		{
			this.kSession.insert(fact);
		}

		this.kSession.fireAllRules();

		return this.kSession;
	}

	public void dispose()
	{
		this.kSession.dispose();
	}
}

RuleRunner.java

As soon as you save this all the error indicators will disappear.

This looks quite hopeful, so let’s forget for now that I explained some of the rules need changing and let’s try running the rules so far.

Running the Rules

Right click on the TestRulesRunner and let’s run this as a JUnit Test.

Look to the console for the output:

I get the following

*** testFatherMarriedAndOlderThanSon ***
*** testFactsInsertedPrintInOrder ***
Found Person: Jack Walker b:07/01/1800
Found Person: John Walker b:18/02/1803
Found Person: Sam Walker b:28/12/1810
Found Person: James Walker b:14/11/1825
*** testFatherOlderThanSon ***
*** testFactsInserted ***
Found Person: Jack Walker b:07/01/1800
Found Person: Sam Walker b:28/12/1810
Found Person: James Walker b:14/11/1825
Found Person: John Walker b:18/02/1803

Console Output (ignoring SL4J errors)

There is nothing meaningful in this other than we know that testFatherMarriedAndOlderThanSon and testFatherOlderThanSon produced no results.

However, we ran a JUnit Test so let’s click on the JUnit Tab to see if the Test Case failed.

Here we can see the following error:

java.lang.RuntimeException: Build Errors:
Error Messages:
Message [id=1, level=ERROR, path=com/skills421/training/geneology/Ancestry3.drl, line=10, column=0
text=Unable to Analyse Expression birthdate > $fborn.add(0,0,18):
[Error: unable to resolve method using strict-mode: java.util.Date.add(java.lang.Integer, java.lang.Integer, java.lang.Integer)]
[Near : {… birthdate > $fborn.add(0,0,18) ….}]
^
[Line: 10, Column: 8]]
Message [id=2, level=ERROR, path=com/skills421/training/geneology/Ancestry3.drl, line=10, column=0
text=Unable to Analyse Expression birthdate > $married.add(0,9,0):
[Error: unable to resolve method using strict-mode: java.util.Date.add(java.lang.Integer, java.lang.Integer, java.lang.Integer)]
[Near : {… birthdate > $married.add(0,9,0) ….}]
^
[Line: 10, Column: 8]]

Warning Messages:

Info Messages:

at com.skills421.training.geneology.rules.RuleRunner.buildKnowledgeBaseWithRuleFiles(RuleRunner.java:40)

Changing the Rule Files

If we look carefully at the error we can see that the Rule Engine is treating out birthdate and marriagedate fields as simple Dates and not as ExtendedDates.

So let’s supply a quick fix by providing a cast in the Rule Files Ancestry2.drl and Ancestry3.drl where the add() method of the ExtendedDate class is used:

package com.skills421.training.geneology

import com.skills421.training.geneology.model.Person;
import com.skills421.training.extend.util.ExtendedDate;

dialect "mvel"

rule "Could be Father"
	when
        $father : Person(lastname=="Walker", $fborn : birthdate);
        $son : Person(lastname=="Walker", $sborn : birthdate > ((ExtendedDate) $fborn).add(0,0,18))
    then
        System.out.println( $father.firstname+" (b: "+$fborn+") could be father of "+$son.firstname+" (b: "+$sborn+")");
end

Ancestry2.drl

package com.skills421.training.geneology

import com.skills421.training.geneology.model.Person;
import com.skills421.training.extend.util.ExtendedDate;

dialect "mvel"

rule "Could be Married Father"
	when
        $father : Person(lastname=="Walker", $fborn : birthdate, $married : marriagedate !=null);
        $son : Person(lastname=="Walker", $sborn : birthdate > ((ExtendedDate) $fborn).add(0,0,18), birthdate > ((ExtendedDate) $married).add(0,9,0) );
    then
        System.out.println("MARRIED "+$father.firstname+" (b: "+$fborn+", m:"+$married+") could be father of "+$son.firstname+" ("+$sborn+")");
end

Ancestry3.drl

Running the Rules

Right click on the TestRulesRunner and let’s run this as a JUnit Test.

Now you should see that all the rules ran successfully.

Look to the console for the output:

*** testFatherMarriedAndOlderThanSon ***
MARRIED Jack (b: 07/01/1800, m:14/02/1824) could be father of James (14/11/1825)
*** testFactsInsertedPrintInOrder ***
Found Person: Jack Walker b:07/01/1800
Found Person: John Walker b:18/02/1803
Found Person: Sam Walker b:28/12/1810
Found Person: James Walker b:14/11/1825
*** testFatherOlderThanSon ***
John (b: 18/02/1803) could be father of James (b: 14/11/1825)
Jack (b: 07/01/1800) could be father of James (b: 14/11/1825)
*** testFactsInserted ***
Found Person: Jack Walker b:07/01/1800
Found Person: Sam Walker b:28/12/1810
Found Person: James Walker b:14/11/1825
Found Person: John Walker b:18/02/1803

Console Output (ignoring SL4J errors)

So there you have it. The project now runs on Drools 6.0.

Advertisements

From → Tutorials

One Comment
  1. Reblogged this on Skills421.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: