You are on page 1of 164

Table

of Contents
1. Introduction 1.1
2. Technical Materials 1.2
3. Overview 1.3
4. Drools tutorial 1.4
1. Installing Drools Tooling 1.4.1
2. Data Model used in the tutorial 1.4.2
3. Lesson 1 : Core concepts 1.4.3
4. Lesson 2 : binding 1.4.4
5. lesson 3 : Some more drools languages 1.4.5
6. lesson 4 : ruleflow 1.4.6
7. lesson 5 : Exercise 1.4.7
1. Requirements 1.4.7.1
5. BRMS tutorial 1.5
1. Organizational Unit And repository 1.5.1
2. Data Model 1.5.2
3. Business Requirements 1.5.3
4. Design 1.5.4
5. Implementing the Process Flow 1.5.5
6. Implementation technical parts 1.5.6
7. Implementing the initial step 1.5.7
8. Implementing standard price step 1.5.8
9. Implementing the Child over 3 years old is free promotion step 1.5.9
10. Implementing the Birthday promotion step 1.5.10
11. Implementing the Zurich reduction step 1.5.11
12. Implementing the final step 1.5.12
13. Conclusion 1.5.13
6. BRMS Runtime tutorial 1.6
1. Install needed tools to run the workbench 1.6.1

1
Introduction

Introduction
Intended audience
This book as its title suggest is for newcomers to drools. As explained in the drools tutorial, when
using drools you will change the classical development paradigm you are using going from
procedurale to declarative programming. The shift is not complex to accomplish but there is a need
for a onboarding set of tutorials that will help you to jump into implementing algorith/business rules
wiht drools with no difficulty. As a regular trainer of drools, I can only encourage you to do the
exercices completely by yourself and try not to go the solutions directly. By doing so, the learning
curve will be a little longer but so much easier at the end. By going directly to the solution, you will
miss 50% of the training content.

drools On Boarding
You can find here all the materials that goes with this book. The book is also on github here .

Here is the content of the book

How to edit the docs


go to Drools Onboarding book
There you can clone and edit the document
You can do it from github also

How to run the tutorial and examples


the source code for the tutorials and examples can be found on github
there is a virtual box appliance available with all included that you can find here (TODO)

2
Technical Materials

Technical Materials
Technical Materials
Here is the list of all source code available for the drools onboarding book

For the drools tutorial


For the drools exercise.
For the BRMS tutorial exercise
There are two other BRMS project available loyalty and Car Insurance. Those two projects are
not yet finished.

3
Overview

Overview
Overview
Software programmers are used to a procedural approach when it comes to implement Business
requirements. The consequence is that the business rule implementation solely depends on the
competence and understanding the software developer has of the Business Rules he has to implement.
This limitation was already seen and identified by researches. Object oriented programming may be
seen as "the solution" to business implementation and agility and many developers rely on this
paradigm. At the end, Object oriented approach ends up with the classical "Spaghetti code". To avoid
this ending, a new approach was taken called "declarative programming". This is a big shift and we
shall try to focus on that before learning the tooling around drools.

the first set of tutorials called "drools tutorials" start from the base concepts (there are few and
is concentrated on the the core engine) up to more language concepts
the second set of tutorials called "BRMS tutorial" will introduce the the management tooling
(called workbench) around the lifecycle of the rule artifact. but the workbench also introduces
more artifacts type like decision table, rule templates, etc.. that will help when modeling
business requirements.
the third set of tutorials called "BRMS runtime tutorials" start to introduce possible software
architectures in a real project. In the examples parts, we will use ideas that we describe in that
part.

4
Drools tutorial

Drools tutorial
When working with drools, there are things you have to define :

1) The data model on which the rules applies. As we are working in the java language, a data model is
a set of java classes.

2) Rules which are going on one side (called left side or LHS) express constraints on the data model,
which here class name and their attributes and on the other side (called right side or RHS) which will
act on the facts which are in the working memory.

When you want to apply some rules on some data, you will instantiate a java class and give it to the
rule engine. This instance is called a Fact object.

To work with the rule engine, we have to instantiate a KieContainer. We will see later how to program
it. This kieContainer contains a set of rules. The KieContainer allows also to contain other types of
artifacts are is common to other components like jbpm and optoplanner.

When you want to work with drools, you have to get a KieSession from the KieContainer. This
KieSession will allow you to give facts to the rule engine and to ask to fire rules.

5
Installing Drools Tooling

Installing Drools Tooling


Installing Drools Tooling
To be able to work with drools, we need :
1) Java Virtual machine version 8
2) Eclipse IDE
3) download drools runtime and tools

Install Java Virtual Machine


Drools is working with either Oracle Java machine or OpenJDK. With the drools version we have, we
can have java version 7 or 8.

Install Eclipse IDE


After installing java, we can install eclipse.

Download drools tooling


To be able to use drools in eclipse, we need to download the Drools and jbpm Tools.

You have to unzip the file. In the directory droolsjbpm-tools-distribution-


6.5.0.Final/binaries/org.drools.updatesite/ is the update site from where you have to install new
Software.

Create you first project


Go to File/New/Other

6
Installing Drools Tooling

then push on next

7
Installing Drools Tooling

Select the middle button and push next

8
Installing Drools Tooling

Enter "FirstProject" as a project name and keep only the "Add a sample Helloworld rule file to this
project". the generated project should look like that:

9
Installing Drools Tooling

If you do with the right mouse on DroolsTest class and run as "java Program", the following should
be displayed on the console view

All is well installed.

10
Data Model used in the tutorial

Data Model used in the tutorial


Data Model used in the tutorial
We are first going to write java code that we are going to use through all the drooks tutorial.
Here is the model we are going to use (taken from presentation done during conferences by drools
members)
We are in a bank that handles accounts (2) and on each account there can be movements (1). The
purpose is to calculate the account balance between an accounting period (3) of all accounts given the
movements it has.
We will run all examples in junit Tests.
This data model is the same as you can find in the reference model in the drools documentation.

Implement the pojo model


We have to create an AccountProject of type drools as previously describes.
Then we shall create a java package that we can name droolscours package in src/main/java (to
respect maven definition) by doing on src/main/java right click and new Package, gibe him a name
and push the finish button.

11
Data Model used in the tutorial

and we are going to create 3 java classes : Account, AccountingPeriod and CashFlow by right-
clicking on the new package just created and new Class

12
Data Model used in the tutorial

And the Account class, add two attributes accountno and balance.
Right click source/generate getter/setter and the Account class will have getter/setter.

13
Data Model used in the tutorial

Do the same for accounting period

and for CashFlow

14
Data Model used in the tutorial

Add JUnit library


To be able to use junit, we have to add the junit library.
Select the project, right click and select BuildPath/Configure BuildPath

15
Data Model used in the tutorial

Click on the Libraries tab and select the "Add library" button.

16
Data Model used in the tutorial

Select the Junit library and push the next button

17
Data Model used in the tutorial

On the next screen, push the Finish Button

and the the Ok button

18
Data Model used in the tutorial

The Junit libray is now part of the project

We now have to create some directory.


Select the src directory and right click button new Directory.

19
Data Model used in the tutorial

Enter test/java in the folder name and press finish.


Do the same but test/rules.
The folder part should look like that :

now Select src/test/java and right click buildpath/use as source folder

20
Data Model used in the tutorial

Do the same with src/test/rules


now the project should look like this :

Create a Helper Class


To simplify the writing of tests, we shall write a helper class.
To do, we first create a package called util and in there create a class that we shall all
KnowledgeSessionHelper

21
Data Model used in the tutorial

Then create a new class called KnowledgeSessionHelper

22
Data Model used in the tutorial

In the Class, the content should look like here

23
Data Model used in the tutorial

Create the first test case


Select the src/test/java package and Right click Other and type junit. Select Junit Test case

Push the next Button.


In the next screen, enter droolscours as package name and FirstTry as Name

24
Data Model used in the tutorial

And push the Finish Button

the code should be entered like this :

25
Data Model used in the tutorial

Select the open class, Right click and run as JUnit Test :

And the Junit Window should appear as above. (of course no rules were fired).
26
Data Model used in the tutorial

We are now ready to start the lessons.

27
Lesson 1 : Core concepts

Lesson 1 : Core concepts


First Rule Language Element
What is a rule ?
In the previously created project, we are going to add a new drl file (drl = drools rule language)
to do so, select src/test/rules and first create a package called lesson1 and the a rule file called
"lesson1".

and the select rule Drools/Rule Resource

28
Lesson 1 : Core concepts

Enter a rule name and package and press the Finish Button.

the following element is then displayed : lesson1.drl

29
Lesson 1 : Core concepts

Let's analyse the rule elements:

1. For every java object we are going to use, we need to import classes
2. It is possible to define global variables
3. Unique rule name
4. Rule conditions sometimes called LHS=Left Hand Side as it is on the left side of the RETE
algorithm which is included in drools
5. Rule action that is triggered when the rule conditions are met. It's possible to use pure java
code, this is even known as Right Hand Side (RHS).

Adding a simple condition to a rule


As any plugin in eclipse, the drools plugin allows auto completion (ctrl+space)

30
Lesson 1 : Core concepts

We create a rule the has a condition just a fact of type Account. If the rule is fired, then we shall show
the message "The account exists" in the console. Notice that the then part is just pure java.

Create a junit test called "testLesson1"

31
Lesson 1 : Core concepts

32
Lesson 1 : Core concepts

And then modify the test case to obtain this

and in src/main/resources/META-INF modify kmodule.xml


33
Lesson 1 : Core concepts

Here we defined a session called "ksession-rules" (that we use in the test to initialize the session) and
where to find the rule files here in package "lesson1".
Now you can run the test in TestLesson1 and you should see this:

It is the message that we have put in the rule "Your first rule"

Let us add some logging to facilate the output

Using a Global variable to log informations


In the code we just did, we wrote the code System.out.println("blabla"). This is fine but imagine you
want to log somewhere else, it is not possible.
One good practice is to use global variables for that purpose.
We first define a java class called OutputDisplay that we shall create in the util package in
src/main/java

Now we shall update the lesson1.drl file

34
Lesson 1 : Core concepts

The keyword global is used and then a normal java declaration. Here the global is of type
OutputDisplay and the variable is called showResults. This variable can now be used in the them part
of the rule as here.
To initialize the global variable, we shall use the method setGlobal on the session we create as shown
here in our test :

When running the junit test, the console should look like this :

Using callback to log activity in drools runtime


Up to known, we only defined one rule. It runs or not and if yes we added a method that shows us
something to see if the rule was executed.
In bigger project, adding logging code to each rule is not a good practice and will complexify the
35
Lesson 1 : Core concepts

writing of rules and furthermore we are pushing drools to be a business tool for business analyst and
they have to write technical code.
Drools offers a pattern to implement that functionnality that is called session callbacks.

Drools can be viewed in a synthetic picture like this :

The production memory contains all the rule definition (in our case the drl for the moment).
The working memory which created with the session and we can add facts to it with the method
insert.
The agenda which contains all the rules that can be fired.
The pattern Matcher which is the algorithm that is used to match the rules on the facts given. In drools
latest version, there are many different algorithm that are used (The main one is the rete algorithm).

On each of those parts, it is possible to add a callback when we create a Session.

Each time a fact is inserted,updated or retracted we shall log the event and show it on the console.
Here we are using the toString method of the java instance given (event.getObject().toString()).

We shall add a toString method to all our pojo classes Account, AccountingPeriod and CashFlow. To
do so we shall let eclipse generate it for us by right clicking in the source code

36
Lesson 1 : Core concepts

37
Lesson 1 : Core concepts

38
Lesson 1 : Core concepts

Then you should add the following unit test to the TestLesson1 java class

And in the console you should see this :

39
Lesson 1 : Core concepts

The first line is generated by the first test. To be able to see the difference when a new test is started,
we can add the following code to our JUnit test case :

and now the console should look like this :

The first test is passed and we see the output generated when the rule is fired.
The second test first inserts an objet, then updates it the retract it.
To update an object, you first have to memorize the fact handle :

FactHandle handlea = sessionStatefull.insert(a);

and then you have to use that facthandle to tell drools the object was updated :

a.setBalance(12.0);
sessionStatefull.update(handlea, a);

and the same applies when you want to retract (now called delete in drools 6) :

sessionStatefull.delete(handlea);

as we call fireAllRules() after we retract the only fact that was in the working memory, the rule "Your
First Rule revisited" is not fired.

When and how is a rule fired ?


40
Lesson 1 : Core concepts

Let us take an example :

We insert an Account, call FireAllRules and then call a second time fireAllRules on the same session.
What shall happen ? How many times the rule "Your First Rule revisited" shall be fired ?

Here is the result :

The rule is fired only once.


May be it is because we did not modify the Account object ?
Let us do another example that modifies the account object we pass to the the session.

Here is the result :

The rule is still not fired a second time. Just modifying the object does not do the job.

Let us modify the example like this by telling the rule engine we modified the fact :

41
Lesson 1 : Core concepts

Before calling the second fireAllRules, we tell drools that the fact we inserted before was updated. In
our case, we did not modify its data.
Here is the result :

The rule was executed a second time.

Here is what is happening when the FireAllRules method is called on a statefull session :

drools will look at all rules that can apply and put it in its agenda.
drools will execute the rule that is on top of its agenda
Once fired, the rule will be deactivated
We have to tell drools of a state change in one of facts in the when part (lhs) to make him
reconsidering the rule.
A state change can be an insert, update or delete (retract).

In the last example, we tell drools that a fact has been updated

sessionStatefull.update(handle, a);

So therefore, considering the previously inserted fact has been updated, drools reconsiders the rule.
As in the rule "Your First Rule revisited" there are no condition on the attributes, the rule is then fired.

But we can also do that in the then (RHS) part of a rule : insert, update, retract.

Here is the test case :

42
Lesson 1 : Core concepts

Here are the concerned rules.

in the first rule, in the then part we create a new instance of time AccountingPeriod and we use the
keyword insert to tell drools to create a new fact.
As a concequence, the second rule will be executed as the only condition is there is an
AccountingPeriod in the working memory.

We see in the logs

43
Lesson 1 : Core concepts

two first line : an object of type CashFlow was inserted. We did that from the Junit test with the
code
FactHandle handlea = sessionStatefull.insert(a);

the third line is generated in the then part of the rule "Your First Rule revisited
AccountingPeriod".

The fourth and fifth line : an object of type AccountingPeriod was inserted. This was done in
the then part of the rule "Your First Rule revisited AccountingPeriod"

AccountingPeriod newPeriod = new AccountingPeriod();


insert (newPeriod);

the last line is coming from the then part of the rule "Rule on AccountingPeriod that are
inserting". Meaning the insered object AccountingPeriod we inserted in the previous rule
triggered the second rule.

Summary
This ends the first lesson where we learned the core concepts needed when working with drools :

1. What is a rule
2. A rule with a simple condition
3. How to log what is happening in the rule engine
4. What triggers a rule execution and how to interact with the rule engine for fact handling.

44
Lesson 2 : binding

Lesson 2 : binding
Relating facts and attributes to each other in a
rule
Adding callback initialization and more
To be able to see what is happening in the rule engine, we shall add to the KnowledgeSessionHelper
this method

public static KieSession getStatefulKnowledgeSessionWithCallback(


KieContainer kieContainer, String sessionName) {
KieSession session = getStatefulKnowledgeSession(kieContainer, session
session.addEventListener(new RuleRuntimeEventListener() {
public void objectInserted(ObjectInsertedEvent event) {
System.out.println("Object inserted \n"
+ event.getObject().toString());
}
public void objectUpdated(ObjectUpdatedEvent event) {
System.out.println("Object was updated \n"
+ "new Content \n" + event.getObject().toString());
}
public void objectDeleted(ObjectDeletedEvent event) {
System.out.println("Object retracted \n"
+ event.getOldObject().toString());
}
});
session.addEventListener(new AgendaEventListener() {
public void matchCreated(MatchCreatedEvent event) {
System.out.println("The rule "
+ event.getMatch().getRule().getName()
+ " can be fired in agenda");
}
public void matchCancelled(MatchCancelledEvent event) {
System.out.println("The rule "
+ event.getMatch().getRule().getName()
+ " cannot b in agenda");
}
public void beforeMatchFired(BeforeMatchFiredEvent event) {
System.out.println("The rule "
+ event.getMatch().getRule().getName()
+ " will be fired");
}
public void afterMatchFired(AfterMatchFiredEvent event) {
System.out.println("The rule "
+ event.getMatch().getRule().getName()
+ " has be fired");
}

45
Lesson 2 : binding

public void agendaGroupPopped(AgendaGroupPoppedEvent event) {


}
public void agendaGroupPushed(AgendaGroupPushedEvent event) {
}
public void beforeRuleFlowGroupActivated(RuleFlowGroupActivatedEve
}
public void afterRuleFlowGroupActivated(RuleFlowGroupActivatedEven
}
public void beforeRuleFlowGroupDeactivated(RuleFlowGroupDeactivate
}
public void afterRuleFlowGroupDeactivated(RuleFlowGroupDeactivated
}
});

In the CashFlow class, we should add the following toString method

import java.text.DateFormat;

public class CashFlow {


public static int CREDIT = 1;
public static int DEBIT = 2;

@Override
public String toString() {
// TODO Auto-generated method stub
StringBuffer buff = new StringBuffer();
buff.append("-----CashFlow-----)\n");
buff.append("Account no=" + this.accountNo + "\n");
if (this.mvtDate != null) {
buff.append("Mouvement Date= "
+ DateFormat.getDateInstance().format(this.mvtDate)
+ "\n");
} else {
buff.append("No Mouvement date was set\n");
}
buff.append("Mouvement Amount=" + this.amount + "\n");
buff.append("-----CashFlow end--)");
return buff.toString();
}

in the util package, we shall create a DateHelper class that will look like this :

package util;

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

public class DateHelper {


public static String sFormat = "yyyy-MM-dd";

public static Date getDate(String sDate) throws Exception {


SimpleDateFormat sdf = new SimpleDateFormat(sFormat);
return sdf.parse(sDate);
46
Lesson 2 : binding

public static Date getDate(String sDate, String anotherFormat)


throws Exception {
SimpleDateFormat sdf = new SimpleDateFormat(anotherFormat);
return sdf.parse(sDate);
}
}

In the kmodule.xml, make it look like that

<?xml version="1.0" encoding="UTF-8"?>


<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
<kbase name="rules" packages="lesson1">
<ksession name="ksession-rules"/>
</kbase>
<kbase name="rules2" packages="lesson2">
<ksession name="ksession-lesson2"/>
</kbase>
</kmodule>

in the src/test/rules, create a package lesson2 and a rule resource named lesson2.drl and should look
like this :

package droolscours
//list any import classes here.
import droolscours.AccountingPeriod;
import droolscours.CashFlow;
import droolscours.Account;
import util.OutputDisplay;
global OutputDisplay showResults;

rule "Your First Rule revisited again"

when
Account( )
then
showResults.showText("The account exists");
end

package droolscours;

import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.kie.api.event.rule.ObjectDeletedEvent;
import org.kie.api.event.rule.ObjectInsertedEvent;
import org.kie.api.event.rule.ObjectUpdatedEvent;
import org.kie.api.event.rule.RuleRuntimeEventListener;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.StatelessKieSession;
47
Lesson 2 : binding

import org.kie.api.runtime.rule.FactHandle;
import util.KnowledgeSessionHelper;

@SuppressWarnings("restriction")
public class TestLesson2 {
static KieContainer kieContainer;
StatelessKieSession sessionStateless = null;
KieSession sessionStatefull = null;

@BeforeClass
public static void beforeClass(){
kieContainer=KnowledgeSessionHelper.createRuleBase();
}

@Before
public void setUp() throws Exception{
System.out.println("------------Before------------");
}
@After
public void tearDown() throws Exception{
System.out.println("------------After------------");
}

@Test
public void testdeuxFait1() {
sessionStatefull = KnowledgeSessionHelper
.getStatefulKnowledgeSessionWithCallback(kieContainer,"ksessio

OutputDisplay display = new OutputDisplay();


sessionStatefull.setGlobal("showResults", display);
Account a = new Account();
sessionStatefull.insert(a);
AccountingPeriod period = new AccountingPeriod();
sessionStatefull.insert(period);
sessionStatefull.fireAllRules();
}
}

To see if it runs, select the TestLesson2 class, right click and run as Junit Test and the console should
look like this :

So now
48
Lesson 2 : binding

we know all is working we can continue our lesson.

Test Case
We are going to implement a test case with the following data : 1) Account with accountno=1 2) an
Accounting period going from January first 2016 to march 31th 2016 3) 3 Cash Flow movements,
credit 1000$ January 15th 2016, debit 500$ February 15th 2016 and a credit movement April 15th
2016 of 1000$.

The result should be a balance of 500$ for the accounting period.

Fact binding
We want to update the account balance for each CashFlow. We first put the CashFlow and select all
CashFlow of type CREDIT. To do so, we add a constraint on the type attribute of java class

CashFlow. Then we add a second constraint of


type Account. The question is now how in the then part do the balance update. It is where the fact
binding comes in :

package droolscours
//list any import classes here.
import droolscours.AccountingPeriod;
import droolscours.CashFlow;
import droolscours.Account;
import util.OutputDisplay;

global OutputDisplay showResults;

rule "Credit rule"

when
$cash :CashFlow(type == CashFlow.CREDIT )
$acc : Account( )
then
$acc.setBalance($acc.getBalance()+$cash.getAmount());
showResults.showText("Account no "+$acc.getAccountNo()+ " has now a
end

In front of the fact, we declare variables $cash and $acc like shown above and then those variables
can be used in the then part as normal java variables. As shown above we can then update the balance.
49
Lesson 2 : binding

package droolscours;

import util.OutputDisplay;

import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.StatelessKieSession;

import org.junit.Assert;
import util.DateHelper;
import util.KnowledgeSessionHelper;

@SuppressWarnings("restriction")
public class TestLesson2 {
static KieContainer kieContainer;
StatelessKieSession sessionStateless = null;
KieSession sessionStatefull = null;

@BeforeClass
public static void beforeClass(){
kieContainer=KnowledgeSessionHelper.createRuleBase();
}
@Before
public void setUp() throws Exception{
System.out.println("------------Before------------");
}
@After
public void tearDown() throws Exception{
System.out.println("------------After------------");
}

@Test
public void testTwoFacts() {
sessionStatefull = KnowledgeSessionHelper
.getStatefulKnowledgeSessionWithCallback(kieContainer,"kse
OutputDisplay display = new OutputDisplay();
sessionStatefull.setGlobal("showResults", display);
Account a = new Account();
a.setAccountNo(1);
a.setBalance(0);
sessionStatefull.insert(a);
CashFlow cash1 = new CashFlow();
cash1.setAccountNo(1);
cash1.setAmount(1000);
cash1.setType(CashFlow.CREDIT);
sessionStatefull.insert(cash1);
sessionStatefull.fireAllRules();
Assert.assertEquals(a.getBalance(), 1000,0);
50
Lesson 2 : binding

}
}

In the TestTwoFacts test we insert an object of type Account and CashFlow mouvement of type
credit. At the end, the balance should be 1000$. the console should look like this when running the
test :

Attribute binding
Let us make now an example with a second CashFlow movement. Here is the test case :

@Test
public void testTwofactsTwocashFlowMovement() throws Exception {
sessionStatefull = KnowledgeSessionHelper
.getStatefulKnowledgeSessionWithCallback(kieContainer,"ksessio
OutputDisplay display = new OutputDisplay();
sessionStatefull.setGlobal("showResults", display);
Account a = new Account();
a.setAccountNo(1);
a.setBalance(0);
sessionStatefull.insert(a);
CashFlow cash1 = new CashFlow();
cash1.setAccountNo(1);
cash1.setAmount(1000);
cash1.setMvtDate(DateHelper.getDate("2010-01-15"));
cash1.setType(CashFlow.CREDIT);
sessionStatefull.insert(cash1);
CashFlow cash2 = new CashFlow();
cash2.setAccountNo(2);
cash2.setAmount(1000);
cash2.setMvtDate(DateHelper.getDate("2010-01-15"));
cash2.setType(CashFlow.CREDIT);

51
Lesson 2 : binding

sessionStatefull.insert(cash2);
sessionStatefull.fireAllRules();
Assert.assertEquals(a.getBalance(), 1000,0);
}

When we run the test case, the test fails :

and in the console, we see that the rule "Credit rule" is running twice :

The rule was fired


twice because there are 2 (CashFlow,Account) couples. The first CashFlow mouvement concerns
account number 1 and the seconde account number 2. We need to find a way to link the two facts. We
shall modify the rule like this :

package droolscours
//list any import classes here.
import droolscours.AccountingPeriod;
import droolscours.CashFlow;
import droolscours.Account;
import util.OutputDisplay;

global OutputDisplay showResults;


rule "Credit rule"
when
$cash :CashFlow($accno : accountNo ,type == CashFlow.CREDIT )
$acc : Account( accountNo ==$accno )
then
$acc.setBalance($acc.getBalance()+$cash.getAmount());
showResults.showText("Account no "+$acc.getAccountNo()+ " has now a
end

52
Lesson 2 : binding

The rule above uses a binding variable. We create an attribute variable called $accno on the attribute
accountNo. We can define now a binding on the attribute accountNo of fact Account.

Now the
rule "Credit Rule" is only fired once and the balance is corret.

Calculating balance
Now we know how to link facts and use attribute variable via binding attributes to use them as
constraint, we shall modify the credit rule and create a debit rule :

package cours

import droolscours.Account;
import droolscours.AccountingPeriod;
import droolscours.CashFlow;
import droolscours.util.OutputDisplay;

global OutputDisplay showResult;

rule "Credit rule"

when
$cash :CashFlow( $aDate : mvtDate, $no : accountNo ,type == CashFlo
$acc : Account(accountNo ==$no )
$period : AccountingPeriod( startDate <= $aDate && endDate >= $aDa
then
$acc.setBalance($acc.getBalance()+$cash.getAmount());
showResults.showText("Account no "+$acc.getAccountNo()+ " has now a
end
53
Lesson 2 : binding

rule "Debit rule"

when
$cash :CashFlow( $aDate : mvtDate, $no : accountNo ,type == CashFlow.D
$acc : Account(accountNo ==$no )
$period : AccountingPeriod( startDate <= $aDate && endDate >= $aDate)
then
$acc.setBalance($acc.getBalance()-$cash.getAmount());
showResults.showText("Account no "+$acc.getAccountNo()
+ " has now a balance of "+$acc.getBalance());
end

In the rule above, we add a constraint so that the mvtDate of the CashFlow is between the startDate
and endDate of the AccountinPeriod.

and the test case

@Test
public void testcalculateBalance() throws Exception {
sessionStatefull = KnowledgeSessionHelper
.getStatefulKnowledgeSessionWithCallback(kieContainer, "ksessi
OutputDisplay display = new OutputDisplay();
sessionStatefull.setGlobal("showResults", display);
Account a = new Account();
a.setAccountNo(1);
a.setBalance(0);
sessionStatefull.insert(a);
CashFlow cash1 = new CashFlow();
cash1.setAccountNo(1);
cash1.setAmount(1000);
cash1.setMvtDate(DateHelper.getDate("2016-01-15"));
cash1.setType(CashFlow.CREDIT);
sessionStatefull.insert(cash1);
CashFlow cash2 = new CashFlow();
cash2.setAccountNo(1);
cash2.setAmount(500);
cash2.setMvtDate(DateHelper.getDate("2016-02-15"));
cash2.setType(CashFlow.DEBIT);
sessionStatefull.insert(cash2);
CashFlow cash3 = new CashFlow();
cash3.setAccountNo(1);
cash3.setAmount(1000);
cash3.setMvtDate(DateHelper.getDate("2016-04-15"));
cash3.setType(CashFlow.CREDIT);
sessionStatefull.insert(cash3);
AccountingPeriod period = new AccountingPeriod();
period.setStartDate(DateHelper.getDate("2016-01-01"));
period.setEndDate(DateHelper.getDate("2016-03-31"));
sessionStatefull.insert(period);
sessionStatefull.fireAllRules();
Assert.assertTrue(a.getBalance()==500);
}

54
Lesson 2 : binding

As expected, the "Credit


Rule" is fired once and the "Debit Rule" is fired once also. The CashFlow movement of April 15 2016
is ignored as it does not fulfill the constraints.

Summary
In lesson 1, we learned the dynamic of the rule engine : how and when are the rules fired. In the
current lesson, we started to link together conditions between facts and how to interact with the facts
in the rule execution. In the next lesson, we will see how to express more complex constraints.

55
lesson 3 : Some more drools languages

lesson 3 : Some more drools languages


Adding more constraints in facts
We concentrate on how the rule engine works in the first lesson. In the second lesson, we introduced
how to express constraint between facts.
In this lesson, we will concentrate on all the drools language possibilities to write constraints on facts
for more complex cases.
The reader has to create a test classes called TestLesson3 like for lesson2, a new package lesson3 in
the src/test/rules and add in the kmodule.xml a new session declaration

<kbase name="rules3" packages="lesson3">


<ksession name="ksession-lesson3"/>
</kbase>

While building the examples, you will see more rules fired than the shown examples. As drools is a
declarative language, as soon as the constraint are satisfied, the rule can fire.

Some more classes


To be able to see some more advanced features, we are going to add 2 new classes in src/main/java
droolscours package.

package droolscours;

public class Customer {


private String name;
private String surname;
private String country;

public Customer(String name, String surname, String country) {


super();
this.name = name;
this.surname = surname;
this.country = country;
}

public Customer() {
super();
// TODO Auto-generated constructor stub
}

public String getCountry() {


return country;
}

public void setCountry(String country) {


this.country = country;
}

56
lesson 3 : Some more drools languages

public String getName() {


return name;
}

public void setName(String name) {


this.name = name;
}

public String getSurname() {


return surname;
}

public void setSurname(String surname) {


this.surname = surname;
}

@Override
public String toString() {
StringBuffer buff = new StringBuffer();
buff.append("-----Customer-----)\n");
buff.append("Name=" + this.name + "\n");
buff.append("Surname Name=" + this.surname + "\n");
buff.append("Country=" + this.country + "\n");
buff.append("-----Customer end-)");
return buff.toString();
}

package droolscours;

public class PrivateAccount extends Account {


private Customer owner;

public Customer getOwner() {


return owner;
}

public void setOwner(Customer owner) {


this.owner = owner;
}

@Override
public String toString() {
StringBuffer buff = new StringBuffer();
buff.append("-----Private Account-)");
buff.append(super.toString());
if (this.owner != null) {
buff.append(this.owner.toString());
}
buff.append("-----Private Account end-)");
return buff.toString();
}
57
lesson 3 : Some more drools languages

In Constraint
This allows to validate an attribute is a list of values

package cours

//#list any import classes here.


import droolscours.CashFlow;
import util.OutputDisplay;
//#declare any global variables here
global OutputDisplay showResult;

rule "The cashFlow can be a credit or a debit"

when
$cash :CashFlow(type in ( CashFlow.DEBIT,CashFlow.CREDIT) )

then
showResult.showText("The cashFlow is a credit or a debit");
end

@Test
public void testInConstrait() throws Exception {
sessionStatefull = KnowledgeSessionHelper
.getStatefulKnowledgeSessionWithCallback(kieContainer, "ksessi
OutputDisplay display = new OutputDisplay();
sessionStatefull.setGlobal("showResult", display);
CashFlow cashFlow = new CashFlow();
cashFlow.setType(CashFlow.CREDIT);
sessionStatefull.insert(cashFlow);
sessionStatefull.fireAllRules();
} }

And the console should look as follows :

Nested Accessor
This allows to add a constraint to a attribute class without the need to add the linked object to the
58
lesson 3 : Some more drools languages

session.

rule "Accessor"
when
$cash :PrivateAccount( owner.name =="Héron" )
then
showResult.showText("Account is owned by Héron");
end

@Test
public void testNestedAccessor() throws Exception {
sessionStatefull = KnowledgeSessionHelper
.getStatefulKnowledgeSessionWithCallback(kieContainer, "ksessi
OutputDisplay display = new OutputDisplay();
sessionStatefull.setGlobal("showResult", display);
Customer customer = new Customer();
customer.setName("Héron");
customer.setSurname("Nicolas");
PrivateAccount pAccount = new PrivateAccount();
pAccount.setOwner(customer);
sessionStatefull.insert(pAccount);
sessionStatefull.fireAllRules();
}

As seen here, we do not add the customer instance to the drools session.

The rule has been fired.

And/or
It is possible to do constraints on attribute like in java.

rule "infixAnd"
when
( $c1 : Customer ( country=="GB") and PrivateAccount( owner==$c1))
or
( $c1 : Customer (country=="US") and PrivateAccount( owner==$c1))
then
showResult.showText("Person lives in GB or US");
end

@Test
59
lesson 3 : Some more drools languages

public void testInOrFact() throws Exception {


sessionStatefull = KnowledgeSessionHelper
.getStatefulKnowledgeSessionWithCallback(kieContainer, "ksessi
OutputDisplay display = new OutputDisplay();
sessionStatefull.setGlobal("showResult", display);
Customer customer = new Customer();

customer.setCountry("GB");
sessionStatefull.insert(customer);
PrivateAccount pAccount = new PrivateAccount();
pAccount.setOwner(customer);
sessionStatefull.insert(pAccount);
sessionStatefull.fireAllRules();
}

not
This allows to test if no fact of a type is in the session.

rule "no customer"


when
not Customer( )
then
showResult.showText("No customer");
end

@Test
public void testNotCondition() throws Exception {
sessionStatefull = KnowledgeSessionHelper
.getStatefulKnowledgeSessionWithCallback(kieContainer, "ksessi
OutputDisplay display = new OutputDisplay();
sessionStatefull.setGlobal("showResult", display);
sessionStatefull.fireAllRules();
}

60
lesson 3 : Some more drools languages

exist
On the contrary of previous syntax, this allows to test if there at least one fact type is in the session.

rule "Exists"
when
exists Account( )
then
showResult.showText("Account exists");
end

@Test
public void testExistsCondition() throws Exception {
sessionStatefull = KnowledgeSessionHelper
.getStatefulKnowledgeSessionWithCallback(kieContainer, "ksessi
OutputDisplay display = new OutputDisplay();
sessionStatefull.setGlobal("showResult", display);
Account pAccount = new Account();
sessionStatefull.insert(pAccount);
Customer c = new Customer();
sessionStatefull.insert(c);
sessionStatefull.fireAllRules();
}

ForAll
We would like to verify that every cashflow instance is linked to an Account instance.

61
lesson 3 : Some more drools languages

rule "ForAll"
when
forall ( Account( $no : accountNo )
CashFlow( accountNo == $no)
)
then
showResult.showText("All cashflows are related to an Account ");
end

In this rule, in the forall condition, we link the CashFLow instance to the Account instance.
We are going to do a test case where all objects are related

@Test
public void testForALl() throws Exception {
sessionStatefull = KnowledgeSessionHelper
.getStatefulKnowledgeSessionWithCallback(kieContainer, "ksessi
OutputDisplay display = new OutputDisplay();
sessionStatefull.setGlobal("showResult", display);
Account a = new Account();
a.setAccountNo(1);
a.setBalance(0);
sessionStatefull.insert(a);
CashFlow cash1 = new CashFlow();
cash1.setAccountNo(1);

sessionStatefull.insert(cash1);
CashFlow cash2 = new CashFlow();
cash2.setAccountNo(1);

sessionStatefull.insert(cash2);
Account a2 = new Account();
a2.setAccountNo(2);
a2.setBalance(0);
sessionStatefull.insert(a2);
CashFlow cash3 = new CashFlow();
cash3.setAccountNo(2);
sessionStatefull.insert(cash3);
sessionStatefull.fireAllRules();
}

When running the test, you should see the following logging.

Just modify the test case by updating the test case :

sessionStatefull.insert(cash2);
Account a2 = new Account();
a2.setAccountNo(2);
a2.setBalance(0);
sessionStatefull.insert(a2);
62
lesson 3 : Some more drools languages

CashFlow cash3 = new CashFlow();


cash3.setAccountNo(1);
sessionStatefull.insert(cash3);
sessionStatefull.fireAllRules();

When you run the test case, the rule ForAll will not be fired.

From
It is sometimes needed to access data from outside the drools session.
As it is not possible to insert all objects in the session, we can use the from instruction in the when
part.

First let us create a CustomerService class in package Droolscours.sercice

package droolscours.service;

import droolscours.Customer;

import java.util.ArrayList;
import java.util.List;

public class CustomerService {

public List<Customer> getListCustomer() {


List<Customer> result = new ArrayList<Customer>();
result.add(new Customer("Héron", "Nicolas", "Fr"));
result.add(new Customer("Héron", "James", "GB"));
result.add(new Customer("Héron", "Nicolas", "GB"));
return result;
}

then we shall create the rule that uses the from

// add import for the service


import droolscours.service.CustomerService;
// add the global
global CustomerService serviceCustomer;

rule "FromCondition"
when
$c : Customer()
$cc : Customer(name ==$c.name,surname==$c.surname,country !=$c.country
then
showResult.showText("Found same customer in 2 countries");
end

and the following test case :

@Test
public void testFromLHS() throws Exception {
sessionStatefull = KnowledgeSessionHelper.getStatefulKnowledgeSessionW
63
lesson 3 : Some more drools languages

"ksession-lesson3");
OutputDisplay display = new OutputDisplay();
sessionStatefull.setGlobal("showResult", display);
sessionStatefull.setGlobal("serviceCustomer", new CustomerService());
Customer c = new Customer("Héron", "Nicolas", "A");
sessionStatefull.insert(c);
sessionStatefull.fireAllRules();
}

The rule is fired twice as in the service there are two customers with the same name and with a
different country.

Collecting
The purpose is to collect a set of fact and constraint if the constraints are true. Let us see the following
example int the rule "More then 2 CashFlow Line". in this rule, we want to collect all CashFlow that
are in the correct time period and the good account number. The "from collect" syntax returns an
arrayList. It is possible to add a condition as in the first rule where we add a constraint that we expect
at least 2 items. In the second rule, we do not add this constraint.

rule "More then 2 CashFlow Line"


when
$c : Account( $acc : accountno )
$p : AccountingPeriod ($sDate : startDate ,$eDate : endDate )
$number : ArrayList(size >= 2 )
from collect( CashFlow( mvtDate >= $sDate && mvtDate <= $eDate,

then
showResult.showText("Found more than 2 CashFlow Lines");
showResult.showText("<<<<<<<<<<");
for (Object ff : $number){
showResult.showText(ff.toString());
}
showResult.showText(">>>>>>>>>>>>>>>>");
end

rule "Numbers of CashFlow Line"


when
$c : Account( $acc : accountno )
$p : AccountingPeriod ($sDate : startDate ,$eDate : endDate )
$number : ArrayList( )
from collect( CashFlow( mvtDate >= $sDate && mvtDate <= $eDate,

then
showResult.showText("Found "+$number+" more than 2 CashFlow Lines");
end
64
lesson 3 : Some more drools languages

You may need to add constructors in the CashFlow and AccountingPeriod classes :

public AccountingPeriod() {
}

public AccountingPeriod(Date startDate, Date endDate) {


super();
this.startDate = startDate;
this.endDate = endDate;
}

public CashFlow() {
super();
}
public CashFlow(Date mvtDate, double amount, int type, long accountNo) {
super();
this.mvtDate = mvtDate;
this.amount = amount;
this.type = type;
this.accountNo = accountNo;
}

Here is our test case :

@Test
public void testCollecting() throws Exception {
sessionStatefull = KnowledgeSessionHelper.getStatefulKnowledgeSessionW
"lesson34-session");
OutputDisplay display = new OutputDisplay();
sessionStatefull.setGlobal("showResult", display);
Account a = new Account();
a.setAccountNo(1);
a.setBalance(0);
sessionStatefull.insert(a);
sessionStatefull.insert(new CashFlow(DateHelper.getDate("2010-01-15"),
sessionStatefull.insert(new CashFlow(DateHelper.getDate("2010-02-15"),
sessionStatefull.insert(new CashFlow(DateHelper.getDate("2010-04-15"),
sessionStatefull
.insert(new AccountingPeriod(DateHelper.getDate("2010-01-01"),
sessionStatefull.fireAllRules();
}

65
lesson 3 : Some more drools languages

Accumulating
In the previous section, we collect data. There is an "from accumulate" that allows us to sum data in
one command.
the "from collect" instruction takes 5 parameters :
1) a fact constraint expression
2) an init condition
3) the instruction when the rule applies to the fact constraint expression
4) the reverse action when the fact constraint expression is not true anymore
5) The result of the accumulate

Here is our example :

rule "Credit and Debit Rule"


when
$c : Account( $acc : accountno )
$p : AccountingPeriod ($sDate : startDate ,$eDate : endDate )
$totalCredit : Number( doubleValue > 100 )
from accumulate( CashFlow( type ==CashFlow.CREDIT,$value : amount
init( double total = 0; ),
action( total += $value; ),
reverse( total -= $value; ),
66
lesson 3 : Some more drools languages

result( total ) )
$totalDebit : Number( doubleValue > 100 )
from accumulate( CashFlow( type ==CashFlow.DEBIT,$value : amount,
init( double total = 0; ),
action( total += $value; ),
reverse( total -= $value; ),
result( total ) )

then
showResult.showText(" Found "+$totalCredit+" as a credit");
showResult.showText(" Found "+$totalDebit+" as a debit");
end

The constraint here is on a fact type CashFlow with the constraints that we already used before (good
account number and the good date period and it should be a credit or a debit)
Then the initial condition, we initialize a double value we call total. Then in the action/reverse, we
add to the total the amount in the CashFlow that we get by using an attribute binding. In the result
action we put the total we calculated.

@Test
public void testAccumulate() throws Exception {
sessionStatefull = KnowledgeSessionHelper.getStatefulKnowledgeSessionW
OutputDisplay display = new OutputDisplay();
sessionStatefull.setGlobal("showResult", display);
sessionStatefull.insert(new Account(1,0));

FactHandle fa = sessionStatefull.insert(new CashFlow(DateHelper.getDat


sessionStatefull.insert(new CashFlow(DateHelper.getDate("2010-02-15"),
sessionStatefull.insert(new CashFlow(DateHelper.getDate("2010-04-15"),
sessionStatefull.insert(new AccountingPeriod(DateHelper.getDate("2010-
sessionStatefull.fireAllRules();
sessionStatefull.delete(fa);
sessionStatefull.fireAllRules();
}

Summary
This lesson was an introduction to the main drools language syntax that are needed for starting a
drools project.
In the next lesson, we will start learning the ruleflow concept that we encourage to use in drools
projects.

67
lesson 4 : ruleflow

lesson 4 : ruleflow
Organizing rule execution for bigger projects
Why do we need a Ruleflow ?
When capturing business requirements, most users express the rules by dividing the problem to
resolve in steps. As this, it is very convenient to be able to implement it the same way. In the
drools/jbpm technology, we are going to use a jbpm process by using rule steps, it is then called a rule
flow. But in reality, we can mix jbpm/drools together.

Configure project to become a jbpm project


select the project, right click with the mouse configure/convert to jbpm project

Create your first rule flow


in the src/test/rules create a package lesson4. Right click and create new File and call it
ruleflow1.bpmn2 and click the OK button. An error messages appear to say the file is empty but the
plugin will create a start event.

68
lesson 4 : ruleflow

put two Rule tasks and one End Event.

Then select each of the Rule set and set the properties as follows :

The workflow should look like that :

Select the workflow in the rear, and in the properties file, change as follows :

69
lesson 4 : ruleflow

Create a new rule file called demo-ruleflow1.drl

package cours

import droolscours.Account;
import droolscours.AccountingPeriod;
import droolscours.CashFlow;
import util.OutputDisplay;

global OutputDisplay showResult;

rule "Account group1"


ruleflow-group "Group1"
when
Account( )
then
showResult.showText("Account in Group1");
end
rule "Account group2"
ruleflow-group "Group2"
when
Account( )
then
showResult.showText("Account in Group2");
end

Do not forget to add a new entry in the kmodule.xml

<kbase name="rules4" packages="lesson4">


<ksession name="ksession-lesson4"/>
</kbase>

Look at the keyword "ruleflow-group". Here the first rule we give it the name "Group1" and the
second "Group2". they have the same name as the Rule-flow items we defined in the process
definition above. Therefor, the first rule can only be fired when the rule-flow group "Group1" is
activated and the same for the second rule and rule-flow group "Group2". Before running a test case,
we will add a new callback to know the activities around the jbpm process.

public static KieSession getStatefulKnowledgeSessionForJBPM(


KieContainer kieContainer, String sessionName) {
KieSession session = getStatefulKnowledgeSessionWithCallback(kieCont
session.addEventListener(new ProcessEventListener() {

@Override
public void beforeVariableChanged(ProcessVariableChangedEvent ar
// TODO Auto-generated method stub
70
lesson 4 : ruleflow

@Override
public void beforeProcessStarted(ProcessStartedEvent arg0) {
System.out.println("Process Name "+arg0.getProcessInstance()

@Override
public void beforeProcessCompleted(ProcessCompletedEvent arg0) {
// TODO Auto-generated method stub

@Override
public void beforeNodeTriggered(ProcessNodeTriggeredEvent arg0)
// TODO Auto-generated method stub

@Override
public void beforeNodeLeft(ProcessNodeLeftEvent arg0) {
if (arg0.getNodeInstance() instanceof RuleSetNodeInstance){
System.out.println("Node Name "+ arg0.getNodeInstance().
}

@Override
public void afterVariableChanged(ProcessVariableChangedEvent arg
// TODO Auto-generated method stub

@Override
public void afterProcessStarted(ProcessStartedEvent arg0) {

@Override
public void afterProcessCompleted(ProcessCompletedEvent arg0) {
System.out.println("Process Name "+arg0.getProcessInstance()

@Override
public void afterNodeTriggered(ProcessNodeTriggeredEvent arg0) {
if (arg0.getNodeInstance() instanceof RuleSetNodeInstance){
System.out.println("Node Name "+ arg0.getNodeInstance().
}
71
lesson 4 : ruleflow

@Override
public void afterNodeLeft(ProcessNodeLeftEvent arg0) {
}
});
return session;
}

Note that we are only looking for Node of type Rule Step called RuleSetNodeInstance. And the test
case looks like this :

package droolscours;
import util.OutputDisplay;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import util.KnowledgeSessionHelper;

@SuppressWarnings("restriction")
public class TestLesson4 {
static KieContainer kieContainer;
KieSession sessionStatefull = null;

@BeforeClass
public static void beforeClass() {
kieContainer = KnowledgeSessionHelper.createRuleBase();
}
@Before
public void setUp() throws Exception {
System.out.println("------------Before------------");
}
@After
public void tearDown() throws Exception {
System.out.println("------------After ------------");
}

@Test
public void testRuleFlow1() {
sessionStatefull = KnowledgeSessionHelper
.getStatefulKnowledgeSessionForJBPM(kieContainer, "ksession-le
OutputDisplay display = new OutputDisplay();
sessionStatefull.setGlobal("showResult", display);
Account a = new Account();
sessionStatefull.insert(a);
sessionStatefull.startProcess("RF1");
sessionStatefull.fireAllRules();
}
}

72
lesson 4 : ruleflow

Before calling the fireAllRules method, we call a startProcess method with the "RF1" parameter
which is the ID we gave to the process above.

And the console display should be like this :

How a rule-group works ?

A rule flow group works like a separate group of rules. Those who are setting the focus when the rule
step is called with the same node id as the ruleflow-group. When no more rules can be fired, the
process can continue to the next node.

We can go further

starting a ruleflow from a rule

Let us write the following rule :

And here is the following test case :

@Test
public void testRuleFlow2() {
sessionStatefull = KnowledgeSessionHelper
.getStatefulKnowledgeSessionForJBPM(kieContainer, "ksession-le
OutputDisplay display = new OutputDisplay();
sessionStatefull.setGlobal("showResult", display);
Account a = new Account();
sessionStatefull.insert(a);
sessionStatefull.fireAllRules();
}

73
lesson 4 : ruleflow

Rule flow with a condition


It is also possible to execute certain ruleflow-group based on condition that can have the same syntax
as a rule constraint.

Create a new package in src/test/rule and calle it lesson4a. Create a new process file that you can call
demo-ruleflow2.bpmn2 and a rule file demo-ruleflow2.drl.

The bpmn process should look like this and give it the id "RF3":

The left split should be a "diverge Gateway" and the right one a "converge Gateway". The calculate1
should have a ruleflow called "group1" and the calculate2 "group2".

When clicking on the "diverge gateway", you should select the "OR" type and for the the "converge
Gateway" the "XOR".

74
lesson 4 : ruleflow

Now we have to edit each connection Here is for "to Node Calculate1". Do not forget to click the
"Imports" button to add the Account class.

"To Node Calculate2".

75
lesson 4 : ruleflow

here is the rule file :

package cours

//#list any import classes here.


import droolscours.Account;
import droolscours.AccountingPeriod;
import droolscours.CashFlow;
import util.OutputDisplay;

global OutputDisplay showResult;

rule "start process"


when
then
kcontext.getKieRuntime().startProcess("RF3");
end

rule "Account group1"


ruleflow-group "Group1"
when
Account(balance > 0 )
then
showResult.showText("Account in Group1 > 1000 ");

end
rule "Account group2"
ruleflow-group "Group2"
when
76
lesson 4 : ruleflow

Account( )
then
showResult.showText("Account in Group2 < 1000");

end

and the test case

@Test
public void testRuleFlow3() {
sessionStatefull = KnowledgeSessionHelper
.getStatefulKnowledgeSession(kieContainer, "ksession-lesson4a"
OutputDisplay display = new OutputDisplay();
sessionStatefull.setGlobal("showResult", display);
Account a = new Account();
a.setBalance(2500);
sessionStatefull.insert(a);
AccountingPeriod period = new AccountingPeriod();
sessionStatefull.insert(period);
sessionStatefull.fireAllRules();

if you change the balance to 500, the console should be :

It is more efficient to have two groups of rules


like this instead adding for all rules of 'Group1' the constraint on balance > 1000 and balance <= 1000
for "Group2". Indeed, if the level 1000$ changes you have to modify all rules. And furthermore, if the
end user gives your the rule : "first case is when balance is less than 1000$", then the good practice is
to implement business rules as they are given. And implementing with a ruleflow will help end users
to divide their way of expressing more complex rules. We will see int the exercise how it helps.

77
lesson 5 : Exercise

lesson 5 : Exercise
Cost transport calculator
The purpose of the exercise is to go through all steps of a real project when using a rule engine like
drools. The use case is simple enough to be realize withing one or two working days. We first expose
the business requirements like they may be expressed by users. Then we are going to design the
implementation we shall use. Feel free to modify the implementation if you think it is not how you
would have done. The only point of view you should keep is to use declarative programming versus
procedural code. It is very common for new comers to write a few rules and implement huge then part
of rules. It works. But you should remember that the implemented algorithms that are behind drools
will work best if you express a lot a pattern expressions (constraints) on java facts and if you insert
new fact that are the result of a rule. For example, in a loyalty system in retail industry, if a customer
is comming more than twice per month on the web site or in the shop, than he is a gold customer. this
is a fact that can be used in other rules as input. This is how the rule engine was thought to be used
and when you see business requirements it is how it is expressed naturally.

78
Requirements

Requirements
Requirements
We are working for a retail company that buys its products from different places in Asian mostly. each
shop can go to the buying web site and say what he wants and put an order.
Up to now, the transport cost was a estimated but not calculated on the content of the order.
The purpose is to implement such a calculator.

Data model
A customer puts an order that contains products. An order contains a lot of products with a number.
A product has

a name
a height,
a width,
a depth,
transport type : if the product can be put with other products in a pallet, individual(alone) or
bulk (like sand for example)
a weight

A product can be put on a pallet. a pallet 120 cm width and and 80 cm depth. It can be maximum 2
meters height and the weight should not exceed 1400 kg. We should use a simple algorithm to fill
each pallet. It will be not optimized but we should used that as a margin of the costs. a product that is
bigger than 60 cm in width or depth or higher than 1m should be put alone in a pallet.

All products start from the same city and go to the same city in an order. A trip is composed f steps.
Each step can be done by train, boat or truck.

Here is the list of products in our test order.

Product Transport type Number Height Weight Depth Weight


Drill pallet 1000 20cm 40cm 30cm 2 kg
Screwdriver pallet 30000 3cm 2cm 20cm 0,2 kg
Sand bulk 35 Tons
Gravel bulk 14 Tons
Furniture individual 23 500 kg

And the trip is a follows :

# start city arrival city distance (km) travel mode


1 Shangai Rotterdam 22000 Boat
2 Rotterdam Tournai 300 Train
3 Tournai Lille 20 Truck

There are 3 types of costs :

79
Requirements

transport costs per Pallet

Transport Cost
Boat 0,2€\/km
Train 0,5€\/km
Truck 1€\/km

Taxes

City Cost
Shangai 0,02€\/kg
Rotterdam 0,05€\/kg and 1€ per handling person
Tournai 2€ per handling person
Lille 30€ per handling person

handling

City Cost
Shangai 20€\/hour and a person can handle 13 pallets\/hour
Rotterdam 45€\/hour and a person can handle 60 pallets\/hour
Tournai 67€\/hour and a person can handle 40 pallets\/hour
Lille 79€\/hour and a person can handle 30 pallets\/hour

The handling should not take more than 12 hours per city.

The source code and the solution can be found here.

80
BRMS tutorial

BRMS tutorial
BRMS Tutorial
In the previous part, we learned the basic of drools concepts. We did that by using a java developer
tooling eclipse. We can not expect a Business User to use eclipse as a User interface to implement
rules. Since version 5 of drools, there is a dedicated User interface for that was called Guvnor in all
versions 5.X, called kie Workbench in versions 6.x till 6.3 and is is now called Business central
starting with version 6.4. Historically, Guvnor is a BRMS=Business Rule Management System. It
allowed to handle all lifecyle of a rule :

CRUD on a rule artifact,


Advanced Rule Pattern like decision tables,
RuleFlow/jbpm process definition,
definition of deployment rules packages, etc..

All was stored in a CMS (Content Management System) Component from the apache fundation called
Jackrabbit. The application Guvnor itself was a monolithic application which features was only
extensible by calling its rest API. The user interface was developed using the Google Web Toolkit
(GWT) from google to develop the application in java and generate the javascript to run as a native
javascript application on the browser. All jbpm components were separated application like jbpm-
console.

What is Business central ?


In version 6, the guvnor part was completely redesigned :

A new framework based on GWT but with features that helps to build a Modern business
application on HTML5 : Errai
a new framework was defined to define plugins to add new features : Uberfire
Use of standard build system widely used in the java ecosystem : Apache Maven
Use of a file base repository versus the CMS used before : git We will explain all this in next
part.

There are now two flavors of business central : the BRMS (Business Rule Management System) and
the BPMS (Business Process Management System).

The BPMS contains the BRMS with all jbpm specific components :

Form Modeler to develop specific Human task User Interfaces


Process instance and task management : to run the processes from within the workbench and
act on human tasks. We will see during this tutorial the BRMS part.

The drools core part itself now implements a new algorithm : PHREAK algorith (see 5.4 part of
documentation).

Before starting the tutorial, we shall go through a few concepts that will help to understand how to
use and integrate the BRMS in a real project.

How version 5.x BRMS was working and used in a project.


81
BRMS tutorial

As shown in the next picture, the steps when using version 5 were the following :

The java developer produces the pojo model (working with the business analyst). You could in
guvnor define a pojo model but with simple attributes. Most of the time, this feature was used
for temporary data produces by rules and consumes by others. Therefor, no java pojo model
was needed for that.
The java archive (jar) shall be uploaded to the Guvnor (BRMS) application. On this pojo mode,
the business analyst can write rules.
The java developer produces the final application that contains the drools runtime and deploys
it with the pojo model inside.

Now the application and Guvnor must have a synchronized version of the pojo model. There a few
other points that should be taken care :

when upgrading a pojo model version, the two parts must be synchronized.
It is not possible to work in parallel. There are workarounds like duplicating the package in
guvnor, etc..
As modern java development is using a maven approach in dependency and configuration
management, using Guvnor 5.x forces to have a specific build and deploy approach for it.

Guvnor is a nice tool very useful where most user interface about writing rules was kept in the
Business Central (enhanced of course) but all the wrapping was redesigned.

How version 6 fits in modern java development process


Maven versus Manuel Dependency management

In the 6.x BRMS, maven is fully supported in both direction :

1. The BRMS can retrieve maven artifact from its local repository as well as from remote maven
repositories.
2. The BRMS can act as a remote Maven repository and can be access from external maven
builds.
82
BRMS tutorial

Here is a typical use case :

1. The java developer makes the pojo/entity model and pushes the code commits on the SCM
repository (like a git repository). There is here an alternative where this can be done in Business
Central. In this case, the maven build that concerns the final application will retrieve the
pojo/entity model from the business Central repository.
2. A maven build is then started (in jenkins for example) and the pojo model is deployed on the
maven repository. The maven artifact has a groupid, an artifactid and a version.
3. The business Analyst creates a new project on the Business Central application, and in the
dependency list user interface, he just enters the groupid, artifactid and the version the java
developer gave him for the pojo/entity model. Maven "magic" now will come in place as
Business Central will automatically retrieves from all remote maven repositories that were
defined to him the pojo/entity model artifact as well as all its dependencies.
4. When building the final application, the rule package is retrieve by its groupid, artifactid and
version. Indeed, when creating a project in Business Central, you have to give it those element.
In the dependency file of the application (pom.xml), just add those identification elements as
well as the url of the business central maven repository, and it works. The maven build of the
application will retrieve the good version of the rule package.

In Guvnor 5.X, if a pojo model had dependencies, you had to upload them one by one with the good
version. It could lead to errors and when an update was needed in the dependencies, you had to
upload them one by one. Someone may ask : why is there a need for handling dependencies ? Just
make a java entity model with no dependencies. This is true and when using guvnor 5.x, we did like
this as good practice because of the limit Guvnor had. But in modern application, the entity model is
stored in databases using JPA or Hibernate annotations (or other framework when using nosql
databases for example). Now with the maven dependency and configuration handled by business
central, it is possible to use this entity model with no need to duplicate the model and then do

83
BRMS tutorial

mapping. It is no more needed. Also, in Guvnor 5.x, we had to build package versions (called
snapshot) and then somehow reference that name in the application to retrieve at runtime the good
package version. We can now do both even if we only show the example the deployment of the rule
package at build time of the application.

The case presented here is one use case. But using other parts of the tooling like the kie server may go
to other architecture. We will present later other scenarios.

There is no mystery in this feature. It is just a way to have a good dependency and configuration
management in the business Central. In the past, we had to handle all that manually with possible
human errors. The process is now automated and complies to an enterprise standard Apache Maven.
Another advantage of using maven build is that the Drools community will not need to maintain a
specific build process and concentrate on other feat

All this should be setup by IT and is no concern of the business analyst except the version of the rule
package to use in development , integration or production environments.

Git versus Apache jackrabbit

Guvnor 5.x was base on a Content Management System (CMS) library Apache Jackrabbit.

All rule artifact were stored as document


Document modification history was kept.
There is a concept of workspace in which all document are stored. Guvnor used that concept to
store a drools package and all its elements. So one project is stored in one workspace.
The storage behind Apache Jackrabbit is stored in a relational database. So you could use all
standard Relation Provider available on the market

To be able to communicate with the repository, a webdav interface was provided. This remote
interface was very near to subversion approach. it was also possible to access the content of a guvnor
project through its eclipse plugin.

This approach works very well when documents are handled. When it comes to source code, there is a
need of more complex content :

1. Branches to allow parallel development


2. It was not possible to store the content as normal source code in the IT department
3. The granularity of having one repository history for all projects was not adequate.

Indeed, when big companies were using guvnor, more than one instance of guvnor were used (one per
department for example). There was a need to be able to centralize all the business rules knowledge
and to allow to many department/organization to access it. A "Organizational Unit" concept was
created in the 6.x version which allow to link one or more repository.

As drools is an open source project and uses itself git as a source code management repository, git
was chosen to store the content of the new Guvnor version. In version 5.x, the storage was only
readable by the CMS library. Now the low level storage can be directly read with a normal file
explorer. There is no more need of Eclipse Plugin to access the rule project. In version 5.x all this
plombing around Developer tooling took a lot of time to the drools community. With this new tooling
choice, all this exists by default and the community can concentrate on other features.

As the new Guvnor version allows to store all elements of a company, it was called Business Central.

Version 6 tooling
84
BRMS tutorial

As seen in the previous part, the drools community redesigned completely the BRMS tooling :

A new build system of the rules to make them usabale in an application (maven)
A new repository content manager (git) to make it easier to integrate with java development
tooling and minimize development needs for that by using software industry standard
Business Central concept
New Rule design pattern
An open architecture to add new features with the plugin architecture
An execution server called kie server that simplifies the deployment of rule packages and is
totally integrated in Business Central.

Rule design pattern : decision tables,decision tree, guided rules

In previous tutorial, we have learned how to write rules using the Drools Rule Language (DRL)

We cannot expect a Business User to write rules using DRL. So Business Central offers editors that
will simplify writing rules. We are going to quickly present some of them.

Guided Rule

This editor allows to build a rule just by clicking and it will guide the user through all the syntax.

85
BRMS tutorial

Guided Decision Table

Guided Decision Tree

86
BRMS tutorial

Guided Score Card

TODO

Test scenario

87
BRMS tutorial

88
Organizational Unit And repository

Organizational Unit And repository


Organizational Unit And repository
Before starting any example, we shall create the necessary wrapping. On the main screen, click the
"Administration" link or the menu Authoring/Administration.

You should arrive to the next screen.

We will first create an organization Click on Organizational Unit/Manage Organizational Units

89
Organizational Unit And repository

And the click on the +Add Button in the middle and the following screen should be displayed. Enter
the data as displayed here and click on the OK button.

Now we shall create a repository (git) for that.

90
Organizational Unit And repository

We Shall give him a name a select the "Managed Repository" selector as shown below. We shall
create the repository for the Organizational Unit we created before.

After clicking the "Next button" the following screen will appear. Enter as shown here. We want it to
be a multi-project repository, we want to use 3 branches and give a default groupID and version. Then
click on the "Finish" button.

91
Organizational Unit And repository

Now go Back on the AUthoring view.

92
Organizational Unit And repository

Now we are ready.

93
Data Model

Data Model
Data Model
In the previous step we create a Organization Unit and a repository nautic.
We are now going to create project called swimmingpool.

In the New Item select "project"

94
Data Model

Enter the data as shown above. The Project general settings view is then displayed.

We want to add a dependency on the pojo/entity java model. So we click as shown below on the
"dependencies" menu item.

95
Data Model

We then enter the data as shown below.

This is a community project we shall speak later about.

The version you should use now is 2.0.10 and the associate maven project is here. Then as in our
runtime we configured all necessary settings for maven, all is retrieved. The project we use is on
maven central.
The following screen is then displayed.

96
Data Model

As said in the introduction on BRMS, all dependency are shown.

Note that in the 6.4.0 version there is a bug. If the workbench is restarted or you quit the editor
and re-open it, the list is then empty when you log again. But it is only a display problem as in
the repository the dependency is kept.
the JIRA is here. It is corrected in version 6.5.0.

Now we have to say which java entity/classes we want to use in our rules. This is then done as
follows.

Then the list looks like this.

Where dependencies and package white list are stored


As said before, in case of a restart of the workbench application, the dependency list is empty.
In this case, to see the dependency, you set the project explorer mode to repository view as shown
below

97
Data Model

You can now see how the repository is stored with all files.

98
Data Model

Click on the pom.xml file. In the dependency part of the file, you should see what we entered before.

99
Data Model

And in the file package-names-white-list the package we defined allowed to be used in the guided
editors.

100
Data Model

101
Business Requirements

Business Requirements
Business Requirements
A new web site has been developed to sell ticket for the city Olympic Swimming pool of Zurich. The
purpose is to build a web service by giving him the customer quote request and in response the
proposed quoting.

As an input, we will receive a quote that contains

* an address,
* A list of person,
* a desired period.

As a response, we shall receive the same quote but filled with standard Price per person, the list of
reduction per person and the total price. Here are the price per age and desired period.

Age >= Age < season amount


0 3 Always Free
3 11 day 3€
11 18 day 5€
18 60 day 12€
60 day 8€
3 11 week 10€
11 18 week 15€
18 60 week 25€
60 week 20€
3 11 weekend 5€
11 18 weekend 7€
18 60 weekend 15€
60 weekend 10€
3 11 month 20€
11 18 month 28€
18 60 month 45€
60 month 40€
3 11 summer 30€
11 18 summer 48€
18 60 summer 60€
60 summer 50€
3 11 winter 28€
11 18 winter 48€
18 60 winter 60€
60 winter 50€
3 11 spring 30€

102
Business Requirements

11 18 spring 48€
18 60 spring 60€
60 spring 50€
3 11 autumn 30€
11 18 autumn 48€
18 60 autumn 60€
60 autumn 50€
3 11 full Year 80€
11 18 full Year 110€
18 60 full Year 150€
60 full Year 120€

In a quote with a group of at least 4 people, the youngest person between 3 and 11 years old
years has a free ticket

if a person has his birtday the day the ticket is quoted, then a 10% applies to that person. This
applies only for a day ticket

If people are living in Zurich, then 10% applies to each person

103
Design

Design
Design
We are going to divide our rules in 5 steps and do a rule-flow which mean a process :

1. init step : we shall determine the age of each person and the person has his birthday based on
the quoting date.
2. standard price : as we have the table with all prices, we shall make a decision table of it.
3. Promotion : if there are more that 4 persons and the youngest is between 3 and 18 years old
4. Reduction : birthday and If people are living in Zurich
5. Calculate the total

The data model we are going to use is as follows :

104
Implementing the Process Flow

Implementing the Process Flow


Implementing the Process Flow
We are going to implement a rule flow with 5 steps.

Name ruleflow group name


init data init
standard price standard
Promotion promotion
Reduction reduction
Subscription subscription

We shall give him a name in a package

Select the start event and select the task element :

Click on the wrench and select "Business Rule Task"

105
Implementing the Process Flow

Click on the "<<" on the right part and the properties will appear. Enter the Name and Ruleflow group
name as follows :

Do the same with other business tasks.

And do not forget to save the process.

106
Implementing the Process Flow

107
Implementation technical parts

Implementation technical parts


Implementation technical parts
Cloning the git repository
Before going further, if you have git installed on your machine, you can checkout the code by cloning
the repository :

git clone git://machine_ip:9418/nautic

In my example, I use a docker container that is in a virtualBox Machine at ip 192.168.99.100 and at


port 19418.

Create functions
We will need some java functions to help us writing rules Go to new Items/DRL FIle and create a rule
called "function" and put in the rule content the following code :

import java.util.Calendar;
import java.util.Date;

function int dateCalculation(Date currentDate,Date birthDate) {

long ageInMillis = currentDate.getTime()-birthDate.getTime();


Date age = new Date(ageInMillis);
return (int)(ageInMillis/1000/60/60/24/365);

function boolean isBirthday(Date currentDate,Date birthDate){


boolean result = false;
Calendar c1 = Calendar.getInstance();
c1.setTime(currentDate);
Calendar c2 = Calendar.getInstance();
c2.setTime(birthDate);
int day1 = c1.get(Calendar.DAY_OF_MONTH);
int month1 = c1.get(Calendar.MONTH);
int day2 = c2.get(Calendar.DAY_OF_MONTH);

108
Implementation technical parts

int month2= c2.get(Calendar.MONTH);


if (day1==day2 && month1==month2){
result = true;
}
return result;
}

function double AgeCalculationYear(Date d1,Date d2) {


Calendar c1 = Calendar.getInstance();
c1.setTime(d1);
int day1= c1.get(Calendar.DAY_OF_MONTH);
int month1 = c1.get(Calendar.MONTH);
int year1 = c1.get(Calendar.YEAR);
Calendar c2 = Calendar.getInstance();
c2.setTime(d2);
int day2= c2.get(Calendar.DAY_OF_MONTH);
int month2 = c2.get(Calendar.MONTH);
int year2 = c2.get(Calendar.YEAR);
double val1= (year2-year1);
double val2=(month2-month1)/12.0;
double val3=(day2-day1)/365.0;
double age = val1+val2+val3;
return age;
}
function double AgeCalculationMonth(Date d1,Date d2) {
Calendar c1 = Calendar.getInstance();
c1.setTime(d1);
int day1= c1.get(Calendar.DAY_OF_MONTH);
int month1 = c1.get(Calendar.MONTH);
int year1 = c1.get(Calendar.YEAR);
Calendar c2 = Calendar.getInstance();
c2.setTime(d2);
int day2= c2.get(Calendar.DAY_OF_MONTH);
int month2 = c2.get(Calendar.MONTH);
int year2 = c2.get(Calendar.YEAR);
double val1= (year2-year1)*12;
double val2=(month2-month1);
double val3=(day2-day1)/30.0;
double age = val1+val2+val3;
return age;
}

109
Implementation technical parts

110
Implementing the initial step

Implementing the initial step


Implementing the initial step

111
Implementing the initial step

112
Implementing the initial step

113
Implementing the initial step

114
Implementing the initial step

115
Implementing the initial step

Then you shall obtain the rule as follows. Notice that the stringValue of the calculatedAttribute as a
formula. So you should select the formula not a litteral. And also that we had to create a binding
varialble "bd" for attribute birthdate of call Person.

116
Implementing the initial step

Notice that we test there is not already a calculated attribute for that person with the same key

Notice that we have to add to the list of calculated attribute of the person and then tell the rule engine
the list was updated.

Test the initial step


The workbench allows to test the rule we do. Create a new item of type "Test scenario" like follows :

117
Implementing the initial step

Click on the "Given" Cross and add a new fact as follows :

Then click on the "Add Field" text.

Select the field "quoteDate" :

118
Implementing the initial step

Click on the "Literal value" button

As the attribute is of type date, you can select a date with the date picker.

Select a date and then add another instance of type Person

119
Implementing the initial step

and add a field and select birthdate.

Select
literal value.

And select a date as for the quote date but on same day (here June 30th?

then add a expectatation by clicking on the Cross


near Expect

120
Implementing the initial step

Add a fact of type CalculatedAttribute

and Click on the "A fact of type... " Then chose the StringValue field

And then enter the value true

And as the rule we want to test is in ruleflow group init. Click on the cross near the "Given" cross and
enter "init" in the activate rule flow group part.

121
Implementing the initial step

The test scenario should then look like this :

Then click the "Run scenario" button twice and you should see the screeen as follows

122
Implementing the initial step

Calculate person age


We shall calculate the person age based on his birthday and the curDay attribute of the quote. At the
same time, we shall initialize the price attribute with a BigDecimal. in the function.drl, add the
"import java.math.BigDecimal"

First add the


two classes on which we shall apply the rule.

Then we should create the rule like that.

123
Implementing the initial step

We added two options : the ruleflow group.

Then we can create a test scenario to verify our rule.

124
Implementing standard price step

Implementing standard price step


Implementing standard price Step
To implement the price table we will use a decision table. Go to the "new Item" and select the
"Guided Decision Table" :

We give him a name :

And we are going to add constraint on the person age we have to add the person class type to the Data
Objects list.

125
Implementing standard price step

126
Implementing standard price step

127
Implementing standard price step

128
Implementing standard price step

129
Implementing standard price step

130
Implementing standard price step

131
Implementing standard price step

132
Implementing standard price step

133
Implementing standard price step

134
Implementing standard price step

135
Implementing standard price step

136
Implementing standard price step

137
Implementing the Child over 3 years old is free promotion step

Implementing the Child over 3 years old is free


promotion step
Implementation the Child over 3 is free
Creating the rule
Create a new Guided Rule that you call YougestUnder3yearsOldFree

then Add a new Constraint

Select the List class. Click on the "There is a list.." and the click on the Expression Builder button

on the first "Choose" select the size() function and on the second "greater than"

Then click on the pencil Click on the "From Collect"


link and then chose the person fact type

138
Implementing the Child over 3 years old is free promotion step

Then click on the "All Person with" link and select the age attribute and add the following constraint.

Then Add a new constraint of type Accumulate

And select the Number type from the add a pattern link.

Then make the rule look like this

139
Implementing the Child over 3 years old is free promotion step

Creating rule to insert a period

Create a drl file with the following content

140
Implementing the Child over 3 years old is free promotion step

Creating rule to start a process


To be able to start a process from a test scenario, we want to insert an object and set a attribute that
would be the process id we want to start. So we first create a new Item of type Data Obkect that we
shall call StartProcess. And we should add a field called procesId of type String

Then we shall create a guided rule called StartProcessRule as follows. In the Then add free form drl

141
Implementing the Child over 3 years old is free promotion step

Testing the rule


Create the test scenario by inserting Objects like follows.

142
Implementing the Birthday promotion step

Implementing the Birthday promotion step


**# Implementing the Birthday promotion step

If the person has his Birthday on the quote day and a day ticket is asked, then a 10% reduction shall
be applied for the person.

143
Implementing the Birthday promotion step

Instead of ROUND_HALF_DOWN you can put ROUND_HALF_UP which will put higher reduction
amount.

144
Implementing the Birthday promotion step

145
Implementing the Zurich reduction step

Implementing the Zurich reduction step


Implementing the Zurich reduction step
Rule Zurich reduction rule

146
Implementing the Zurich reduction step

Instead of ROUND_HALF_DOWN you can put ROUND_HALF_UP which will put higher reduction
amount.

Test the Zurich reduction rule

147
Implementing the Zurich reduction step

148
Implementing the final step

Implementing the final step


Implementing the final step
We shall now implement a rule that calculates the total and one that create the subscription we
propose to the end user.

In the rule above, we have to use custom code as when working with java Bigdecimal class (which we
shall use when working with financial amounts) the standard functions do not work.

149
Implementing the final step

In the second rule, we use the definitive price we create before. in the picture shown below, we can
see an example of the test server we shall present in the next tutorial

150
Implementing the final step

151
Implementing the final step

152
Conclusion

Conclusion
What we did up to now
In the first tutorial we have learned the core drools concepts that are needed to start implementing
business rules. The course example was simply there to demonstrate and help the user to understand
the concepts. The exercise then starts to show a more complex case where we have to separate in
steps to get the final result. In those two first examples on the drools tutorial, how to interface with a
real application was not in the scope of the exercise. In the BRMS tutorial, we took the same
approach on how to express the rules and how to test them. But it is never express how we should
interface our rule engine implementation with our real application. In the next tutorial, we shall
propose an architecture of a drools application.

153
BRMS Runtime tutorial

BRMS Runtime tutorial


BRMS runtime tutorial
Using previous drools version (5.x)
In a classical drools project in previous version, the architecture was often like this :

an application A is going to call and use a drools service.


a drools runtime that is built for the application using standard drools API. In many cases the
runtime is using a stateless session.
The drools runtime is loading the rule package from a rule repository that has an authoring tool
to implement the rules. This last two features were implemented using what was called Guvnor.
Deploying a new version of a package could be done by updating the runtime remotely by
calling the drools API features.

An alternative of this architecture is to implement the runtime directly in the application A.

Architecture using 6.4 version and drools API


In the 6.x serie, the Guvnor tool was replaced by what was called workbench and now Business
Central. We shall call it kie-wb. It is possible to take the same architecture using the drools API and
the kie-wb. There are now two possibilities
154
BRMS Runtime tutorial

as the business central is a maven repository, rebuild the runtime (or application if the runtime
is embedded) with the new package version.
In the runtime dynamically add to the java classpath the new jar of the rules package each time
there is a new version.

using kie-server and kie-wb together


In the 6.x version, a new component was introduced called kie-server. It wraps all we described in
previous step and is integrated with the workbench. The new architecture looks like the previous one :

Each kie-server calls the kie-wb at startup and declares itself


From the kie-wb, it is possible to create kie-container in the desired kie-server.
This kie-container contains one maven artifact defined in the kie-wb for one version.
All this is linked together using maven configuration and all related features but all is done for
us. You just say you want this artifact in this version (it must be built before of course) and that
is all.
The kie-container exposes a basic rest service that has the same signature as the API we used in
the drools tutorial : insert object, fire all rules etc..
The application A calls the drools API remotely and does not have to take care of new rule
version deployment.

Out of the box, the new drools tooling in version 6.4 offers us an authoring tool with a runtime ready
to work\/deploy a drools set of rules in a maven package.

Which is even better is that as well the authoring tool (kie-wb) or the execution server (kie-server) can
be extended. You can see that at the end of the book.

For now on, we shall use the standard tooling and interface available out of the box.

155
BRMS Runtime tutorial

156
Install needed tools to run the workbench

Install needed tools to run the workbench


To be able to run the workbench as well as the kie server nativelely on your machine, a lot has to be
installed :

Java runtime
Bundle that installs all needed component

Then you run a shell script to make all run. What if you have another version of java ?

To make it easier, we are going to use a container technology called docker.

This technology allows us to use pre-build images that run on all supported platform of docker :
nearly all linux flavors, Mac Os and windows and this in native way for windows or for MacOS. You
can use this way of using but we shall use the toolbox instead.

This will avoid spolling your operating system, we are going to use virtual machines on your host.

For the windows\/MacOS machines, the toolbox is here.

We shall continue withe the windows installation.

Click in the download page on the Docker ToolBox Setup. The security system will ask you if you
allow this program to execute, answer yes.

157
Install needed tools to run the workbench

158
Install needed tools to run the workbench

159
Install needed tools to run the workbench

160
Install needed tools to run the workbench

Now double click on the "Docker quickstart terminal" and all should start :
161
Install needed tools to run the workbench

Now we are ready to start the workbench.

We are first going to start the workbench

type in the command line

docker run -p 8080:8080 -p 8001:8001 -d --name drools-wb


jboss/drools-workbench-showcase:6.4.0.Final

The command may need to download the image from the docker hub.

To know if if is running, we shall connect to the drools workbench, we shall first look on which ip the
docker machine is running :

162
Install needed tools to run the workbench

We shall first verify the drools workbench is running :

you can now access the workbench at the following address : http:\/\/192.168.99.100:8080\/drools-wb

the username is admin and the password is admin.

We shall now start a kie-server.

enter the command :

docker run -p 8180:8080 -d --name kie-server --link drools-wb:kie_wb


jboss/kie-server-showcase:6.4.0.Final

Login in the workbench and go the deploy\/rule deployment and you should see this.

163
Install needed tools to run the workbench

You can now start the tutorial on the BRMS.

164

You might also like