PolarSPARC |
Java Rules Engine - Drools :: Part 1
Bhaskar S | 06/05/2021 |
Overview
Typical business applications comprise of two aspects - the core application logic and some business rules. The core logic does not change that often and remains static over time, while the business rules tend to change quite frequently over time. As an example, consider a retailer who offers promotions on different products based on different situations. The promotions will have to be coded as business rules as they will have to change often based on different situations. If the business rules are coded as Java class(es), then they will have to change every time the promotions change. This results in a tight coupling between the business rules and the core application logic. To accomodate for flexiblity of promotions, we need to externalize the business rules from the core application logic so they can be changed independent of the core application logic. This is where Rules Engine like Drools comes into play.
Setup
The setup will be on a Ubuntu 20.04 LTS based Linux desktop. Ensure at least Java 11 or above is installed and setup. Also, ensure Apache Maven is installed and setup.
We will use Drools with Spring Boot for all the demonstrations in this article.
To setup the root Java directory structure for the demonstrations in this article, execute the following commands:
$ cd $HOME
$ mkdir -p $HOME/java/Drools
$ cd $HOME/java/Drools
The following is the listing for the parent Maven project file pom.xml that will be located at $HOME/java/Drools:
<?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> <groupId>com.polarsparc</groupId> <artifactId>Drools</artifactId> <version>1.0</version> <packaging>pom</packaging> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.5.0</version> </parent> <properties> <java.version>11</java.version> <maven.compiler.source>11</maven.compiler.source> <maven.compiler.target>11</maven.compiler.target> <drools.version>7.54.0.Final</drools.version> <slf4j.version>1.7.30</slf4j.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-logging</artifactId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.kie</groupId> <artifactId>kie-api</artifactId> <version>${drools.version}</version> <scope>provided</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> <dependency> <groupId>org.drools</groupId> <artifactId>drools-templates</artifactId> <version>${drools.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>${slf4j.version}</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>${java.version}</source> <target>${java.version}</target> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectors</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project>
Drools Overview
The Drools rules engine uses a declarative style of programming. What this means is that one defines a set of business rules as Condition-Action pairs without defining the sequence of flow that must be followed. As the domain data flows through the rules engine, and if the Condition part matches, the Action part is executed. A subtle point here - the order of the rules execution is not based on the order of the rules defined in the rules engine. In other words, with the declarative approach, one does not specify the sequence of rules execution flow. It is determined by the conditions of the domain data flowing through the rules engine and triggers the execution of the actions.
The following are some of the terminology used to describe the Drools rules engine:
Rules :: the busines logic that is expressed as a Condition and Action pair
Rule Set :: a set of Rules that are logically grouped together and deployed
Facts :: the domain data objects that flow through the rules engine for evaluation of the conditions in the Rule Set
Production Memory :: the memory location where the deployed Rules are stored
Working Memory :: the memory location where the Facts are stored
Pattern Matcher :: the core component of rules engine that matches the conditions in the Rule Set based on the Facts. The set of Rules whose conditions have matched is referred to as the activated Rules
Agenda :: the memory location where all the activated Rules are stored
The following is the high-level architecture of Drools rules engine:
At a high level, when an application starts with Drools, the Rules are loaded into the Production Memory. As the application starts to add Facts into Drools, they get stored in the Working Memory and the Pattern Matcher identifies all the Rules from the Production Memory whose conditions match the various Facts in the Working Memory. The matched Rules are then activated in the Agenda for execution.
Drools Core Components
Every core component in Drools revolves around knowledge - hence we will observe the prefix KIE, which stands for Knowledge Is Everything in all the core components.
The following are some of the core components used in Drools rules engine:
KieServices :: is a thread-safe singleton that acts as a factory for all the other core components of Drools
KieBase :: is a repository of the knowledge definitions for an application. It contains the main assets of the rules engine such as the rules, the types, the functions, etc. One important point - it does not contain the runtime assets such as the domain data objects. It is heavy to create an instance of this object and hence it is recommended that it be cached where possible
KieSession :: represents an instance of the rules engine (with all the rules of the associated KieBase) and allows an application to interact with it. It stores and executes the rules on the application domain data at runtime
KieModule :: is the main container for definitions (along with their configurations) of all the application specific KieBases and their associated KieSessions. Under-the-hood it has a structure similar to that of a Maven project
KieContainer :: is the container of all the runtime KieBase instances (defined in the KieModule) from which one can create the associated runtime instances of the KieSession
The following is the pictorial representation of the Drools core components:
Hands-on with Drools
In the First application, we will have the application compute the cost of shipping based on the given weight.
First Application
To setup the Java directory structure for the First application, execute the following commands:
$ cd $HOME/java/Drools
$ mkdir -p $HOME/java/Drools/First
$ mkdir -p First/src/main/java First/src/main/resources First/target
$ mkdir -p First/src/main/resources/com/polarsparc/first
$ cd $HOME/java/Drools/First
The following is the listing for the Maven project file pom.xml that will be used:
<?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>com.polarsparc</groupId> <artifactId>Drools</artifactId> <version>1.0</version> </parent> <artifactId>First</artifactId> <version>1.0</version> <name>First</name> </project>
The following is the listing for the slf4j-simple logger properties file simplelogger.properties located in the directory src/main/resources:
# ### SLF4J Simple Logger properties # org.slf4j.simpleLogger.defaultLogLevel=info org.slf4j.simpleLogger.showDateTime=true org.slf4j.simpleLogger.dateTimeFormat=yyyy-MM-dd HH:mm:ss:SSS org.slf4j.simpleLogger.showThreadName=true
The following is the listing for the Spring Boot application properties file application.properties located in the directory src/main/resources:
# ### Spring Boot Application properties # spring.main.banner-mode=off
The following is the Drools rules set file (with file extension .drl) called src/main/resources/com/polarsparc/first/first.drl, that computes the shipping cost based on the given weight:
/* * Name: first.drl * Author: Bhaskar S * Date: 06/05/2021 * Blog: https://www.polarsparc.com */ package com.polarsparc.first; import com.polarsparc.first.model.First; import org.slf4j.Logger; global org.slf4j.Logger log; rule "Rule 1" when $f: First(weight > 0 && weight <= 15) then log.info("Shipping Price I - the weight {} will be charged $ 12.00", $f.getWeight()); end rule "Rule 2" when $f: First(weight > 15 && weight <= 25) then log.info("Shipping Price II - the weight {} will be charged $ 24.00", $f.getWeight()); end rule "Rule 3" when $f: First(weight > 25) then log.info("Shipping Price III - the weight {} will be charged $ 48.00", $f.getWeight()); end
The general format of each Drools rule is as follows:
package PACKAGE_NAME; import IMPORT_STATEMENTS; global G_TYPE G_NAME; rule "RULE_NAME" when R_VARIABLE: R_CONDITION then ACTION_STATEMENTS; end
The following are the explanations:
The PACKAGE_NAME is similar to that in Java for namespace isolation
The IMPORT_STATEMENTS is similar to that in Java to import Java classes to be used in the rule
The G_TYPE is the Java class that is defined external to this rule
The G_NAME is the corresponding name associated with externally defined data object
The RULE_NAME is the human friendly name for the rule
The R_CONDITION is the condition to match for a given data object
The R_VARIABLE is the variable that references the data object when the condition is met
The ACTION_STATEMENTS is the action part of the rule that could reference R_VARIABLE
The following is the Java POJO that encapsulates the weight:
/* * Name: First * Author: Bhaskar S * Date: 06/05/2021 * Blog: https://www.polarsparc.com */ package com.polarsparc.first.model; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.ToString; @Getter @AllArgsConstructor @ToString public class First { private final int weight; }
The following is the Java Config that defines the desired Drools container bean:
/* * Name: FirstDroolsConfig * Author: Bhaskar S * Date: 06/05/2021 * Blog: https://www.polarsparc.com */ package com.polarsparc.first.config; import org.kie.api.KieServices; import org.kie.api.builder.*; import org.kie.api.io.KieResources; import org.kie.api.runtime.KieContainer; import org.springframework.beans.factory.BeanCreationException; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FirstDroolsConfig { private final static String FIRST_DRL = "com/polarsparc/first/first.drl"; @Bean public KieContainer firstKieContainer() { KieServices services = KieServices.Factory.get(); KieResources resources = services.getResources(); KieFileSystem fileSystem = services.newKieFileSystem(); fileSystem.write(resources.newClassPathResource(FIRST_DRL)); KieBuilder builder = services.newKieBuilder(fileSystem); Results results = builder.buildAll().getResults(); if (results.hasMessages(Message.Level.ERROR)) { throw new BeanCreationException("Error building rules: " + results.getMessages()); } KieModule module = builder.getKieModule(); return services.newKieContainer(module.getReleaseId()); } }
The code from Listing.3 above needs some explanation:
@Configuration is a class level annotation and is used to tag a class as the source of bean definitions.
@Bean is a method level annotation to define a bean. By default, the method name is used as the name of the bean.
The interface org.kie.api.io.KieResources is a factory that provides the implementations of the various IO wrappers for the application resources such as the rules, etc. The method newClassPathResource() locates the specified rules file from the project resources directory at src/main/resources via the classpath.
The interface org.kie.api.builder.KieFileSystem is an in-memory file system that is used to programatically compose a KieModule, which under-the-hood encapsulates the core components KieBase and KieSession. The method write() is used to add the specified rules file as a resource.
The interface org.kie.api.builder.KieBuilder is a builder for the resources (such as rules) contained in a KieModule. The method buildAll() builds a default KieBase with the added rules file. The method getResults() returns the results from the build processs.
The following is the main Spring Boot application to test the Drools rules engine:
/* * Name: FirstApplication * Author: Bhaskar S * Date: 06/05/2021 * Blog: https://www.polarsparc.com */ package com.polarsparc.first; import com.polarsparc.first.model.First; import lombok.extern.slf4j.Slf4j; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @Slf4j public class FirstApplication implements ApplicationRunner { private KieContainer container; @Autowired public void setKieContainer(KieContainer container) { this.container = container; } public static void main(String[] args) { SpringApplication.run(FirstApplication.class, args); } @Override public void run(ApplicationArguments args) { log.info("ReleaseId: {}", container.getReleaseId()); // Test 1 KieSession ks1 = container.newKieSession(); ks1.setGlobal("log", log); First f1 = new First(12); ks1.insert(f1); ks1.fireAllRules(); ks1.dispose(); log.info("[1] First: f1 = {}", f1); // Test 2 KieSession ks2 = container.newKieSession(); ks2.setGlobal("log", log); First f2 = new First(23); ks2.insert(f2); ks2.fireAllRules(); ks2.dispose(); log.info("[2] First: f2 = {}", f2); // Test 3 KieSession ks3 = container.newKieSession(); ks3.setGlobal("log", log); First f3 = new First(36); ks3.insert(f3); ks3.fireAllRules(); ks3.dispose(); log.info("[3] First: f3 = {}", f3); log.info("Done !!!"); } }
The code from Listing.4 above needs some explanation:
@SpringBootApplication is a class level annotation that triggers automatic scan of all the beans and auto configurations them.
@Slf4j is a class level annotation that causes Lombok to generate an instance of org.slf4j.Logger as a variable log.
The interface org.springframework.boot.ApplicationRunner indicates that a bean should run as a standalone Java application.
The method setGlobal() on a KieSession allows one to pass an external object to the rules engine. In this example, we are passing a reference to the Logger.
The method insert() allows one to add a fact to the rules engine. In other words, it allows one to add an application object to the working memory of the rules engine. This in-turn triggers the evaluation of the rules and all the matches rules are activated in the agenda of the rules engine.
The method fireAllRules() triggers the execution of all the activated rules in the agenda of the rules engine.
The method dispose() is *VERY* important and needs to be called in the end. It allows one to free-up all the KieSession resources and allows for it to be garbage collected.
To execute the code from Listing.4, open a terminal window and run the following commands:
$ cd $HOME/java/Drools/First
$ mvn spring-boot:run
The following would be the typical output:
2021-06-05 13:31:10:339 [main] INFO com.polarsparc.first.FirstApplication - Starting FirstApplication using Java 15.0.2 on polarsparc with PID 16908 (/home/polarsparc/java/Drools/First/target/classes started by polarsparc in /home/polarsparc/java/Drools/First) 2021-06-05 13:31:10:340 [main] INFO com.polarsparc.first.FirstApplication - No active profile set, falling back to default profiles: default 2021-06-05 13:31:11:422 [main] INFO com.polarsparc.first.FirstApplication - Started FirstApplication in 1.358 seconds (JVM running for 1.611) 2021-06-05 13:31:11:423 [main] INFO org.springframework.boot.availability.ApplicationAvailabilityBean - Application availability state LivenessState changed to CORRECT 2021-06-05 13:31:11:424 [main] INFO com.polarsparc.first.FirstApplication - ReleaseId: org.default:artifact:1.0.0 2021-06-05 13:31:11:424 [main] INFO com.polarsparc.first.FirstApplication - KieBases: [defaultKieBase] 2021-06-05 13:31:11:424 [main] INFO com.polarsparc.first.FirstApplication - KieBase: defaultKieBase, KieSessions: [defaultStatelessKieSession, defaultKieSession] 2021-06-05 13:31:11:424 [main] INFO org.drools.compiler.kie.builder.impl.KieContainerImpl - Start creation of KieBase: defaultKieBase 2021-06-05 13:31:11:466 [main] INFO org.drools.compiler.kie.builder.impl.KieContainerImpl - End creation of KieBase: defaultKieBase 2021-06-05 13:31:11:511 [main] INFO com.polarsparc.first.FirstApplication - Shipping Price I - the weight 12 will be charged $ 12.00 2021-06-05 13:31:11:515 [main] INFO com.polarsparc.first.FirstApplication - [1] First: f1 = First(weight=12) 2021-06-05 13:31:11:518 [main] INFO com.polarsparc.first.FirstApplication - Shipping Price II - the weight 23 will be charged $ 24.00 2021-06-05 13:31:11:518 [main] INFO com.polarsparc.first.FirstApplication - [2] First: f2 = First(weight=23) 2021-06-05 13:31:11:521 [main] INFO com.polarsparc.first.FirstApplication - Shipping Price III - the weight 36 will be charged $ 48.00 2021-06-05 13:31:11:521 [main] INFO com.polarsparc.first.FirstApplication - [3] First: f3 = First(weight=36) 2021-06-05 13:31:11:522 [main] INFO com.polarsparc.first.FirstApplication - Done !!! 2021-06-05 13:31:11:523 [main] INFO org.springframework.boot.availability.ApplicationAvailabilityBean - Application availability state ReadinessState changed to ACCEPTING_TRAFFIC [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 2.971 s [INFO] Finished at: 2021-06-05T13:31:11-04:00 [INFO] ------------------------------------------------------------------------
In the Second application, we will have the application display the supplier name, the product, and the product cost (per the supplier).
Second Application
To setup the Java directory structure for the Second application, execute the following commands:
$ cd $HOME/java/Drools
$ mkdir -p $HOME/java/Drools/Second
$ mkdir -p Second/src/main/java Second/src/main/resources Second/target
$ mkdir -p Second/src/main/resources/com/polarsparc/second
$ cd $HOME/java/Drools/Second
The following is the listing for the Maven project file pom.xml that will be used:
<?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>com.polarsparc</groupId> <artifactId>Drools</artifactId> <version>1.0</version> </parent> <artifactId>Second</artifactId> <version>1.0</version> <name>Second</name> </project>
The contents of the simplelogger.properties and application.properties located in the directory src/main/resources will be identical to the one from the First application listed above and hence we will not show them here again.
The following is the Drools rules set file called src/main/resources/com/polarsparc/second/second.drl, that display the supplier name, the product, and the product cost (per the supplier):
/* * Name: second.drl * Author: Bhaskar S * Date: 06/05/2021 * Blog: https://www.polarsparc.com */ package com.polarsparc.second; import com.polarsparc.second.model.Second; import org.slf4j.Logger; global org.slf4j.Logger log; rule "Rule One" when $s: Second() then log.info("Second - supplier: {}, product: {}, price: {}", $s.getSupplier(), $s.getProduct(), $s.getPrice()); end
The following is the Java POJO that encapsulates the supplier-product details:
/* * Name: Second * Author: Bhaskar S * Date: 06/05/2021 * Blog: https://www.polarsparc.com */ package com.polarsparc.second.model; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; import lombok.ToString; @Getter @Setter @AllArgsConstructor @ToString public class Second { private String supplier; private String product; private double price; }
The following is the Java Config that defines the desired Drools container bean:
/* * Name: SecondDroolsConfig * Author: Bhaskar S * Date: 06/05/2021 * Blog: https://www.polarsparc.com */ package com.polarsparc.second.config; import org.kie.api.KieServices; import org.kie.api.builder.*; import org.kie.api.builder.model.KieBaseModel; import org.kie.api.builder.model.KieModuleModel; import org.kie.api.builder.model.KieSessionModel; import org.kie.api.conf.EqualityBehaviorOption; import org.kie.api.conf.EventProcessingOption; import org.kie.api.io.KieResources; import org.kie.api.io.Resource; import org.kie.api.io.ResourceType; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.conf.ClockTypeOption; import org.springframework.beans.factory.BeanCreationException; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class SecondDroolsConfig { private final static String SECOND_DRL = "src/main/resources/com/polarsparc/second/second.drl"; @Bean public KieContainer secondKieContainer() { KieServices services = KieServices.Factory.get(); ReleaseId releaseId = services.newReleaseId("com.polarsparc.second", "second", "1.0"); KieFileSystem fileSystem = services.newKieFileSystem(); KieResources resources = services.getResources(); Resource drlResource = resources.newFileSystemResource(SECOND_DRL) .setResourceType(ResourceType.DRL); KieModuleModel model = services.newKieModuleModel(); KieBaseModel base = model.newKieBaseModel("second-base") .setDefault(true) .addPackage("com.polarsparc.second") .setEqualsBehavior(EqualityBehaviorOption.EQUALITY) .setEventProcessingMode(EventProcessingOption.CLOUD); base.newKieSessionModel("second-session") .setDefault(true) .setType(KieSessionModel.KieSessionType.STATEFUL) .setClockType(ClockTypeOption.REALTIME); fileSystem.generateAndWritePomXML(releaseId) .write(drlResource) .writeKModuleXML(model.toXML()); KieBuilder builder = services.newKieBuilder(fileSystem); Results results = builder.buildAll().getResults(); if (results.hasMessages(Message.Level.ERROR)) { throw new BeanCreationException("Error building rules: " + results.getMessages()); } KieModule module = builder.getKieModule(); return services.newKieContainer(module.getReleaseId()); } }
The code from Listing.7 above needs some explanation:
The interface org.kie.api.builder.ReleaseId is the Maven like 3-pair tuple resource identifier. As indicated earlier, the KieModule has a Maven like structure. This implies it has an associated 3-pair tuple - the groupID, the artifactId, and a version.
The interface org.kie.api.io.Resource is a resource wrapper for the application resources such as the rules, etc.
The interface org.kie.api.builder.model.KieModuleModel is a factory for programatically creating a KieModule.
The interface org.kie.api.builder.model.KieBaseModel is a factory for programatically creating a KieBase. The method setDefault() allows one to set the created KieBase as the default instance. The method addPackage() isolates the created KieBase to the specified rules package (which is com.polarsparc.second for this example).
The enum EqualityBehaviorOption has two values - EQUALITY and IDENTITY. It defines how Drools will behave when a new fact (domain object) is inserted into the working memory. With EQUALITY, when a fact object is inserted, a new reference to the fact object is created and added to the working memory only if the fact object is not equal to a similar fact object. On the other hand, with IDENTITY, a new reference to the fact object is created and added to the working memory only if the same fact object is not already present.
The enum EventProcessingOption has two values - CLOUD and STREAM. In the CLOUD mode, every fact object inserted into the working memory is treated without any regard for time (as is individually). On the other hand, in the STREAM mode, Drools will try to correlate the fact objects inserted into the working memory. In other words, this mode is suitable for complex events processing.
The method newKieSessionModel() returns an object that is a factory for programatically creating a KieSession.
The enum KieSessionModel.KieSessionType has two values - STATEFUL and STATELESS. In the STATEFUL mode, the KieSession is long-lived and allows for iterative changes to the state of the fact objects. In other words, the rules engine continuously matches and processes rules as the state of the fact objects change. On the other hand, the STATELESS mode, is for simple one-off execution of the rules and any state changes to the fact objects does not trigger additional rules processing.
The option ClockTypeOption has two values - PSEUDO and REALTIME. In the PSEUDO mode, the clock is controlled by the application, while in the REALTIME mode, the clock is determined by the system clock.
The following is the main Spring Boot application to test the Drools rules engine:
/* * Name: SecondApplication * Author: Bhaskar S * Date: 06/05/2021 * Blog: https://www.polarsparc.com */ package com.polarsparc.second; import com.polarsparc.second.model.Second; import lombok.extern.slf4j.Slf4j; import org.kie.api.runtime.KieContainer; import org.kie.api.runtime.KieSession; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @Slf4j public class SecondApplication implements ApplicationRunner { private KieContainer container; @Autowired public void setKieContainer(KieContainer container) { this.container = container; } public static void main(String[] args) { SpringApplication.run(SecondApplication.class, args); } @Override public void run(ApplicationArguments args) { log.info("ReleaseId: {}", container.getReleaseId()); log.info("KieBases: {}", container.getKieBaseNames()); container.getKieBaseNames().forEach(name -> log.info("KieBase: {}, KieSessions: {}", name, container.getKieSessionNamesInKieBase(name))); KieSession ks = container.newKieSession(); ks.setGlobal("log", log); Second s1 = new Second("S1", "P1", 25.49); Second s2 = new Second("S2", "P1", 24.99); Second s3 = new Second("S3", "P2", 18.78); ks.insert(s1); ks.insert(s2); ks.insert(s3); ks.fireAllRules(); ks.dispose(); log.info("Done !!!"); } }
To execute the code from Listing.8, open a terminal window and run the following commands:
$ cd $HOME/java/Drools/Second
$ mvn spring-boot:run
The following would be the typical output:
2021-06-05 14:54:34:303 [main] INFO com.polarsparc.second.SecondApplication - Starting SecondApplication using Java 15.0.2 on polarsparc with PID 21471 (/home/polarsparc/java/Drools/Second/target/classes started by polarsparc in /home/polarsparc/java/Drools/Second) 2021-06-05 14:54:34:304 [main] INFO com.polarsparc.second.SecondApplication - No active profile set, falling back to default profiles: default 2021-06-05 14:54:35:402 [main] INFO com.polarsparc.second.SecondApplication - Started SecondApplication in 1.392 seconds (JVM running for 1.636) 2021-06-05 14:54:35:403 [main] INFO org.springframework.boot.availability.ApplicationAvailabilityBean - Application availability state LivenessState changed to CORRECT 2021-06-05 14:54:35:404 [main] INFO com.polarsparc.second.SecondApplication - ReleaseId: com.polarsparc.second:second:1.0 2021-06-05 14:54:35:405 [main] INFO com.polarsparc.second.SecondApplication - KieBases: [second-base] 2021-06-05 14:54:35:405 [main] INFO com.polarsparc.second.SecondApplication - KieBase: second-base, KieSessions: [second-session] 2021-06-05 14:54:35:405 [main] INFO org.drools.compiler.kie.builder.impl.KieContainerImpl - Start creation of KieBase: second-base 2021-06-05 14:54:35:442 [main] INFO org.drools.compiler.kie.builder.impl.KieContainerImpl - End creation of KieBase: second-base 2021-06-05 14:54:35:495 [main] INFO com.polarsparc.second.SecondApplication - Second - supplier: S1, product: P1, price: 25.49 2021-06-05 14:54:35:495 [main] INFO com.polarsparc.second.SecondApplication - Second - supplier: S2, product: P1, price: 24.99 2021-06-05 14:54:35:495 [main] INFO com.polarsparc.second.SecondApplication - Second - supplier: S3, product: P2, price: 18.78 2021-06-05 14:54:35:496 [main] INFO com.polarsparc.second.SecondApplication - Done !!! 2021-06-05 14:54:35:497 [main] INFO org.springframework.boot.availability.ApplicationAvailabilityBean - Application availability state ReadinessState changed to ACCEPTING_TRAFFIC [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 4.478 s [INFO] Finished at: 2021-06-05T14:54:35-04:00 [INFO] ------------------------------------------------------------------------
References