A minimal Spring Boot Drools web service

A little while back, I knocked up Qzr to demonstrate using Spring Boot with the Drools rules engine. However, I also wanted to play around with a few more technologies (AngularJS and Spring HATEOAS), so it’s a bit large for just demonstrating exposing Drools rules as an HTTP web service.

A few folks found it difficult to pick out the essentials of running Drools in a Spring Boot application, so I thought I’d have a go at creating a simpler application, which does nothing more than that.

Hence, the Bus Pass Web Service.

Before we continue, I should probably mention that you can grab the source code here:

https://github.com/gratiartis/buspass-ws

As might be guessed from the project name, for the rules, I took my cues from the Drools Bus Pass example in the Drools project. I cut the rules down a little bit and reduced the code by replacing some of the Java fact classes with DRL declared types. I prefer this for facts which are only referenced from within the DRL.

Assuming that you have a reasonably recent install of Maven and the JDK (I have tested with 8, but I think 7 should be okay), you should be able to do the following from the command line.

Build the application:

    mvn clean package

Run the application:

    java -jar target/buspass-ws-1.0.0-SNAPSHOT.jar

Then send a request to the API using curl or your favourite web browser. The rules state that if you request a bus pass for a person with age less than 16, you should see a ChildBusPass. For someone 16 or over, you should see an AdultBusPass.

For example, opening http://127.0.0.1:8080/buspass?name=Steve&age=15 gives me:

    {"person":{"name":"Steve","age":15},"busPassType":"ChildBusPass"}

… and opening http://127.0.0.1:8080/buspass?name=Steve&age=16 gives me:

    {"person":{"name":"Steve","age":16},"busPassType":"AdultBusPass"}

The full source code is on GitHub, so that you can browse through it. I don’t intend to change it much now, other than to add a few comments. The following are some of the key features, that you should know about.

First of all, it’s a Maven project, so I hope you’re familiar with that. The following XML is extracted from the pom.xml. Note that to enable Spring Boot, I have imported the Spring platform Bill of Materials and defined spring-boot-starter-web as a dependency. By including the spring-boot-maven-plugin, the Maven build will generate an executable jar, which will run up an embedded Tomcat instance to host the web application. You don’t need to have a web server installed on your machine, to run this application.

The Drools functionality is enabled by defining kie-ci as a dependency. This brings in the Drools API, and sets up classpath scanning so that it can find the rules in your application.


<!– Transitively bring in the Spring IO Platform Bill-of-Materials `pom.xml` –>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>io.spring.platform</groupId>
<artifactId>platform-bom</artifactId>
<version>1.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!– … –>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-ci</artifactId>
<version>${kie.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

Having kie-ci in the project means that Drools will scan for rules based on certain conventions. It will look for a file called kmodule.xml in src/main/resources/META-INF/.


<?xml version="1.0" encoding="UTF-8"?>
<kmodule xmlns="http://jboss.org/kie/6.0.0/kmodule&quot;
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&gt;
<kbase name="BusPassKbase" packages="com.sctrcd.buspassws.rules">
<ksession name="BusPassSession" />
</kbase>
</kmodule>

view raw

kmodule.xml

hosted with ❤ by GitHub

The kmodule.xml defines the package where the rules for your knowledge base can be found. Based on the definition above, it will scan for rules (.drl files and others) in src/main/resources/com/sctrcd/buspassws/rules. I won’t explain the rules. Feel free to go take a look at them yourself. As can be seen in the XML, this also defines a knowledge session called “BusPassSession”. This means that you can now start a knowledge session like so:


KieContainer kieContainer = KieServices.Factory.get().getKieClasspathContainer();
KieSession kieSession = kieContainer.newKieSession("BusPassSession");

view raw

gistfile1.java

hosted with ❤ by GitHub

The heart of a Spring Boot application is its main class, which causes your application to be bootstrapped.


@SpringBootApplication
public class BusPassApp {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(BusPassApp.class, args);
}
@Bean
public KieContainer kieContainer() {
return KieServices.Factory.get().getKieClasspathContainer();
}
}

view raw

BusPassApp.java

hosted with ❤ by GitHub

This is standard Spring Boot stuff, but the addition we have here is to define a bean, which references the Drools KieClasspathContainer. In doing this, we have a reference to the container, which we can inject into our application beans. This is exactly what we do with the BusPassService.


@Service
public class BusPassService {
private final KieContainer kieContainer;
@Autowired
public BusPassService(KieContainer kieContainer) {
log.info("Initialising a new bus pass session.");
this.kieContainer = kieContainer;
}
/**
* Create a new session, insert a person's details and fire rules to
* determine what kind of bus pass is to be issued.
*/
public BusPass getBusPass(Person person) {
KieSession kieSession = kieContainer.newKieSession("BusPassSession");
kieSession.insert(person);
kieSession.fireAllRules();
BusPass busPass = findBusPass(kieSession);
kieSession.dispose();
return busPass;
}
// …
}

As you can see, we are now exposing Drools functionality in our Spring Boot application. A service bean is injected with a reference to the Drools KieContainer. Subsequently, whenever a call is made to the getBusPass method, we instantiate a new KieSession (note the session name, which matches that defined in kmodule.xml), insert details about a person, fire rules, and see what kind of bus pass they should be given.

Finally, we need a controller.


@RestController
public class BusPassController {
private static Logger log = LoggerFactory.getLogger(BusPassController.class);
private final BusPassService busPassService;
@Autowired
public BusPassController(BusPassService busPassService) {
this.busPassService = busPassService;
}
@RequestMapping(value = "/buspass",
method = RequestMethod.GET, produces = "application/json")
public BusPass getBusPass(
@RequestParam(required = true) String name,
@RequestParam(required = true) int age) {
Person person = new Person(name, age);
log.debug("Bus pass request received for: " + person);
BusPass busPass = busPassService.getBusPass(person);
return busPass;
}
}

By annotating the controller class as @RestController, Spring will set it up as a bean and ensure that anything returned from a method is marshalled. As the getBusPass method has been defined as producing application/json, Spring will automatically use Jackson to marshal the response to JSON.

The @RequestMapping annotation indicates that you can reach the URL at /buspass. For instance, if you run up the application as it is, this means that you can send GET requests to http://127.0.0.1:8080/buspass. The @RequestParam annotations indicate that you need to send querystring arguments, providing values for “name” and “age”.

All that remains is to try it out. Please do let me know if you spot anything that you think could be improved.

12 thoughts on “A minimal Spring Boot Drools web service

  1. Many thanks for this example, it is very easy to understand and a great help to us.

    I have a question and maybe you know the answer:
    – When I tried to configure this project with decision tables, I received and exception with the message: “No RuleTable cells in spreadsheet.” Decision tables need the configuration from the XLS and the DRL file, and here I found a problem:

    I am very newbie with this matter, but debugging I found at AbstractKieModule (v6.5.0.Final) at line 335 “if (template.getDtable().equals( fileName )) {” where it is comparing the configured path against the fileName recovered from classpath. The problem here is with the Decision tables where one is loaded from the classpath resolution but the other is loaded from configuration: the first one has “BOOT-INF/classes/” and the other haven’t causing it cannot load rules because fileName and template.getDtable() are not the same.

    Do you know if there is any extra configuration to fix this? (I resolved this with a trick: adding twice the template configuration once with the “BOOT-INF/classes” and another one without this part, because I need it works when run from an IDE (normal classpath loaded) or from Spring Boot (jar packaged with BOOT-INF/classes)

    Thanks a lot again by the example!

    Like

    1. Hi there Victor. Apologies for not replying yet – I’ve been away skiing for the past week. 🙂

      If you put all the .drl, .xls, .gdst, etc. under src/main/resources/com/mycompany/mypackage the packages can be defined in the kbase XML definition. This enables the kmodule to be created for you by convention.

      If you wish to load in DRL/Excel that isn’t stored in locations which meet the convention, then I think you will need to look at creating your ksession programmatically instead. See “4.2.2.4. Defining a KieModule programmatically” in the Drools user guide: https://docs.jboss.org/drools/release/6.5.0.Final/drools-docs/html_single/index.html#BuildDeployUtilizeAndRunSection

      Like

    2. What did you do exactly? Did you place the kmodules.xml in the BOOT-INF/classes with maven-resources-plugin?

      I have the same issue although with data tables (XLS) and templates (DRT) so I need to define them in the kmodules.xml

      Like

      1. A quickly (and a little dirty) work around is to duplicate the configuration one for the packed application (Sprint Boot) and one for local development:

        With this trick the rule engine loads the configuration and can execute it.

        Another way is what @sctrcdr said before (this is the cleanest way), make an implementation or look for already implemented KieModule “loader”. I think I checked one but it doesn’t working properly with excel tables (See “4.2.2.4. Defining a KieModule programmatically” in the Drools user guide: https://docs.jboss.org/drools/release/6.5.0.Final/drools-docs/html_single/index.html#BuildDeployUtilizeAndRunSection)

        Best regards!

        Liked by 1 person

      2. Ups! Source code was removed sorry. I’m trying again removing special characters (let me know if you can understand):

        — Definition of business rules–
        kbase name=”DefaultKB” packages=”core.rules.businessRules”
        — Normal load —
        ruleTemplate
        dtable=”/core/rules/businessRules/GenericPolicy.xls”
        template=”/core/rules/businessRules/GenericBusinessRules.drt”
        row=”3″ col=”2″

        — Trick to load from a Spring Boot packaged —
        ruleTemplate
        dtable=”BOOT-INF/classes/core/rules/businessRules/GenericPolicy.xls”
        template=”BOOT-INF/classes/core/rules/businessRules/GenericBusinessRules.drt”
        row=”3″ col=”2″
        ksession name=”DefaultKS”
        kbase

        Liked by 1 person

  2. Thanks again! Then a good week skiing 😉

    I resolved it: all excels are loaded although they are not defined into the kbase XML, so I removed all not used (it is not my expected behavior.

    Your instructions are very helpfully and I think I can take advantage of them in the future.

    Thanks a lot!

    Like

  3. This example is by far one the comprehensive examples I have seen on implementation of executing drools rules to make REST calls.Thanks a lot..
    I am trying a similar project with jBPM process engine by creating a workflow with BPMN2 containing 3 REST tasks to be called at runtime.. First REST task is an “Authentication service” and then second REST task retrieves customer info when the web services is called. The third performs a “Funds Transfer service”…
    I would like to know if you have some knowledge in integrating all these REST service with the process engine for execution??

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.