Spring Boot with Drools Engine

In this blog we would see how Spring boot easily integrates with Drools Rule Engine.

Praveen G. Nair
CodeX

--

Spring Boot and Drools Rule Engine Integration

Rule engines are most commonly used integration pattern when we have to implement most complex business rules.

The Drools is an open-source Business Rule Management System (BRMS) that can be integrated easily with many applications. It is written in 100% pure Java, runs on any JVM and is available in the Maven Central repository too.

We would today explore and implement an integration with Spring boot.

KIE (Knowledge Is Everything) is an umbrella project introduced to bring the related technologies together under one roof. Hence KIE is the main core component which integrates with Spring boot.

In this blog, we will see how to integrate the Drools rule engine and implement a demo rule engine service using spring boot.

We would also use the DRL (Drools Rule Language) rules by creating the .drl file inside the spring boot application.

1. Add maven dependency

Let’s create a basic spring boot application and add below drools dependency to pom.xml.

<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.6</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.praveen.drools.example</groupId>
<artifactId>springboot-drools-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-drools-demo</name>
<description>Demo project for Spring Boot with Drools Engine</description>
<properties>
<java.version>11</java.version>
<drools.version>7.67.0.Final</drools.version>
<springfox-swagger2.version>3.0.0</springfox-swagger2.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>${drools.version}</version>
</dependency>

<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>${drools.version}</version>
</dependency>

<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-decisiontables</artifactId>
<version>${drools.version}</version>
</dependency>

<!-- swagger ui -->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
<version>${springfox-swagger2.version}</version>
</dependency>

<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-swagger-ui</artifactId>
<version>${springfox-swagger2.version}</version>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>

2. Configure the Drools Application

Create a configuration java class with the name DroolsConfig.java and add the below configuration to the java class.

package com.praveen.drools.example.configuration;

import com.praveen.drools.example.service.CustomerCategorizeService;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieModule;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
* Drools Config.
*
@author Praveen.Nair
*/
@Configuration
public class DroolsConfig {

private static final String RULES_CUSTOMER_RULES_DRL = "rules/customer-category.drl";

@Bean
public KieContainer kieContainer() {
final KieServices kieServices = KieServices.Factory.get();
KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_CUSTOMER_RULES_DRL));
KieBuilder kb = kieServices.newKieBuilder(kieFileSystem);
kb.buildAll();
KieModule kieModule = kb.getKieModule();
return kieServices.newKieContainer(kieModule.getReleaseId());
}
}

The config class creates a spring bean KieContainer to build the rule engine by loading the rule files under the application’s /resources folder. We create the KieFileSystem instance and load the DRL file from the application’s resources directory.

Finally, we use KieService and KieBuilder to create a KieContainer and configure it as a spring bean.

3. Create the model classes

Create Pojo class with the name CustomerRequest and define the below fields.

We receive this class as a request object to the rule engine and also we send these fields as the input to defined rules to derive the customerType for the given customer request.

Also define a java enum with the name CustomerCategory.java as shown below. The enum holds the customer categories and based on the value, the rule engine derives the customer type.

package com.praveen.drools.example.model;

/**
* Customer Categories.
*/
public enum CustomerCategory {

GENERAL, KIDS, SENIOR_CITIZEN, SUSPENDED;

public String getValue() {
return this.toString();
}
}

Finally, create a response POJO class with the name CustomerType as shown below.

package com.praveen.drools.example.model;

import java.util.Objects;
import java.util.StringJoiner;

/**
* CustomerType Response model.
*
@author Praveen.Nair
*/
public class CustomerType {

private CustomerCategory customerType;

public CustomerCategory getCustomerType() {
return customerType;
}

public void setCustomerType(CustomerCategory customerType) {
this.customerType = customerType;
}
}

4. Define Drools Rules

Create a drools rules file with the name customer-category.drl and place the file under the directory /src/main/resources/rules.

import com.praveen.drools.example.model.CustomerRequest
import com.praveen.drools.example.model.CustomerCategory;
global com.praveen.drools.example.model.CustomerType customerType;

dialect "mvel"

rule "Categorize customer based on age"
when
CustomerRequest(age < 20)
then
customerType.setCustomerType(CustomerCategory.KIDS);
end

rule "Categorize senior citizen customer based on age"
when
CustomerRequest(age > 50)
then
customerType.setCustomerType(CustomerCategory.SENIOR_CITIZEN);
end

rule "Categorize customer based on number of orders"
when
CustomerRequest(numberOfOrders == 0)
then
customerType.setCustomerType(CustomerCategory.SUSPENDED);
end

rule "Categorize customer general case"
when
CustomerRequest((gender == "M" || gender == "F") && age > 20 && age < 50)
then
customerType.setCustomerType(CustomerCategory.GENERAL);
end

We need to import the models that are being used in the DRL file. We are also using a global parameter with the name customerType. The global parameter can be shared between multiple rules.

The DRL file can contain one or multiple rules. We can use the mvel syntax to specify the rules. Also, each rule can be described with a description using the rule keyword.

Then we define when-then syntax to specify the conditions for a rule. Based on the input values of the Customer request, we are deriving the customerType to the result.

5. Add the controller and Service layer

Create a service java class with the name CustomerCategorizeService, and add the below content.

package com.praveen.drools.example.service;

import com.praveen.drools.example.model.CustomerRequest;
import com.praveen.drools.example.model.CustomerType;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;

/**
* Customer Categorization service.
*
@author Praveen.Nair
*/
public class CustomerCategorizeService {

private final KieContainer kieContainer;

public CustomerCategorizeService(KieContainer kieContainer) {
this.kieContainer = kieContainer;
}

public CustomerType getCustomerType(CustomerRequest customerRequest) {
CustomerType customerType = new CustomerType();
KieSession kieSession = kieContainer.newKieSession();
kieSession.setGlobal("customerType", customerType);
kieSession.insert(customerRequest);
kieSession.fireAllRules();
kieSession.dispose();
return customerType;
}
}

Now we are injecting the KieContainer instance and creating a KieSession instance. We are also setting a global parameter of type CustomerType, that will hold the rules execution result.

To pass the request object to the DRL file, we can use the insert() method. Then we fire all the rules by calling the fireAllRules() method and finally terminate the session by calling the dispose() method of the KieSession.

expose a POST API with the endpoint /api/getCustomerType. The endpoint expects an CustomerRequest object and returns the CustomerType response. controller content looks like below :

package com.praveen.drools.example.web;

import com.praveen.drools.example.model.CustomerRequest;
import com.praveen.drools.example.model.CustomerType;
import com.praveen.drools.example.service.CustomerCategorizeService;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/getCustomerType")
public class CustomerCategorizeController {

private final CustomerCategorizeService customerCategorizeService;

public CustomerCategorizeController(
CustomerCategorizeService customerCategorizeService) {
this.customerCategorizeService = customerCategorizeService;
}

@PostMapping
public ResponseEntity<CustomerType> getCustomer(@RequestBody CustomerRequest customerRequest) {
CustomerType customerType = customerCategorizeService.getCustomerType(customerRequest);
return new ResponseEntity<>(customerType, HttpStatus.OK);
}

}

6. Add Swagger Config (Optional)

Add swagger config to the application to get a swagger page.

Execute some tests using below curl or run via swagger.

  1. if numberOfOrders == 0 then customerType = SUSPENDED
curl -X POST "http://localhost:8080/api/getCustomerType" -H "accept: */*" -H "Content-Type: application/json" -d "{ \"age\": 0, \"gender\": \"M\", \"id\": 0, \"numberOfOrders\": 0}"Response :
{
"customerType": "SUSPENDED"
}

2. if age > 0 and numberOfOrders ! = 0 then customerType = KIDS

curl -X POST "http://localhost:8080/api/getCustomerType" -H "accept: */*" -H "Content-Type: application/json" -d "{ \"age\": 10, \"gender\": \"M\", \"id\": 0, \"numberOfOrders\": 1}"Response :
{
"customerType": "KIDS"
}

3. if age>50 and numberOfOrder!=0 then customerType = SENIOR_CITIZEN

curl -X POST "http://localhost:8080/api/getCustomerType" -H "accept: */*" -H "Content-Type: application/json" -d "{ \"age\": 60, \"gender\": \"M\", \"id\": 0, \"numberOfOrders\": 1}"Response :
{
"customerType": "SENIOR_CITIZEN"
}

Summary

In this blog, we have learned how to create the drools rule engine using the spring boot framework. We have also seen how we can use the DRL files to define the business rules.

Hope you have enjoyed the learning. I have pushed the entire code into github.
If you don’t want to miss similar contents follow and subscribe to my medium handle. Happy coding !!

--

--

Praveen G. Nair
CodeX
Writer for

I am a Software Developer and a Technologist. Interested in all cool stuffs of software development, Machine Learning and Cloud. https://praveeng-nair.web.app/