Spring Boot + JBoss Drools Stateless Session Example (2021) | CodeUsingJava








Spring Boot + JBoss Drools Stateless Session Example (2021)


In a previous tutorial we had implemented Spring Boot + JBoss Drools Hello World Example. In this tutorial we will be learning about stateless session using Spring Boot + JBoss Drools. In case of a stateless session, a new session is created for each request. The rules engine is not aware of any changes in the facts. So any change in facts does not lead to reactivation of previously executed rules.
To better understand Stateless and Stateful sessions using Spring Boot + JBoss Drools we will be taking the example of a library System. We will be writing rules to determine if any penalty should be imposed when accepting the book returned by the user.
Spring Boot + Drools Stateless Session
The maven project we will be creating is as follows-
Spring Boot + Drools Maven
The pom.xml will be as follows-
<?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.4.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.codeusingjava</groupId>
	<artifactId>boot-drools</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>boot-drools</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>1.8</java.version>
		<drools.version>7.49.0.Final</drools.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-core</artifactId>
			<version></version>
		</dependency>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-mvel</artifactId>
			<version></version>
		</dependency>
		<dependency>
			<groupId>org.drools</groupId>
			<artifactId>drools-compiler</artifactId>
			<version></version>
		</dependency>		
		<dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
        </dependency>

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

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

</project>
Create the domain class named Book -
package com.codeusingjava.bootdrools.model;

import java.time.LocalDate;

public class Book {

	private String name;
	private int id;
	private int expectedPages;
	private int actualPages;
	private LocalDate returnDate;
	private boolean isReturnedLate;

	public Book(String name, int id, int expectedPages, int actualPages, LocalDate returnDate) {
		this.name = name;
		this.id = id;
		this.expectedPages = expectedPages;
		this.actualPages = actualPages;
		this.returnDate = returnDate;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public int getExpectedPages() {
		return expectedPages;
	}

	public void setExpectedPages(int expectedPages) {
		this.expectedPages = expectedPages;
	}

	public int getActualPages() {
		return actualPages;
	}

	public void setActualPages(int actualPages) {
		this.actualPages = actualPages;
	}

	public LocalDate getReturnDate() {
		return returnDate;
	}

	public void setReturnDate(LocalDate returnDate) {
		this.returnDate = returnDate;
	}

	public boolean isReturnedLate() {
		if (returnDate.isAfter(LocalDate.now()))
			return true;
		else
			return false;
	}

	public void setReturnedLate(boolean isReturnedLate) {
		this.isReturnedLate = isReturnedLate;
	}

	@Override
	public String toString() {
		return "Book [name=" + name + ", id=" + id + ", expectedPages=" + expectedPages + ", actualPages=" + actualPages
				+ ", returnDate=" + returnDate + "]";
	}

}
Next we will be defining the rules file named books.drl.
import  com.codeusingjava.bootdrools.model.Book;
import  java.time.LocalDate;

rule "Book returned without fine"
    when
       $book: Book( !isReturnedLate(), $bookCondition: actualPages == expectedPages)
    then
        System.out.println($book + " Book returned without fine");
end;

rule "Book returned with fine - Book Damaged"
    when
       $book: Book( $bookCondition: actualPages != expectedPages)
    then
        System.out.println($book + " Book returned with fine as book is damaged");
end;

rule "Book returned with fine - Late Return"
    when
       $book: Book( isReturnedLate() )
    then
        System.out.println($book + "Book returned with fine as book returned late");
end;
Next we define the Drools Configuration file named DroolsConfiguration. This file we create an instance of KieContainer which we will be later using to create stateless and stateful sessions.
package com.codeusingjava.bootdrools.configuration;

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;

@Configuration
public class DroolsConfiguration {

	private final KieServices kieServices = KieServices.Factory.get();

	@Bean
	public KieContainer kieContainer() {
		KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
		kieFileSystem.write(ResourceFactory.newClassPathResource("book.drl"));
		KieBuilder kb = kieServices.newKieBuilder(kieFileSystem);
		kb.buildAll();
		KieModule kieModule = kb.getKieModule();
		return kieServices.newKieContainer(kieModule.getReleaseId());
	}
}
Finally we will be exposing a GET REST API using the controller class named BookController. For this API we will be passing the following request parameters -
  1. name - Name of the book to be returned.
  2. id - Id of the book to be returned.
  3. actualPages - The actual pages of the book being returned.
  4. expectedPages - The expected pages of the book being returned.
  5. returnDate - The last date of the book to be returned.
Also in this class we will be autowiring the KieContainer instance which we created in the configuration class. Also using this KieContainer we will be getting the stateless session.
package com.codeusingjava.bootdrools.controller;

import java.time.LocalDate;

import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.StatelessKieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.codeusingjava.bootdrools.model.Book;

@RestController
public class BookController {

	@Autowired
	private KieContainer kieContainer;

	@RequestMapping(value = "/return", method = RequestMethod.GET, produces = "application/json")
	public String getInterest(@RequestParam(required = true) String name, @RequestParam(required = true) Integer id,
			@RequestParam(required = true) Integer actualPages, @RequestParam(required = true) Integer expectedPages,
			@RequestParam(required = true) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate returnDate) {
		StatelessKieSession kieSession = kieContainer.newStatelessKieSession();
		Book book = new Book(name, id, actualPages, expectedPages, returnDate);

		kieSession.execute(book);
		return "Book is returned";
	}
}
Finally create the Spring Boot Bootstrap class.
package com.codeusingjava.bootdrools;

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

@SpringBootApplication
public class BootDroolsApplication {

	public static void main(String[] args) {
		SpringApplication.run(BootDroolsApplication.class, args);
	}
}
Start the application. If we now go to localhost:8080/return?name=book1&id=3&actualPages=55&expectedPages=55&returnDate=2021-08-25
Spring Boot + Drools Example
We will now modify the created project to return the book status to the user. Create an enum named ReturnStatus as follows -
package com.codeusingjava.bootdrools.model;

public enum BookStatus {
	PENALTY, NOPENALTY, UNKNOWN
}
In the Book class create a field named status of type BookStatus
package com.codeusingjava.bootdrools.model;

import java.time.LocalDate;

public class Book {

	private String name;
	private int id;
	private int expectedPages;
	private int actualPages;
	private LocalDate returnDate;
	private boolean isReturnedLate;
	private BookStatus status;

	public Book(String name, int id, int expectedPages, int actualPages, LocalDate returnDate) {
		this.name = name;
		this.id = id;
		this.expectedPages = expectedPages;
		this.actualPages = actualPages;
		this.returnDate = returnDate;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public int getExpectedPages() {
		return expectedPages;
	}

	public void setExpectedPages(int expectedPages) {
		this.expectedPages = expectedPages;
	}

	public int getActualPages() {
		return actualPages;
	}

	public void setActualPages(int actualPages) {
		this.actualPages = actualPages;
	}

	public LocalDate getReturnDate() {
		return returnDate;
	}

	public void setReturnDate(LocalDate returnDate) {
		this.returnDate = returnDate;
	}

	public boolean isReturnedLate() {
		if (returnDate.isAfter(LocalDate.now()))
			return true;
		else
			return false;
	}

	public void setReturnedLate(boolean isReturnedLate) {
		this.isReturnedLate = isReturnedLate;
	}
	
	public BookStatus getStatus() {
		return status;
	}

	public void setStatus(BookStatus status) {
		this.status = status;
	}

	@Override
	public String toString() {
		return "Book [name=" + name + ", id=" + id + ", expectedPages=" + expectedPages + ", actualPages=" + actualPages
				+ ", returnDate=" + returnDate + "]";
	}

}

Next we will be modifying the rules file to set the book status field depending on the conditions.
import  com.codeusingjava.bootdrools.model.Book;
import  com.codeusingjava.bootdrools.model.BookStatus;
import  java.time.LocalDate;

rule "Book returned without fine"
    when
       $book: Book( !isReturnedLate(), $bookCondition: actualPages == expectedPages)
    then
        System.out.println($book + " Book returned without fine");
        $book.setStatus(BookStatus.NOPENALTY);
end;

rule "Book returned with fine - Book Damaged"
    when
       $book: Book( $bookCondition: actualPages != expectedPages)
    then
        System.out.println($book + " Book returned with fine as book is damaged");
        $book.setStatus(BookStatus.PENALTY);
end;

rule "Book returned with fine - Late Return"
    when
       $book: Book( isReturnedLate() )
    then
        System.out.println($book + "Book returned with fine as book returned late");
        $book.setStatus(BookStatus.PENALTY);
end;
Next in the controller class we will be returning the return status back to the user
package com.codeusingjava.bootdrools.controller;

import java.time.LocalDate;

import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.StatelessKieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import com.codeusingjava.bootdrools.model.Book;

@RestController
public class BookController {

	@Autowired
	private KieContainer kieContainer;

	@RequestMapping(value = "/return", method = RequestMethod.GET, produces = "application/json")
	public String getInterest(@RequestParam(required = true) String name, @RequestParam(required = true) Integer id,
			@RequestParam(required = true) Integer actualPages, @RequestParam(required = true) Integer expectedPages,
			@RequestParam(required = true) @DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate returnDate) {
		StatelessKieSession kieSession = kieContainer.newStatelessKieSession();
		Book book = new Book(name, id, actualPages, expectedPages, returnDate);

		kieSession.execute(book);
		return "Book is returned with - " + book.getStatus();
	}
}
If we now run the example we get the output as -
Spring Boot + Drools Example

Download the code-

Spring Boot + Drools Stateless Session Example