Drools tutorial – The Rule Engine made simple

In this tutorial we will learn about the JBoss Drools Rule engine and how to run a simple application on the top of it using Drools latest release (7.62.0 released November 2021 ). Before getting into a simple example, we need to clarify some concepts about Rules, Rule engines and what is Drools.

What is Drools Rule Engine ?

A Rule Engine refers to a system that uses rules, in any form, to a set of data to produce outcomes. It may refer to simple systems like form validation or more complex systems like dynamic expression engines. In a few words, a Rule engine allows you to say “What to do” and not “How to do it”.

Please notice there are some key differences between a Rule engine and a Decision engine: this article discusses them in detail: Getting started with Decision Models (DMN)

Why using Drools Rule Engine ?

Rule systems can solve very hard problems, explaining of each “decision” taken along the way. Here is a list of a Rule engine benefits:

  • Logic and Data Separation: Breaking your domain objects from business rules can lead to a simpler application maintenance. That can shield from changes in the future, since you have coded the logic into a set of rules.
  • Speed and Scalability: Many times we apply “if” conditions that we don’t really need. The Rete algorithm, as implemented by Drools, replaces all the if … then statements with an optimized network.
  • Centralization of Knowledge: By using rules, you create a repository of knowledge  for your business policy. Ideally, rules are so readable that they can also serve as documentation
  • Tool Integration: Eclipse provides an excellent tool to deliver and test your Rules without leaving your IDE.
  • Understandable Rules: JBoss Drools rules can make it easy to express solutions to complex problems and consequently have those solutions verified (rules are much easier to read then code). Often, business users are more comfortable expressing things that they know to be true, than to express things in an if…then format. Examples of things that you might hear from a business expert are:

We need to buy that estate if the price is not over 1000000 $ and estate agency doesn’t claim more than 5000 $“.
We buy shares when the price goes over 15 Euro before next week

By focusing on what we know to be true, rather than traditional “if…then” clause, the logic is much cleaner.

Drools Rule engine ecosystem

Now that we know what a Rule system is, our drool tutorial will focus on the following components that make up the Drools Rule engine ecosystem:

Drools Fusion gives the rules engine the ability to perform event processing, often referred to as complex event processing ( CEP ).

jBPM is a means of describing some process that occurs within your organization as a series of steps. jBPM lets you design how to connect these steps and to assign an owner to each step. jBPM, which is based on the BPMN 2.0 specification, serves as a tool for describing such processes in a flowchart
format. jBPM supports the entire process life cycle, from development to execution and management up to retirement.

Drools Guvnor provides us with a centralized repository with a layer of user authentication in which we can store and manage our rules, processes, models, and other entities related to knowledge bases.

Drools Planner helps us to carry out automated planning, to optimize how we distribute and utilize resources in an organization. This process helps to consider multiple solutions and choose which works best for us.

Business Central Workbench: Business Central Workbench is the web application and repository to govern Rules and Business Process. Within the Business Central, you can design and deploy your assets. It was formerly known as the Kie WorkBench.

The following picture depicts the core components of Drools Rule engine architecture:

drools rule engine tutorial

Developing applications with JBoss Drools

In order to develop applications using Drools, you can use any IDE which supports Maven. We, however, recommend using an Eclipse based environment (Eclipse or Code Ready Studio) which can use the Drools Plugin for Eclipse.

Therefore, in this tutorial we will show how to create a simple project using Eclipse with Drools Plugin. If you want to check how to install the Drools Plugin on Eclipse / JBoss Developer Studio, check this tutorial: Installing Drools plugin on Eclipse

An Hello World Drool Project

Once you have installed the Drools plugin for Eclipse, you will be able to create a New Drools project from the “New…” Menu

drools rule engine tutorial

Click Next. In the following Window, choose the initial project contents. We will just need a blank project with the correct Maven folders:

drools rule engine tutorial

In the last screen you have to choose how to build your project. You have mainly two options:

  • Use a local Drools Runtime: This can be a handy solution if you are working offline and you want to use the libraries which are bundled in Drools Engine.
  • Use a Maven project: This should be your default option as it generates a Maven project with all Drools dependencies in place.

We will choose to use a Maven project. Check at the end of the article for instructions how to use the local Drools Runtime.

drools rule engine tutorial

Now the project will be created so we can add two core pieces:

  1. A simple Rule which interacts with a Java instance (a fact) passed to the Rule
  2. A Java main class to execute the Rule

Let’s add at first a new Rule. From the File menu choose to add a new Rule:

drools rule engine tutorial

As you can see, you will be requested to enter a Rule file name and the Type of resource Rule. In our case we will create the Rule.drl file name as part of a rule package. The rule will be in the rules package name. Here is the simple Rule:

package com.sample.rules

dialect "mvel"

import com.sample.DroolsShop.Customer
import com.sample.DroolsShop.Product
import com.sample.DroolsShop.Purchase
import com.sample.DroolsShop.Discount
 
rule "Purchase notification"
    salience 10

    when
        $c : Customer()
        $p : Purchase( customer == $c )
    then
        System.out.println( "Customer " + $c.name + " just purchased " + $p.product.name );
end
 
rule "Discount removed notification"
    when
        $c : Customer()
        not Discount( customer == $c )
    then
        $c.discount = 0;
        System.out.println( "Customer " + $c.name + " now has a discount of " + $c.discount );
end

rule "Discount awarded notification"
    when
        $c : Customer()
        $d : Discount( customer == $c )
    then
        System.out.println( "Customer " + $c.name + " now has a discount of " + $d.amount );
end

rule "Apply 10% discount if total purchases is over 100"
    no-loop true
    dialect "java"
    when
        $c : Customer()
        $i : Double(doubleValue > 100) from accumulate (
                Purchase( customer == $c, $price : product.price ),
                sum( $price )
        )
    then
        $c.setDiscount( 10 );
        insertLogical( new Discount( $c, 10 ) );
        System.out.println( "Customer " + $c.getName() + " now has a shopping total of " + $i );
end

Let’s learn a bit about Drools Rule syntax.

dialect

The first keyword we have met is “dialect” which shows which language you can use to code your Drool Rules. You have mainly two options:

  • mvel” which an expression language with a simple and powerful syntax which simplifies access to getter and setter methods of your objects
  • java” which allows using java to code Drools rules. Some restrictions apply: you cannot use Java code inside “when” part of a condition but you can use Java code in “then” part.

salience

salience is a keyword in the .drl file that we can assign a positive or negative number. The number determines the salience priority. A higher number denotes a higher priority, so the Drools engine will execute rules with a higher salience first.

no-loop true

Self-loops are very common to find in our rules, hence Drools already comes with a special attribute we can use in our rules to avoid these situations: no-loop. If you don’t want to activate your Rules multiple times, mark them with the no-loop attribute.

accumulate functions

Accumulate is a conditional element available in Drools since version 4.0 and it is used to iterate over the list of objects and allows to perform java operations to validate the data.

In our case, when this condition is met:

    when
        $c : Customer()
        $i : Double(doubleValue > 100) from accumulate (
                Purchase( customer == $c, $price : product.price ),
                sum( $price )
        )

Then the variable “i” will be set to the sum of items which have been purchased.

insertLogical

insertLogical(new Something()); is similar to insert, but the Rule engine will automatically retract it when there are no more facts to support the truth of the currently firing rule.

In order to execute them we’ll create a Java Class named DroolsShop. For the sake of simplicity, we will include all Java Beans in the Main class, so that we have to create just one Java class:

package com.sample;

import org.kie.api.KieServices;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.api.runtime.rule.FactHandle;

public class DroolsShop {

    public static final void main(String[] args) {
        KieContainer kc = KieServices.Factory.get().getKieClasspathContainer();
        System.out.println(kc.verify().getMessages().toString());
        execute( kc );
    }

    public static void execute( KieContainer kc ) {
        KieSession ksession = kc.newKieSession("ksession-rules");

        Customer mark = new Customer( "mark",
                                      0 );
        ksession.insert( mark );

        Product shoes = new Product( "shoes",
                                     60 );
        ksession.insert( shoes );

        Product hat = new Product( "hat",
                                   60 );
        ksession.insert( hat );

        ksession.insert( new Purchase( mark,
                                       shoes ) );

        FactHandle hatPurchaseHandle = ksession.insert( new Purchase( mark,
                                                                      hat ) );

        ksession.fireAllRules();

        ksession.delete( hatPurchaseHandle );
        System.out.println( "Customer mark has returned the hat" );
        ksession.fireAllRules();
    }

    public static class Customer {
        private String name;

        private int    discount;

        public Customer(String name,
                        int discount) {
            this.name = name;
            this.discount = discount;
        }

        public String getName() {
            return name;
        }

        public int getDiscount() {
            return discount;
        }

        public void setDiscount(int discount) {
            this.discount = discount;
        }

    }

    public static class Discount {
        private Customer customer;
        private int      amount;

        public Discount(Customer customer,
                        int amount) {
            this.customer = customer;
            this.amount = amount;
        }

        public Customer getCustomer() {
            return customer;
        }

        public int getAmount() {
            return amount;
        }

    }

    public static class Product {
        private String name;
        private float  price;

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

        public String getName() {
            return name;
        }

        public float getPrice() {
            return price;
        }

    }

    public static class Purchase {
        private Customer customer;
        private Product  product;

        public Purchase(Customer customer,
                        Product product) {
            this.customer = customer;
            this.product = product;
        }

        public Customer getCustomer() {
            return customer;
        }

        public Product getProduct() {
            return product;
        }
    }
}

This class does the following actions:

  • Creates a KieContainer from the KieServices.Factory
  • Bootstraps the Rule Engine session: The KieSession represents a running instance of the Rule Engine with a specific configuration and set of rules. It holds the evaluation algorithm used to match the rules against our domain objects.
  • Let the Rule Engine to know about our data: We are responsible for providing all the information to the engine through the insert() method on KieSession. Next, if the information that we provided matches with one or more available rules, we will get Matches. Calling the fireAllRules() method will execute these matches.

As you might have noticed, we have inserted two objects in the KieSession:

ksession.insert( new Purchase( mark, shoes ) );

FactHandle hatPurchaseHandle = ksession.insert( new Purchase( mark, hat ) );

For the latter Purchase, we return the FactHandle so that we can later delete it from the KieSession:

ksession.delete( hatPurchaseHandle );

Then, as a proof of concept, we fire again all Rules to verify that the Discount (for purchases over 100) has been cancelled.

The last thing we need to do in order to complete our project, is a file in the src/main/resources/META-INF/ directory called kmodule.xml. This file is used to configure how to load the rules defined in the project in the rule engine. For now, the content of kmodule.xml will be quite simple as we will be using all the default configurations. Here is a sample content of it:

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule">
    <kbase name="rules" packages="com.sample.rules">
        <ksession name="ksession-rules"/>
    </kbase>
    <kbase name="dtables" packages="com.sample.dtables">
        <ksession name="ksession-dtables"/>
    </kbase>
    <kbase name="process" packages="com.sample.process">
        <ksession name="ksession-process"/>
    </kbase>
</kmodule>

Here is how your project source tree should look like:

src
└── main
    ├── java
    │   └── com
    │       └── sample
    │           └── DroolsShop.java
    └── resources
        ├── com
        │   └── sample
        │       ├── dtables
        │       ├── process
        │       └── rules
        │           └── Rule.drl
        ├── logback-test.xml
        └── META-INF
            ├── kmodule.xml
            └── maven
                └── pom.properties

Building and Running the application

To build Drools application we recommend including the following Bill of Material:

<dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-bom</artifactId>
        <type>pom</type>
        <version>${drools-version}</version>
        <scope>import</scope>
      </dependency>
    </dependencies>
</dependencyManagement>

Then, include as property the latest version of Drools which is 7.62.0 (November 2021):

<drools-version>7.62.0.Final</drools-version>

You can run the App class from the IDE with “Run as Java Application”.

When executed the output will be:

Customer mark just purchased shoes
Customer mark just purchased hat
Customer mark now has a discount of 0
Customer mark now has a shopping total of 120.0
Customer mark now has a discount of 10
Customer mark has returned the hat
Customer mark now has a discount of 0

This is a basic example taken from the Drools examples available in the GitHub repository. In the following articles we’ll see how a rules engine can significantly reduce the complexity of components that implement the business-rules logic in your Java applications. By expressing your rules using JBoss Drools declarative approach your application has a higher chance of being more maintainable and extensible than one that doesn’t.

Source code for this tutorial: https://github.com/fmarchioni/mastertheboss/tree/master/drools/helloworld-eclipse

Using a Local Drools Rule Engine Runtime

If you prefer, you can complete this tutorial by using a Drools Rule Engine Runtime which can be downloaded as a zip from http://www.drools.org/download/download.html

drools rule engine tutorial

As you can see from the above screen, you have two options:

1. Specify where the Drools Rule Engine Runtime is located

2. Specify the Maven GAV

In case you have already a Runtime,  you can simply choose to Add Runtime selecting the “binaries” sub folder of your Drools Distribution:

drools rule engine tutorial

Continue Learning Drools Rule Engine

Continue Learning Drools Rule Engine through the following tutorials:

1) Creating a Drools project from the Command Line with Maven

If you are not using Red Hat Code Ready Studio or Eclipse to develop your project, as simple environment Maven based will work.  The following tutorial shows how to create a minimal Drools project using Maven: Drools and Maven Hello World example

Then, import the project in your IDE and manage it from there.

2) Coding Rules in Decision Tables

In Drools, Decision Tables are a way to generate rules from the data entered into a spreadsheet. The spreadsheet can be a standard Excel (XLS) or a CSV File.

drools rule engine tutorial

If you want to learn how to code your Rules into an excel Spread Sheet, keep reading this tutorial: Getting started with Decision Tables in Drools

3) Getting started with the Kie Execution Server

The Kie Server is a Java web application that allows us to expose rules and business process to be executed remotely using REST and JMS interfaces. The following tutorial shows how to install it on the top of WildFly: Configure Kie Execution Server on WildFly

4) Deploy your assets on the Business Central

Once that you are familiar with the Kie Execution Server, you can learn how install also the Business Central where you can design, build and deploy your assets. Here is the tutorial for you: Getting started with Business Central Workbench

Enterprise Drools

Are you interested in a supported versions of Drools and jBPM?  Red Hat provides support for them as follows: