Skip to content

Simple Genealogy Rules – part 5

This tutorial is an excerpt from the new Skills421 (www.skills421.com) training course that I am writing for JBOSS Drools.

In part1 we covered the Problem Domain, in part2 we set up our project to work with Drools 5.4.0.Final and JUnit, and in part3 we will create the Classes for our Facts that we are going to pass to the Rule Engine.

In part4 we created the Person class which is the type of Fact that we insert into the Rule Engine, we created the Rule Runner that will build the Knowledge Base for our Rule Engine and insert our Facts into the Rule Engine, and we created and ran our first rule.

Now in part5 we will modify our rules to identify possible Father – Son relationships in the People from whom we have derived our facts.

Refining Our Rules

The first thing we will do is add the additional test cases to our TestRuleRunner class.

Change class as follows:

package com.skills421.training.geneology.rules;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

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

public class TestDroolsRunner
{
	private static RuleRunner ruleRunner;

	private static Person p1;
	private static Person p2;
	private static Person p3;
	private static Person p4;

	@BeforeClass
	public static void setupFacts()
	{
		try
		{
			ruleRunner = new RuleRunner();

			p1 = new Person();
			p1.setFirstname("John");
			p1.setLastname("Walker");
			p1.setBirthdate(new ExtendedDate("18/02/1803"));

			p2 = new Person();
			p2.setFirstname("James");
			p2.setLastname("Walker");
			p2.setBirthdate(new ExtendedDate("14/11/1825"));

			p3 = new Person();
			p3.setFirstname("Sam");
			p3.setLastname("Walker");
			p3.setBirthdate(new ExtendedDate("28/12/1810"));

			p4 = new Person();
			p4.setFirstname("Jack");
			p4.setLastname("Walker");
			p4.setBirthdate(new ExtendedDate("07/01/1800"));
			p4.setMarriagedate(new ExtendedDate("14/02/1824"));
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	@Test
	public void testFactsInserted()
	{
		System.out.println("*** testFactsInserted ***");
		ruleRunner.buildKnowledgeBaseWithRuleFiles("Ancestry1.drl");
		ruleRunner.runWithFacts(p1,p2,p3,p4);
		ruleRunner.dispose();
	}

	@Test
	@Ignore
	public void testFactsInsertedPrintInOrder()
	{
		System.out.println("*** testFactsInsertedPrintInOrder ***");
		ruleRunner.buildKnowledgeBaseWithRuleFiles("Ancestry1.1.drl");
		ruleRunner.runWithFacts(p1,p2,p3,p4);
		ruleRunner.dispose();
	}

	@Test
	@Ignore
	public void testFatherOlderThanSon()
	{
		System.out.println("*** testFatherOlderThanSon ***");
		ruleRunner.buildKnowledgeBaseWithRuleFiles("Ancestry2.drl");
		ruleRunner.runWithFacts(p1,p2,p3,p4);
		ruleRunner.dispose();
	}

	@Test
	@Ignore
	public void testFatherMarriedAndOlderThanSon()
	{
		System.out.println("*** testFatherMarriedAndOlderThanSon ***");
		ruleRunner.buildKnowledgeBaseWithRuleFiles("Ancestry3.drl");
		ruleRunner.runWithFacts(p1,p2,p3,p4);
		ruleRunner.dispose();
	}

}

TestRuleRunner.java

Reviewing the Changes

We have added 3 more test cases but we have annotated then with @Ignore for now because we don’t want these additional test cases to run.

Each test does exactly the same thing but uses a different Rule File so the expected output  will change.

To run each new test as we create each new Rule File, simply remove the @Ignore that prefixes that test

Sorting our Facts into Date-of-Birth Order

The first of our new rules will simply sort our Facts into order by Date of Birth.

Create the DRL file Ancestry1.1.drl as follows:

package com.skills421.training.geneology

import com.skills421.training.geneology.model.Person;

dialect "mvel"

rule "Order People by Date of Birth"
    when
        $person : Person(lastname=="Walker", $dob : birthdate);
        not Person(birthdate < $dob)
    then
        System.out.println( "Found Person: "+$person.firstname+" "+$person.lastname+" b:"+$person.birthdate);
        retract($person);
end

Ancestry1.1.drl

This rule is a simple modification to our first rule.

Looking at the first line of our Criteria:

  • First of all we identify every Person with a lastname of Walker.  We store a reference to that Person in the variable $person.
  • In addition, we also obtain the birthdate of that person and reference that value in the $dob variable.

Looking at the second line of our Criteria;

  • We tell the Rule Engine that there must not exist a Person whose birthdate is less than the birthdate of the first Person whose birthdate we reference with $dob.

Now if we think about this.  We have 4 Person objects in working memory, so only one of these Person objects can match that criteria.  This rule therefore will return the one person who was born before everybody else.

In order for the remaining Person objects to be sorted we now need to make a change to the Consequence section of the rule.

Looking at the second line of our Consequence:

We now retract the Person who matched our criteria.

This means that we will remove from Working Memory the Person who was born before everybody else.

As soon as a Fact is removed from the Working Memory, however, the Rule Engine will re-evaluate all the other remaining Facts to see if this change has affected them.  It does this by re-firing all the rules.

In this situation, by retracting the first born of all the Person objects and then implicitly re-firing all the rules, the rule will not identify, print and retract a new first born Person.

The rule will be re-fired until all the Person objects have become the first born and retracted from working memory.

This is not a particularly efficient way to use the Rule Engine as every rule would be re-evaluated and if we have a lot of rules this could result in a lot of unnecessary processing.

Remove the @Ignore from the testFactsInsertedPrintInOrder() test and run the tests.

Your console should now display the following:

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

Console Output

Looking for a Potential Father

In our next test case we will identify all potential fathers.

  • We do this by stipulating that  a father must be 18 years older than his son.

Create the DRL file Ancestry2.drl as follows:

package com.skills421.training.geneology

import com.skills421.training.geneology.model.Person;

dialect "mvel"

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

Ancestry2.drl

Let’s take a look a the first line of our Criteria:

  • here we identify a  father, and reference the Person object with the variable $father.
  • The Father object must have a lastname of Walker
  • We reference the birthdate of the Father with the variable $fborn so that we can use it later.

Let’s take a look at the second line of our Criteria:

  • here we identify a son for the father, and reference the Person object with the variable $son.
  • The Son object must have a lastname of Walker
  • We reference the birthdate of the Son with the variable $sborn so that we can use it later.
  • We stipulate that the Son’s birthdate must be more than the Father’s birthdate + 18 years.

If we think about this, all the work is done in the second line of the Criteria and shows how simple this rule is with our new ExtendedDate object in the Person class as we can access the add() method directly from within the Criteria.

As for the calculation itself.  If we assume that the father was born on 18/05/1800; then adding 18 years to that would give 18/05/1818 making the father 18 years old.  So long as the son is born after that date we have a potential match.

Remove the @Ignore from the testFatherOlderThanSon() test and run the tests.

Your console should now display the following:

testFatherOlderThanSon
Jack (b: 07/01/1800) could be father of James (b: 14/11/1825)
John (b: 18/02/1803) could be father of James (b: 14/11/1825)

Console Output

Looking for a Potential Married Father

In our next test case we will identify all potential fathers who were married.

  • We do this by stipulating that  a father must be 18 years older than his son.
  • We also stipulate that the father must have married 9 month before the son was born.

Create the DRL file Ancestry3.drl as follows:

package com.skills421.training.geneology

import com.skills421.training.geneology.model.Person;

dialect "mvel"

rule "Could be Married Father"
	when
        $father : Person(lastname=="Walker", $fborn : birthdate, $married : marriagedate !=null);
        $son : Person(lastname=="Walker", $sborn : birthdate > $fborn.add(0,0,18), birthdate > $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

Let’s take a look a the first line of our Criteria:

  • here we identify a  father, and reference the Person object with the variable $father.
  • The Father object must have a lastname of Walker
  • We reference the birthdate of the Father with the variable $fborn so that we can use it later.
  • We reference the marriagedate  of the Father with the variable $married so that we can use it later.
  • We ensure that the marriagedate is not null

Let’s take a look at the second line of our Criteria:

  • here we identify a son for the father, and reference the Person object with the variable $son.
  • The Son object must have a lastname of Walker
  • We reference the birthdate of the Son with the variable $sborn so that we can use it later.
  • We stipulate that the Son’s birthdate must be more than the Father’s birthdate + 18 years.
  • We stipulate that the Son’s birthdate must be more than the Father’s marriagedate + 9 months.

Remove the @Ignore from the testFatherMarriedAndOlderThanSon() test and run the tests.

Your console should now display the following:

testFatherMarriedAndOlderThanSon
MARRIED Jack (b: 07/01/1800, m:14/02/1824) could be father of James (14/11/1825)

Console Output

 

Moving Forward

Moving forward we will modify our rules to make them more efficient and more resilient. We will also create new Fact types and new Rules to process the additional information we may discover as we research of Genealogy tree.

Simple Genealogy Rules – part 4

This tutorial is an excerpt from the new Skills421 (www.skills421.com) training course that I am writing for JBOSS Drools.

In part1 we covered the Problem Domain, in part2 we set up our project to work with Drools 5.4.0.Final and JUnit, and in part3 we will create the Classes for our Facts that we are going to pass to the Rule Engine.

Now in part4 we will create the Person class which is the type of Fact that we will insert into the Rule Engine, we will create the Rule Runner that will build the Knowledge Base for our Rule Engine and insert our Facts into the Rule Engine, and we will create and run our first rule.

Creating the Rule Runner

The Rule Runner is responsible for parsing the rules that we pass to it and packaging them into a Knowledge Base.

It is also responsible for evaluating the Facts that we supply against the Rules that we have written.

Once a Knowledge Base is created it is possible to create multiple Knowledge Sessions with which to process the Facts we supply – meaning that we do not need to rebuild the Knowledge Base every time we process a different set of facts.  Indeed, we only need to rebuild the Knowledge Base if we change the rules.

In the src/main/java let’s create the package com.skills421.training.geneology.rules and in that package we will create the RuleRunner.java class as follows:

package com.skills421.training.geneology.rules;

import org.drools.KnowledgeBase;
import org.drools.KnowledgeBaseFactory;
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;

public class RuleRunner
{
	private KnowledgeBase knowledgeBase;
	private StatefulKnowledgeSession session;

	public KnowledgeBase buildKnowledgeBaseWithRuleFiles(String...ruleFiles)
	{
		KnowledgeBuilder builder = KnowledgeBuilderFactory.newKnowledgeBuilder();

		for(String ruleFile : ruleFiles)
		{
			if(ruleFile.toUpperCase().endsWith(".DRL"))
			{
				builder.add(ResourceFactory.newClassPathResource(ruleFile), ResourceType.DRL);
			}
		}

		if (builder.hasErrors())
		{
			throw new RuntimeException(builder.getErrors().toString());
		}

		this.knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase();
		this.knowledgeBase.addKnowledgePackages(builder.getKnowledgePackages());
		
		return this.knowledgeBase;
	}

	public StatefulKnowledgeSession runWithFacts(Object... facts)
	{
		this.session = this.knowledgeBase.newStatefulKnowledgeSession();

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

		this.session.fireAllRules();
		
		return this.session;
	}

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

RuleRunner.java

Reviewing the Code

Let’s take a quick look at the code:

The RuleRunner class comprises three methods:

  • buildKnowledgeBaseWithRuleFiles(String…ruleFiles)
  • runWithFacts(Object… facts)
  • dispose()

The buildKnowledgeBase method is responsible for parsing the rules and creating a Knowledge Base in the Rule Engine.  We store this in a member variable so that we can reuse this Knowledge Base with future calls and we also return a reference to the Knowledge Base in the event that we wish to access that Knowledge Base from our own code.

Each time we supply a set of facts we will use the same Knowledge Base.

The runWithFacts methods is responsible for processing the facts that we pass.  It firsts creates a  Knowledge Session from the Knowledge Base and then inserts the facts into the Knowledge Session.  It then fires all the rules.

A reference to the Knowledge Session is returned to the calling code in case we can to access the Knowledge Sesssion from our own code.

Finally, the dispose method is responsible for disposing with the Knowledge Session and freeing up memory and resources.

Creating our First Rule

So far all we have done is provide the foundations for our Geneology Project.

In simple terms, all that we want to do is the following:

  • create a set of Rules
  • create a set of Facts
  • pass the Rules and the Facts to the Rule Engine
  • process the Facts with the Rules
  • print out the result

So let’s get on with creating a very simple rule to put together everything that we have done and test it with a JUnit Test.

The first thing we need to do, is create a Source Folder in which to store our Rules so that we can find the Rules on our ClassPath.

So let’s create a new Source Folder called src/main/rules by doing the following:

Right click on the Project in Project Explorer panel and from the popup menu select
-> New -> Source Folder

025-RuleFolder

In the Folder Name: field enter: src/main/rules and click Finish

Now create a New File in the rules folder by right clicking on the Folder and selecting
-> New -> Other -> File

Name the file Ancestry1.drl and click Finish.

Now let’s type our first rule into Ancestry1.drl as follows:

package com.skills421.training.geneology
 
import com.skills421.training.geneology.model.Person;

dialect "mvel"
 
rule "Person Found"
    when
        $person : Person(lastname=="Walker");
    then
        System.out.println( "Found Person: "+$person.firstname+" "+$person.lastname+" b:"+$person.birthdate);
end

Ancestry1.drl

Understanding our First Rule

For now we will take a cursory look at the Rule we have written, looking at the most important parts.

First we have imported the Person class so that the Rule knows how to communicate with the Person facts that are passed to it.

The Rule itself is then broken down into two section:

  • The Criteria (aka the Where Clause, or the Condition) is the code preceded by the when statement.
  • The Consequence is the code preceded by the then statement.

When the facts are inserted into the Rule Engine they are inserted into Working Memory where the are stored.

When the Rules are Fired each Fact is matched against each rule and where a match occurs, the Fact is processed by that Rule.

The Criteria specifies the Matching Criteria.

In our rule above we have specified:

$person : Person(lastname=="Walker")

This means that every Person object that the Rule Engine is holding will be evaluated to see if the lastname is equal to “Walker”.  Where a match is found, the Person object will be referenced by the variable $person so that we can access that object in the Consequence.

In the Consequence, we simply print the details of our referenced $person object.

Putting it all Together – JUnit Test

Ok, so we now have our Rule Runner and our first simple rule.

Let’s now write the JUnit Test Case to put it all together.

In the  /src/test/java Source Folder, create the package com.skills421.training.geneology.rules

In our new package, create the JUnit Test Case TestDroolsRunner.java as follows:

package com.skills421.training.geneology.rules;

import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;

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

public class TestDroolsRunner
{
	private static RuleRunner ruleRunner;
	
	private static Person p1;
	private static Person p2;
	private static Person p3;
	private static Person p4;

	@BeforeClass
	public static void setupFacts()
	{
		try
		{
			ruleRunner = new RuleRunner();
			
			p1 = new Person();
			p1.setFirstname("John");
			p1.setLastname("Walker");
			p1.setBirthdate(new ExtendedDate("18/02/1803"));
			
			p2 = new Person();
			p2.setFirstname("James");
			p2.setLastname("Walker");
			p2.setBirthdate(new ExtendedDate("14/11/1825"));
			
			p3 = new Person();
			p3.setFirstname("Sam");
			p3.setLastname("Walker");
			p3.setBirthdate(new ExtendedDate("28/12/1810"));
			
			p4 = new Person();
			p4.setFirstname("Jack");
			p4.setLastname("Walker");
			p4.setBirthdate(new ExtendedDate("07/01/1800"));
			p4.setMarriagedate(new ExtendedDate("14/02/1824"));
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}

	@Test
	public void testFactsInserted()
	{
		System.out.println("*** testFactsInserted ***");
		ruleRunner.buildKnowledgeBaseWithRuleFiles("Ancestry1.drl");
		ruleRunner.runWithFacts(p1,p2,p3,p4);
		ruleRunner.dispose();
	}
	
	@Test
	public void testFactsInsertedPrintInOrder()
	{
		System.out.println("*** testFactsInsertedPrintInOrder ***");
		ruleRunner.buildKnowledgeBaseWithRuleFiles("Ancestry1.1.drl");
		ruleRunner.runWithFacts(p1,p2,p3,p4);
		ruleRunner.dispose();
	}
	
	@Test
	public void testFatherOlderThanSon()
	{
		System.out.println("*** testFatherOlderThanSon ***");
		ruleRunner.buildKnowledgeBaseWithRuleFiles("Ancestry2.drl");
		ruleRunner.runWithFacts(p1,p2,p3,p4);
		ruleRunner.dispose();
	}
	
	@Test
	public void testFatherMarriedAndOlderThanSon()
	{
		System.out.println("*** testFatherMarriedAndOlderThanSon ***");
		ruleRunner.buildKnowledgeBaseWithRuleFiles("Ancestry3.drl");
		ruleRunner.runWithFacts(p1,p2,p3,p4);
		ruleRunner.dispose();
	}

}

TestDroolsRunner.java

Reviewing the Code

In our Test class we first create a static reference to the RuleRunner and to four Facts.

@BeforeClass setupFacts()

Then, one time only for the whole class (regardless of the number of tests in the class) we run the static method setupFacts().  This method instantiates the RuleRunner object and 4 Person objects which are the facts that we will insert into each Knowledge Session as we run each individual Test.

@Test testFactsInserted()

Finally we have the testFactsInserted() method which is out Test.

In this method, we first:

  • Print a message so we know which Test Case is being executed.
  • Then we build a Knowledge Base with the rule that we have written.
  • Then we obtain a Knowledge Session and insert into it the 4 facts that we have.
  • Then we Fire all the Rules.
  • Finally we dispose of the Knowledge Session.

Our expected result is that the Rule Engine will print out the details of each Person with a last name of Walker.

Running the Test

So at last, let’s give our test a whirl.

Run the TestDroolsRunner as a JUnit Test and look to the Console for the Output.

*** 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

Sample Console Output

Note that the order in which each Person is printed may vary.  This is because the Rule Engine does not process the Rules or the Facts in any particular order unless we tell it to.

Moving Forward

In the next part we will modify our rules to identify possible Father – Son relationships amongst the People for whom Facts have been created.

Simple Genealogy Rules – part 3

In part1 we covered the Problem Domain, in part2 we set up our project to work with Drools 5.4.0.Final and JUnit.  Now in part3 we will create the Classes for our Facts that we are going to pass to the Rule Engine.

Creating the Facts

If we remember from our Problem Domain, the main fact that we will provide to the Rule Engine (for now) is each Person for whom we have found a record.

For this reason we will need a Person class.  Also, since we are going to be doing a lot of Date comparison and manipulation, we will extend the java.util.Date class to give us and ExtendedDate class into which we can add any new methods that we require.

Since the Person class will have a dependency on the ExtendedDate class, let’s start with the ExtendedDate class.

ExtendedDate

in the src/main/java source folder, create the package com.skills421.training.extend.util

Now create the ExtendedDate class in that package as follows:

package com.skills421.training.extend.util;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class ExtendedDate extends Date
{
	public static final String DEFAULTFORMAT = "dd/MM/yyyy";
	private String dateFormat;

	/**
	 *
	 */
	private static final long serialVersionUID = 4921346043888844588L;

	public ExtendedDate(String dateFormat, String dateString) throws ParseException
	{
		this.dateFormat = dateFormat;

		SimpleDateFormat formatter = new SimpleDateFormat(dateFormat);

		Date date = formatter.parse(dateString);
		this.setTime(date.getTime());
	}

	public ExtendedDate(String dateFormat, long time)
	{
		this.setTime(time);
		this.dateFormat = dateFormat;
	}

	public ExtendedDate(String dateString) throws ParseException
	{
		this(DEFAULTFORMAT,dateString);
	}

	public ExtendedDate(long time)
	{
		this(DEFAULTFORMAT,time);
	}

	public String toString()
	{
		SimpleDateFormat formatter = new SimpleDateFormat(this.dateFormat);
		return formatter.format(this);
	}

	public ExtendedDate add(int days, int months, int years)
	{
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(this.getTime());

		calendar.add(Calendar.DAY_OF_YEAR, days);
		calendar.add(Calendar.MONTH, months);
		calendar.add(Calendar.YEAR, years);

		ExtendedDate newDate  = new ExtendedDate(calendar.getTimeInMillis());

		return newDate;
	}

}

ExtendedDate.java

This class extends the java.util.Date class and adds the following constructors and methods:

  • public ExtendedDate(String dateFormat, String dateString)
  • public ExtendedDate(String dateString)

These methods enable us to pass the dateFormat – e.g. dd/MM/yyyy (the default) and the dateString to instantiate and ExtendedDate object.  The dateFormat is then used by the overloaded toString() method to return the correctly formatted Date as  a String.

  • public ExtendedDate(String dateFormat, long time)
  • public ExtendedDate(long time)

Likewise, these methods enable us to pass the dateFormat to the class and use this for formatting the output returned by the toString() method.

  • public ExtendedDate add(int days, int months, int years)

This method will be used from within the Rule Engine and allows us to add days, months and years to the existing ExtendedDate and return a new ExtendedDate object.

This is particularly useful for determining if, for example a father is 18 years older than a prospective son , and if the father was married 9 months before the son was born:

sonsDateOfBirth > fathersDateOfBirth.add(0,0,18)

sonsDateOfBirth  > fathersDateOfMarriage.add(0,9,0)

as we will see when we write the Rules.

 

Testing the ExtendedDate

Since we have JUnit installed, let’s create a quick JUnit test to test our ExtendedDate class works as planned.

In the src/test/java source folder we need to create the same package as we created in src/main/Java: com.skills421.training.extend.util

inside that package we can create the JUnit test TestExtendedDate by doing the following:

Right-click on the com.skills421.training.extend.util package and from the pop-up menu select New -> Other -> JUnit

020-JunitTest

Select the JUnit Test Case and click Next

021-JunitTest

set the name to TestExtendedDate and click Finish.

Now we can edit the source as follows:

package com.skills421.training.extend.util;

import static org.junit.Assert.*;

import java.text.ParseException;

import org.junit.Test;

import com.skills421.training.extend.util.ExtendedDate;

public class TestExtendedDate
{

	@Test
	public void test() throws ParseException
	{
		ExtendedDate date = new ExtendedDate("28/05/1907");
		ExtendedDate newDate = date.add(0, 0, 15);

		assertEquals("28/05/1907", date.toString());
		assertEquals("28/05/1922", newDate.toString());
	}

	@Test
	public void test2() throws ParseException
	{
		ExtendedDate date = new ExtendedDate("28/05/1907");
		ExtendedDate newDate = date.add(2, 1, 3);

		assertEquals("28/05/1907", date.toString());
		assertEquals("30/06/1910", newDate.toString());
	}

	@Test
	public void test3() throws ParseException
	{
		ExtendedDate date = new ExtendedDate("28/05/1907");
		ExtendedDate newDate = date.add(5, 7, 20);

		assertEquals("28/05/1907", date.toString());
		assertEquals("02/01/1928", newDate.toString());
	}

}

TestExtendedDate.java

 

Running the Tests

Now to run the tests right click on the class in the Project Explorer and from the pop-up menu select Run As -> Junit Test

You should see the following output:

024-JunitTest

 

Moving Forward

In the next part we will create the Person class which is the type of Fact that we will insert into the Rule Engine, we will create the Rule Runner that will build the Knowledge Base for our Rule Engine and insert our Facts into the Rule Engine, and we will create and run our first rule.

Simple Genealogy Rules – part 2

This is the second part from the excerpts from the Skills421 (www.skills421.com) training course that I am currently developing.

In part 1 we identified the problem domain.  Here in part 2 we will create a simple Drools Project with which to implement our solution.

Creating the Geneology Project

Some of you may have noticed that I have spelled Genealogy wrong.  This is a deliberate act so that I can build this project step by step alongside my real Genealogy project.

So – here we go installing the incorrectly spelled Geneology Project.

I am going to assume you have downloaded Eclipse and Maven and installed both.  If not, then have a look on www.skills421.com for the Maven tutorial.  It will be appearing there some time tomorrow.

  • Open Eclipse and right click in the Project Explorer window.
  • From the popup menu select New -> Other -> Maven Project

009-New Project

Select Maven Project and click Next

010-New Project

Check the box to create a simple project and click Next

011-New Project

Enter the Group id as: com.skills421.training.drools
Enter Artifact Id as: Geneology

click Finish.

You can now see your project as a Maven project similar to that below:

012-New Project

Open the pom.xml file and change the view in the content panel to pom.xml

<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>
</project>

pom.xml

Adding the Drools Dependencies

We are going to use Drools 5.4.0.Final for this tutorial.

To find the dependency code navigate to http://mvnrepository.com/artifact/org.drools and click on the Last Version button for the top two entries – drools-compiler and drools-core.

This will display a list of version numbers, types and download buttons. Find the version 5.4.0.Final and click that.

The dependency will appear similar to this:

015-DroolsRepository

Cut and paste the dependency into your pom.xml.

Your final result will look like this:

<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>5.4.0.Final</version>
		</dependency>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-core</artifactId>
			<version>5.4.0.Final</version>
		</dependency>
	</dependencies>
</project>

pom.xml

Take a look in your Project Explorer in the Maven Dependencies library folder and you will see the following:

016-DroolsRepository

This is everything we need to be able to build and run Drools Rules.

However, for testing, let’s do one more thing:

Install JUnit

Go back to your browser and enter: http://mvnrepository.com/artifact/junit 

Choose the latest release version of JUnit and copy the dependency into your pom.xml

Your pom will now look like this:

<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>5.4.0.Final</version>
		</dependency>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-core</artifactId>
			<version>5.4.0.Final</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.11</version>
		</dependency>
	</dependencies>
</project>

pom.xml

This will download the JUnit jars and add them to your Maven Dependencies Library folder.

Next Steps

We have now set up our project so in the next part we will build create the Fact objects that will represent the people in our genealogy tree, and some simple extensions to some of the standard Java classes to make life simpler for us as we implement the rules.

Simple Genealogy Rules – part 1

The following is a simple excerpt from the coming Skills421 (www.skills421.com) Drools Training Course.

The Problem Domain

We are busy researching a family tree and have encountered a person by the name of Richard Walker from Thornhill in West Yorkshire in 1813.  Our task is to identify his immediate and extended family from the information we can gleam from Ancestry and Parish records.

The problem we are faced with is that nearly everybody in the area appears to be called Walker in the 1600s, 1700s and 1800s and we need to try and piece together relationships.  Parish records from the area provide scant information – name, date of the christening, fathers name and location.  On rare occasions we are provided with a mother’s name but very rarely.

Other than that we can sometimes find details of marriage date and location, birth date and location, christening date and location and death date and location.  Once we get into the mid 1800s we also have census records but not going back before about 1830.

So, our task is to try and piece together the most probable family tree from the information given.

Person

In summary, a Person can have the following attributes:

  • firstname
  • lastname
  • father’s firstname
  • mother’s firstname
  • date of birth
  • date of christening
  • date of marriage
  • date of death
  • location of birth
  • location of christening
  • location of marriage
  • location of death

To put the pieces together, therefore, we need to create a few simple rules that we can configure to try and put together a family tree.

Putting the Pieces Together – Father Son

  • the father must be 18 years older than the son
  • the father must have married 9 months before the son was born
  • the father must not have died before the son was born
  • the father must have been under 40 years old when the son was born

Additional rules that are a little more assumptive

  • the father and son must have been born in the same location or nearby

Putting the Pieces Together – Husband and Wife

  • the husband and wife must be approximately the same age
  • neither the husband or wife must be dead at the time of the marriage
  • both husband and wife must be 18 or over to get married

Additional rules that are a little more assumptive

  • the husband and wife must have been born in the same location or nearby

The Solution

Now we have identified our problem domain let’s move on to developing the solution. For this we are going to use the JBOSS Drools Rule Engine with Java and Eclipse as the IDE.

This is covered in part2.

Exciting Workshops on the Skills421 Training Courses

Skills421

IT Training Courses

Skills421 (www.skills421.com) specialises in Java and Java Enterprise Classroom Led IT Training Courses. These courses are offered both as 5 days courses during the week and 2 day courses during the Weekends.

Along with our Java courses we also offer 5 Day and Weekend classes in Rule Engine technology – specifically JBOSS Drools, and the Spring Framework: Core Spring, Spring MVC and Spring WebFlow.

For the iPhone and iPad Developers we offer hands-on Training Courses in Objective-C, iOS Development for the iPhone and iOS Development for the iPad.

As a Skills421 Delegate you can work on our own Skills421 computers or you can bring your own laptop with you to the course. All delegates will be able to take the training materials away with them, including their project work, and will be invited to join a dedicated Skills421 Community Group for ongoing support, questions and…

View original post 421 more words

%d bloggers like this: