Drools and Maven Hello World example

[Tutorial updated on August 2020]

In the following tutorial we will learn how to create a basic Drools Rule Engine example project using Maven as project builder.

The prerequisite to this tutorial is that you have installed Maven on your pc. We will learn at first how to create a project from the shell. As an alternative, you can use an IDE like Eclipse with Drools plugin or Red Hat Code Ready Studio.

Create a new Maven Project for Drools

In order to get started you just need a basic Maven archetype which can be used to set up an initial structure for our projects. The kie-drools-archetype can be used for this purpose. From a shell, issue the following command:

mvn archetype:generate -B -DarchetypeGroupId=org.kie -DarchetypeArtifactId=kie-drools-archetype -DarchetypeVersion=7.41.0.Final -DgroupId=com.sample -DartifactId=helloworld -Dversion=1.0-SNAPSHOT -Dpackage=com.sample

The project will create a basic Drool project with an example Rule file and a Java class to test it.

Let’s have a look at the generated pom.xml file for this project:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.sample</groupId>
  <artifactId>helloworld</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>kjar</packaging>

  <name>helloworld</name>
  <url>http://drools.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <drools-version>7.41.0.Final</drools-version>
    <slf4j-version>1.7.26</slf4j-version>
    <junit-version>4.12</junit-version>
  </properties>

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

  <dependencies>

    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-compiler</artifactId>
        <scope>test</scope>
      </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>${junit-version}</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.slf4j</groupId>
      <artifactId>slf4j-log4j12</artifactId>
      <version>${slf4j-version}</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.kie</groupId>
        <artifactId>kie-maven-plugin</artifactId>
        <version>${drools-version}</version>
        <extensions>true</extensions>
      </plugin>
    </plugins>

  </build>
</project>

As you can see, the archetype version has created for us a project using the same Drools Engine version (7.41.0.Final), therefore in order to have an updated version of the Engine just use the matching archetype version.

Add Classes and Rules

Ok, now we need to set up a simple Rule which is based on the Server class. This rule will issue a warning if the Server class does not meet minimal requirements.

package com.sample;

public class Server {

    private String name;
    private int processors;
    private int memory;
    private int diskspace;
    private boolean isValid=true;

    public Server(String name, int processors, int memory, int diskspace) {
        this.name = name;
        this.processors = processors;
        this.memory = memory;
        this.diskspace = diskspace;
    }

    // Getter/Setters method omitted for brevity
}

And here’s our simple Rule contained in src/main/resources/rules.drl:

import com.sample.Server
rule "Check Server Configuration"
  when
  $server : Server( processors < 2 || memory<=1024 || diskspace <= 2048)
  then
  $server.setValid(false);
  System.out.println("Server "+ $server.getName() + " configuration does not meet requirements!");
end

Under the folder srm/main/resources/META-INF the file kmodule.xml has been added with just an empty module:

<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
   
</kmodule>

This means, we will use the default KSession in order to insert our facts and fire the rules.

Now, let’s code a simple Test class which will create two Server resources and validate them using our rule:

package com.sample;

import org.junit.Test;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.Message;
import org.kie.api.builder.Results;
import org.kie.api.definition.KiePackage;
import org.kie.api.definition.rule.Rule;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static org.junit.Assert.assertTrue;

public class RuleTest {
    static final Logger LOG = LoggerFactory.getLogger(RuleTest.class);

    @Test
    public void test() {
        KieServices kieServices = KieServices.Factory.get();

        KieContainer kContainer = kieServices.getKieClasspathContainer();

        LOG.info("Creating kieBase");
        KieBase kieBase = kContainer.getKieBase();

        LOG.info("There should be rules: ");
        for ( KiePackage kp : kieBase.getKiePackages() ) {
            for (Rule rule : kp.getRules()) {
                LOG.info("kp " + kp + " rule " + rule.getName());
            }
        }

        LOG.info("Creating kieSession");
        KieSession session = kieBase.newKieSession();

        LOG.info("Now running data");

        Server s1 = new Server("rhel7",2,1024,2048);
        session.insert(s1);
        session.fireAllRules();
        assertTrue(s1.isValid());

        Server s2 = new Server("rhel8",2,2048,4096);
        session.insert(s2);
        session.fireAllRules();
        assertTrue(s2.isValid());

    }
}

You can run the Test as follows:

$ mvn clean install

The expected outcome is that the first Server configuration will not pass the minimal requirements:

Server rhel7 configuration does not meet requirements!

You can try increasing the Server settings for the first server and verify that all Server configurations are now valid

You can find the source code for this Drools Hello World example at: https://github.com/fmarchioni/mastertheboss/tree/master/drools/helloworld

Running the Hello World Drools with CDI

It is noteworthy to mention that CDI is now tightly integrated into the KIE API. It can be used to inject versioned KieSession and KieBases. Here is how to rewrite the above example using CDI.

First, we need to enable CDI, so we will add a beans.xml file under the folder resources/META-INF

$ touch src/main/resources/META-INF/beans.xml

Then, let’s add a CDI Bean which will get injected the default KSession:

package com.sample;

import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;
import org.kie.api.cdi.KSession;
import org.kie.api.runtime.KieSession;

import javax.inject.Inject;

public class CDIExample {

    @Inject
    @KSession
    KieSession session;

    public boolean go(Server server) {
        session.insert(server);
        session.fireAllRules();
        return server.isValid();
    }

    public static void main(String[] args) {

        Weld w = new Weld();
        WeldContainer wc = w.initialize();

        CDIExample bean = wc.instance().select(CDIExample.class).get();

        Server s1 = new Server("rhel7",2,2048,1024);

        boolean isValid = bean.go(s1);
        System.out.println("Configuration isValid "+isValid);
        w.shutdown();
    }

}

As you can see this CDI Bean also contains a main method so that the example can be tested using the maven-exec plugin as well.

And now, our test class which creates the Weld container where the CDIExample is added:

package com.sample;

import org.jboss.weld.environment.se.Weld;
import org.jboss.weld.environment.se.WeldContainer;
import org.junit.Test;

import static org.junit.Assert.assertTrue;

public class RuleTest {

    @Test
    public void testGo() {

        Weld w = new Weld();
        WeldContainer wc = w.initialize();

        com.sample.CDIExample bean = wc.instance().select(com.sample.CDIExample.class).get();
        
        Server s1 = new Server("rhel7",2,2048,4096);

        boolean isValid = bean.go(s1);
        assertTrue(isValid);

        w.shutdown();

    }
}

In terms of dependencies you have to include also the cdi and weld dependencies (with the exclusion of javax.el and javax.interceptor) in order to be able to use the CDI Container:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent>
    <groupId>org.drools</groupId>
    <artifactId>drools</artifactId>
    <version>7.41.0.Final</version>
  </parent>
  <groupId>com.sample</groupId>
  <artifactId>helloworld-cdi</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>kjar</packaging>

  <name>helloworld-cdi</name>
  <url>http://drools.org</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <drools-version>7.41.0.Final</drools-version>
    <slf4j-version>1.7.26</slf4j-version>
    <junit-version>4.12</junit-version>
  </properties>

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

  <dependencies>

 <dependency>
      <groupId>org.drools</groupId>
      <artifactId>drools-cdi</artifactId>
    </dependency>
    <dependency>
      <groupId>javax.enterprise</groupId>
      <artifactId>cdi-api</artifactId>
      <exclusions>
        <exclusion>
          <groupId>javax.el</groupId>
          <artifactId>javax.el-api</artifactId>
        </exclusion>
        <exclusion>
          <groupId>javax.interceptor</groupId>
          <artifactId>javax.interceptor-api</artifactId>
        </exclusion>
      </exclusions>
    </dependency>

    <dependency>
      <groupId>org.jboss.weld.se</groupId>
      <artifactId>weld-se-core</artifactId>
    </dependency>

    <dependency>
        <groupId>org.drools</groupId>
        <artifactId>drools-compiler</artifactId>
        <scope>test</scope>
      </dependency>

    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <scope>test</scope>
    </dependency>

  </dependencies>

  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
      <plugin>
        <groupId>org.kie</groupId>
        <artifactId>kie-maven-plugin</artifactId>
        <version>${drools-version}</version>
        <extensions>true</extensions>
      </plugin>
    </plugins>

  </build>
</project>

With the single Server we are testing, the unit test will pass:

[INFO] Running com.sample.RuleTest
Aug 14, 2020 3:37:00 PM org.jboss.weld.bootstrap.WeldStartup <clinit>
INFO: WELD-000900: 2.4.1 (Final)
SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
SLF4J: Defaulting to no-operation (NOP) logger implementation
SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
Aug 14, 2020 3:37:07 PM org.jboss.weld.bootstrap.WeldStartup startContainer
INFO: WELD-000101: Transactional services not available. Injection of @Inject UserTransaction not available. Transactional observers will be invoked synchronously.
Aug 14, 2020 3:37:07 PM org.jboss.weld.environment.se.WeldContainer complete
INFO: WELD-ENV-002003: Weld SE container 6efac82c-0c7d-4297-91d2-c48811e91808 initialized
Aug 14, 2020 3:37:08 PM org.jboss.weld.environment.se.WeldContainer shutdown
INFO: WELD-ENV-002001: Weld SE container 6efac82c-0c7d-4297-91d2-c48811e91808 shut down
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 7.391 s - in com.sample.RuleTest

This project is available on github at https://github.com/fmarchioni/mastertheboss/tree/master/drools/helloworld-cdi

Continue Learning Drools through the following tutorials:

1) 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 decision tables example
If you want to learn how to code your Rules into an excel Spread Sheet, keep reading this tutorial:
Getting started with Drools Decision Tables

2) Getting started with the Kie Execution Server

The Kie Server is a Java web application that allow 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

3) 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 jBPM Business Central