1. Introduction
In this quick tutorial, we’re going to integrate Drools with Spring. If you’re just getting started with Drools, check out this intro article.
2. Maven Dependencies
Let’s start by adding the following dependencies to our pom.xml file:
<dependency> <groupId>org.drools</groupId> <artifactId>drools-core</artifactId> <version>7.0.0.Final</version> </dependency> <dependency> <groupId>org.kie</groupId> <artifactId>kie-spring</artifactId> <version>7.0.0.Final</version> </dependency>
The latest versions can be found here for drools-core and here for kie-spring.
3. Initial Data
Let’s now define the data which will be used in our example. We’re going to calculate the fare of a ride based on the distance traveled and the night surcharge flag.
Here’s a simple object which will be used as a Fact:
public class TaxiRide { private Boolean isNightSurcharge; private Long distanceInMile; // standard constructors, getters/setters }
Let’s also define another business object which will be used for representing fares:
public class Fare { private Long nightSurcharge; private Long rideFare; // standard constructors, getters/setters }
Now, let’s define a business rule for calculating taxi fares:
global com.baeldung.spring.drools.model.Fare rideFare; dialect "mvel" rule "Calculate Taxi Fare - Scenario 1" when taxiRideInstance:TaxiRide(isNightSurcharge == false && distanceInMile < 10); then rideFare.setNightSurcharge(0); rideFare.setRideFare(70); end
As we can see, a rule is defined to calculate the total fare of the given TaxiRide.
This rule accepts a TaxiRide object and checks if the isNightSurcharge attribute is false and the distanceInMile attribute value is less than 10, then calculate the fare as 70 and sets the nightSurcharge property to 0.
The calculated output is set to Fare object for further use.
4. Spring Integration
4.1. Spring Bean Configuration
Now, let’s move on to the Spring integration.
We’re going to define a Spring bean configuration class – which will be responsible for instantiating the TaxiFareCalculatorService bean and its dependencies:
@Configuration @ComponentScan("com.baeldung.spring.drools.service") public class TaxiFareConfiguration { private static final String drlFile = "TAXI_FARE_RULE.drl"; @Bean public KieContainer kieContainer() { KieServices kieServices = KieServices.Factory.get(); KieFileSystem kieFileSystem = kieServices.newKieFileSystem(); kieFileSystem.write(ResourceFactory.newClassPathResource(drlFile)); KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem); kieBuilder.buildAll(); KieModule kieModule = kieBuilder.getKieModule(); return kieServices.newKieContainer(kieModule.getReleaseId()); } }
KieServices is a singleton which acts as a single point entry to get all services provided by Kie. KieServices is retrieved using KieServices.Factory.get().
Next, we need to get the KieContainer which is a placeholder for all the object that we need to run the rule engine.
KieContainer is built with the help of other beans including KieFileSystem, KieBuilder, and KieModule.
Let’s proceed to create a KieModule which is a container of all the resources which are required to define rule knowledge known as KieBase.
KieModule kieModule = kieBuilder.getKieModule();
KieBase is a repository which contains all knowledge related to the application such as rules, processes, functions, type models and it is hidden inside KieModule. The KieBase can be obtained from the KieContainer.
Once KieModule is created, we can proceed to create KieContainer – which contains the KieModule where the KieBase has been defined. The KieContainer is created using a module:
KieContainer kContainer = kieServices.newKieContainer(kieModule.getReleaseId());
4.2. Spring Service
Let’s define a service class which executes the actual business logic by passing the Fact object to the engine for processing the result:
@Service public class TaxiFareCalculatorService { @Autowired private KieContainer kieContainer; public Long calculateFare(TaxiRide taxiRide, Fare rideFare) { KieSession kieSession = kieContainer.newKieSession(); kieSession.setGlobal("rideFare", rideFare); kieSession.insert(taxiRide); kieSession.fireAllRules(); kieSession.dispose(); return rideFare.getTotalFare(); } }
Finally, a KieSession is created using KieContainer instance. A KieSession instance is a place where input data can be inserted. The KieSession interacts with the engine to process the actual business logic defined in rule based on inserted Facts.
Global (just like a global variable) is used to pass information into the engine. We can set the Global using setGlobal(“key”, value); in this example, we have set Fare object as Global to store the calculated taxi fare.
As we discussed in Section 4, a Rule requires data to operate on. We’re inserting the Fact into session using kieSession.insert(taxiRide);
Once we are done with setting up the input Fact, we can request engine to execute the business logic by calling fireAllRules().
Finally, we need to clean up the session to avoid memory leak by calling the dispose() method.
5. Example in Action
Now, we can wire up a Spring context and see in action that Drools works as expected:
@Test public void whenNightSurchargeFalseAndDistLessThan10_thenFixWithoutNightSurcharge() { TaxiRide taxiRide = new TaxiRide(); taxiRide.setIsNightSurcharge(false); taxiRide.setDistanceInMile(9L); Fare rideFare = new Fare(); Long totalCharge = taxiFareCalculatorService.calculateFare(taxiRide, rideFare); assertNotNull(totalCharge); assertEquals(Long.valueOf(70), totalCharge); }
6. Conclusion
In this article, we learned about Drools Spring integration with a simple use case.
As always, the implementation of the example and code snippets are available over on GitHub.