Drools Decision Table Example – Spring Boot

The drools decision table is another way of defining the user-friendly rules, and we can implement the drools decision table using the excel files with the spring boot framework.

In the previous article, we learned how to use DRL files to define the drools rules.

In this article, we will learn how to implement excel based drools rule service with the spring boot framework.

Create spring boot drools decision table example application

Create a spring boot application with the required dependencies.

Add the spring boot web starter dependency and the drools dependencies to the application’s pom XML configuration file.

The below is the complete pom file.

<?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.5.5</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.asb.example</groupId>
	<artifactId>spring-boot-drools-dicision-table</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>spring-boot-drools-dicision-table</name>
	<description>spring-boot-drools-dicision-table</description>
	<properties>
		<java.version>1.8</java.version>
		<maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
		<drools.version>7.59.0.Final</drools.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</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>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Creating the Excel drools decision table

Instead of defining the drools rules using a DRL file, we can use the user-friendly excel file.

Create an excel file with the name customer-rules.xlsx and add the content as shown in the below image.

excel decision table

The two important parts of the drools decision table are:

RuleSet

This part contains the import statements, global variables, etc.

In our example above, we are importing the OrderRequest and CustomerType classes. We are also defining the global variable orderDiscount.

RuleTable

This part contains the rule definition and conditions. We can also specify a name for the Rule Table

We use the NAME column to specify the rule name. In our example, we are defining four rules that are based on customer age, type, and order amount fields

The CONDITION columns are used to specify the condition to execute for each rule.

Also, in our example, we have four conditions used to check the customer age, type, and order amount fields.

The ACTION column specifies the action we are applying if the condition evaluation passes.

In the above example, we are adding the discount percentage for every rule and also printing the value to the application’s console log.

Add the spring boot drools configuration

Create a spring configuration class with the name DroolsConfig. The class configures the drools engine by loading the rules in the excel file.

package com.asb.example.config;

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.io.Resource;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class DroolsConfig {

	private static final String RULES_ORDER_DISCOUNT_XLS = "rules/customer-rules.xlsx";
	private static final KieServices kieServices = KieServices.Factory.get();

	@Bean
	public KieContainer kieContainer() {
		Resource dt = ResourceFactory.newClassPathResource(RULES_ORDER_DISCOUNT_XLS, getClass());
		KieFileSystem kieFileSystem = kieServices.newKieFileSystem().write(dt);
		KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem);
		kieBuilder.buildAll();
		KieModule kieModule = kieBuilder.getKieModule();
		KieContainer kieContainer = kieServices.newKieContainer(kieModule.getReleaseId());
		return kieContainer;
	}
}

Here, we pass the file customer-rules.xlsx under the src/main/resources/rules directory to the KieFileSystem.

We are using the ResourceFactory class’s newClassPathResource() method to load the excel file to the drools rule engine.

Also, we are passing this resource to the KieFileSystem and then creating a KieModule using the KieBuilder.

Finally, we configure the KieContainer as the spring bean that we will use inside the service layer of the spring boot application.

Add the model classes

Create a java POJO class with the name OrderRequest.

We will receive the order request to the REST API, and we map the values to an instance of this class. Then we will pass this object to the rule for calculating the order discount.

package com.asb.example.model;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class OrderRequest {

	private String customerNumber;
	private Integer age;
	private Integer amount;
	private CustomerType customerType;
}

Create an enum with the name EmployeeType. We use this enum to define the supported employee type to decide the order discount by the drools rules.

package com.asb.example.model;

public enum CustomerType {
	LOYAL, NEW, DISSATISFIED;

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

Create a java POJO class with the name OrderDiscount.

We use this class as a response object type. The REST API returns the response of this type to the API caller.

package com.asb.example.model;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class OrderDiscount {

	private Integer discount = 0;
}

Add the service layer

Create a spring service class with the name OrderDiscountService.

This service passes the incoming order request object to the drools rules and returns the received response to the controller layer.

package com.asb.example.service;

import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.asb.example.model.OrderDiscount;
import com.asb.example.model.OrderRequest;

@Service
public class OrderDiscountService {

	@Autowired
	private KieContainer kieContainer;

	public OrderDiscount getDiscount(OrderRequest orderRequest) {
		OrderDiscount orderDiscount = new OrderDiscount();
		KieSession kieSession = kieContainer.newKieSession();
		kieSession.setGlobal("orderDiscount", orderDiscount);
		kieSession.insert(orderRequest);
		kieSession.fireAllRules();
		kieSession.dispose();
		return orderDiscount;
	}
}

We are using the KieContainer bean instance to open a KieSession and execute the drools rules.

We are setting the global variable with the name orderDiscount, of type OrderDiscount class. This POJO class will hold the order discount value, and we also share the variable among multiple drool rules.

Add the REST endpoint

Create a spring REST API endpoint with the URL /get-discount.

We receive the OrderRequest object as the request and return back the OrderDiscount object as the response.

package com.asb.example.controller;

import org.springframework.beans.factory.annotation.Autowired;
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.RestController;

import com.asb.example.model.OrderDiscount;
import com.asb.example.model.OrderRequest;
import com.asb.example.service.OrderDiscountService;

@RestController
public class OrderDiscountController {

	@Autowired
	private OrderDiscountService orderDiscountService;

	@PostMapping("/get-discount")
	public ResponseEntity<OrderDiscount> getDiscount(@RequestBody OrderRequest orderRequest) {
		OrderDiscount discount = orderDiscountService.getDiscount(orderRequest);
		return new ResponseEntity<>(discount, HttpStatus.OK);
	}
}

Test the application

Start the spring boot application.

Invoke the /get-discount API and pass the order details as shown below.

rest api input 1

We receive the response discount value as 20, as the age of the customer is < 20, amount > 10000, and the customer type is LOYAL.

We can also observe the applied discount in the console log as shown below.

drools decision table spring

Finally, change the request JSON field value to check for the different scenarios as shown below

drools decision table spring

Here, we can observe that only one discount is applied.

drools decision table spring

Conclusion

In this article, we learned how to implement excel based drools rule table with the spring boot framework.

The example code is available on Github.

Leave a Reply