Introduction

We will see an example on drools support in Spring Boot 2. We have seen in past example on how to integrate drools in Spring application. We had used Spring Boot version 1.5.9 but here we will support Spring Boot 2.1.5 version. I will not explain about drools in this post and if you want to know on drools then you can check my previous example how to integrate drools in Spring application.

Prerequisites

Eclipse Neon, Java 1.8, Gradle 5.4.1, Drools 7.22.0

Example with Source Code

Creating Project

Create a gradle based project in Eclipse, the project structure may look to the similar image as shown below:

drools support in Spring Boot 2

Updating Build File

Update the content of the default generated build.gradle script to include the required dependencies.

We are using Spring Boot 2.1.5 and Drools 7.22.0.

buildscript {
	ext {
		springBootVersion = '2.1.5.RELEASE'
	}

    repositories {
        mavenCentral()
    }

    dependencies {
        classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
    }
}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'

repositories {
    mavenCentral()
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

dependencies {
	compile("org.springframework.boot:spring-boot-starter-web:${springBootVersion}")
	//Drools and KIE components
	compile("org.kie:kie-spring:7.22.0.Final") 
	compile("org.drools:drools-compiler:7.22.0.Final")
}

Drools Configuration

We want to use Java based configurations so create below java class to create all the required configurations. We will put all rule files under src/main/resources/rules directory.

We have defined few beans for our Spring and Drools integration.

We have created KieFileSystem bean that finds all the rule files put under the classpath file system. These rule files define the rules, which would be applied on the object fields for validation.

We have defined KieContainer bean which is used to create different components, such as, KieSession that is required to create before fire rules on the drools file.

package com.jeejava.spring.drools.config;

import java.io.IOException;

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.builder.KieRepository;
import org.kie.api.builder.ReleaseId;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

@Configuration
public class SpringDroolsConfig {

	private static final String RULES_PATH = "rules/";

	@Bean
	public KieFileSystem kieFileSystem() throws IOException {
		KieFileSystem kieFileSystem = getKieServices().newKieFileSystem();
		for (Resource file : getRuleFiles()) {
			kieFileSystem.write(ResourceFactory.newClassPathResource(RULES_PATH + file.getFilename(), "UTF-8"));
		}

		return kieFileSystem;
	}

	private Resource[] getRuleFiles() throws IOException {
		ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
		return resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "**/*.*");
	}

	@Bean
	public KieContainer kieContainer() throws IOException {
		final KieRepository kieRepository = getKieServices().getRepository();

		kieRepository.addKieModule(new KieModule() {
			public ReleaseId getReleaseId() {
				return kieRepository.getDefaultReleaseId();
			}
		});

		KieBuilder kieBuilder = getKieServices().newKieBuilder(kieFileSystem());
		kieBuilder.buildAll();

		return getKieServices().newKieContainer(kieRepository.getDefaultReleaseId());
	}

	private KieServices getKieServices() {
		return KieServices.Factory.get();
	}

}

Creating Custom Exception

We will create custom exception class to throw exception when validation fails. We will create NumberFormatException class to throw number format exception for non-integer value.

package com.jeejava.spring.drools.exception;

public class NumberFormatException extends RuntimeException {

	private static final long serialVersionUID = 1L;

	public NumberFormatException(String msg) {
		super(msg);
	}

}

Creating Vo Class

We will create VO class to use on REST controller.

We will apply validation on code using drools.

package com.jeejava.spring.drools.vo;

public class AreaPin {

	private int code;

	public AreaPin(int code) {
		this.code = code;
	}

	public int getCode() {
		return code;
	}

}

Creating Rule Validate File

Create a file called PinCodeValidation.drl to perform validation on pin code. We will check whether the pin code is 6 digits integer value or not.

The rule validation file must be put into src/main/resources/rules folder as we mentioned earlier. You may put into other location and accordingly you have to change the configuration.

You find AreaPin in when clause is the object and field code is accessed directly by its property name in the AreaPin class.

package com.jeejava.spring.drools.rules

import com.jeejava.spring.drools.vo.AreaPin;
import com.jeejava.spring.drools.exception.NumberFormatException;

rule "PinCodeValidation"
	when
		AreaPin(code != 0 && code not matches "^[0-9]{6}$")
	then
		throw new NumberFormatException("Invalid Area Pin Code. Must be a valid 6 digits number.");
end

Creating REST Controller

The Spring REST controller that handles end users requests/responses is given below.

We want to search the area name for a given pin code using REST service. So before we search into the Map whether the area name will be returned or not, we perform validation on pin code and rule is fired using drools.

Notice how we autowire KieContainer and create KieSession and fire the rule on AreaPin object.

If pin code validation fails then you will see exception in the console as well as on the web page or REST client.

If pin code validation passes then area name will be returned if found or Area not found message will e returned as a default value from the Map.

package com.jeejava.spring.drools.rest.controller;

import java.util.HashMap;
import java.util.Map;

import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

import com.jeejava.spring.drools.vo.AreaPin;

@RestController
public class AreaRestController {

	@Autowired
	private KieContainer kieContainer;

	@GetMapping("/area/name/{pinCode}")
	public ResponseEntity<String> getAreaByPinCode(@PathVariable int pinCode) {
		KieSession kieSession = kieContainer.newKieSession();
		kieSession.insert(new AreaPin(pinCode)); // which object to validate
		kieSession.fireAllRules(); // fire all rules defined into drool file (drl)
		kieSession.dispose();

		return new ResponseEntity<String>(getAreaByCode(pinCode), HttpStatus.OK);
	}

	private String getAreaByCode(int pin) {
		final Map<Integer, String> areaMap = new HashMap<>();

		areaMap.put(700001, "BBD Bag");
		areaMap.put(700010, "Beliaghata");
		areaMap.put(700105, "Nabapally");
		areaMap.put(700098, "Sukanta Nagar");

		return areaMap.getOrDefault(pin, "Area not found");
	}
}

Creating Main Class

A class with main method and @SpringBootApplication is enough to start the Spring Boot application.

package com.jeejava.spring.drools;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(scanBasePackages = "com.jeejava.spring.drools")
public class SpringBoot2Drools {

	public static void main(String[] args) {
		SpringApplication.run(SpringBoot2Drools.class, args);
	}

}

Testing the Application

Run the above main class to deploy into Tomcat server and start the application.

Once the application deployed, it starts on port number 8080 as it is a default port of Tomcat. You may also override this port number in application.properties or application.yml file using key server.port.

If you hit below URL, you will get below output on the browser

drools support in Spring Boot 2

If you hit below URL, you will see below exception on page as well in console.

drools support in spring boot 2

Console error is given below:

[Request processing failed; nested exception is Exception executing consequence for rule "PinCodeValidation" in com.jeejava.spring.drools.rules: com.jeejava.spring.drools.exception.NumberFormatException: Invalid Area Pin Code. Must be a valid 6 digits number.]

Source Code

You can download source code.

Thanks for reading.

Leave a Reply

Your email address will not be published. Required fields are marked *