Load Drools rules from the database using a template DRL file in a spring boot app
Recently for an application, we were required to use a rule engine in a Spring Boot app. The rules need to be dynamically maintained via a UI. Figuring it out was not straightforward, hence putting some code snippets here in case it helps someone else.
First, create a Spring Boot 3 application with the following dependencies:-
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
I am using Postgres as the DB, Java 17, and Drools 7.59.0.Final.
Next, add a DRL template file in your src/main/resources folder
template header
id
ifcondition
thencondition
template "tmp1"
rule "@{id}"
dialect "mvel"
when
@{ifcondition};
then
@{thencondition};
end
end template
The database table storing rules looks like this:- (Rule.java)
package com.springproject.droolEngineProject.model;
//import javax.persistence.*;
import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;
@Entity
@Table(name = "rule_table")
@Getter
@Setter
public class Rule {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
@Column(name = "ifcondition")
private String ifcondition;
@Column(name = "thencondition")
private String thencondition;
@Column(name = "version")
private int version;
// getters and setters...
}
Spring boot configuration looks like this:-
spring.datasource.url=jdbc:postgresql://localhost:5432/testdb
spring.datasource.username=postgres
spring.datasource.password=postgres
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
This configuration will take care of auto-creating the rule and other tables in Postgres.
Then I added a RestController to CRUD Rules:-
@RestController
public class RulesController {
@Autowired
private DroolRulesRepo rulesRepo;
@PostMapping("/rule")
public void addRule (@RequestBody Rule rule) {
rulesRepo.save(rule);
}
@GetMapping("/rules")
public List<Rule> getRules () {
List<Rule> rules = new ArrayList<Rule>();
rulesRepo.findAll().forEach(rules::add);
return rules;
}
}
The Rule engine is to determine discounts for Orders, so here is the OrderService executing all the rules to find the discount:-
public Order getDiscountForOrderV2(Order order) throws FileNotFoundException{
List<Rule> ruleAttributes = new ArrayList<>();
rulesRepo.findAll().forEach(ruleAttributes::add);
ObjectDataCompiler compiler = new ObjectDataCompiler();
String generatedDRL = compiler.compile(ruleAttributes, Thread.currentThread().getContextClassLoader().getResourceAsStream(DroolConfig.RULES_TEMPLATE_FILE));
KieServices kieServices = KieServices.Factory.get();
KieHelper kieHelper = new KieHelper();
//multiple such resoures/rules can be added
byte[] b1 = generatedDRL.getBytes();
Resource resource1 = kieServices.getResources().newByteArrayResource(b1);
kieHelper.addResource(resource1, ResourceType.DRL);
KieBase kieBase = kieHelper.build();
KieSession kieSession = kieBase.newKieSession();
kieSession.insert(order);
int numberOfRulesFired = kieSession.fireAllRules();
kieSession.dispose();
return order;
}
To add a rule using Postman:-
Now run an Order to get the discount:-
The full project can be found here.
#drools #springboot #ruleengine