Chapter 3. Drools Runtime

In the previous chapter, we covered how to start a project from scratch, write some rules, and execute them. Now, we need to understand how the Drools internal components work together, their main responsibilities, and their configurations. Knowing this, we will be able to fine-tune the engine and the surrounding services to our specific needs. We will see how Drools can be used in different contexts and ways so that you can decide which fits the best for your projects. This chapter will cover the following topics:

  • Understanding the Drools runtime instances
  • Common configurations
  • Loading dynamic changes using KieScanner

Understanding the Drools runtime instances

Drools allows us to create instances of the Rule Engine in different ways, so that we can choose which fits better to the problem that we are trying to solve. Each Rule Engine instance is an encapsulated context, where the rules that we define will be evaluated against the data that we provide to this particular instance. Historically, Rule Engines were seen as big and monolithic processes that run them in a server and we can send data to it to be processed. Drools, on the other hand, allows us to locally spawn lightweight instances to our application. It is common to have multiple instances dealing with different rules and data than just one big instance.

In order to spawn a new instance of the rule engine, we need to understand the following concepts:

  • KieServices
  • KieContainer
  • KieModule
  • KieBase
  • KieSession

By using these five concepts, we will be able to define how each instance is configured and rules that will be available to each of them. In cases where we need to create multiple rule engines, it is important to understand what exactly happens under the hood to avoid unnecessary bottleneck and performance issues.

It is important also to understand that these five concepts are extended versions of what was provided in the previous versions of Drools. Notice that the Knowledge is Everything (KIE) prefix that indicates the fact that now we are not only dealing with Rule Engine instances, but with more ways of defining and executing business knowledge in general.

We will start by looking at the KieServices class that gives us access to all these other concepts by providing a registry of services where we can find helpers for different purposes. In the future versions of Drools, more services may be included to fulfill different use cases. For now, we need to know how to get hold of a KieServices instance, and we do that by using the following static KieServices.Factory.get() method:

KieServices ks = KieServices.Factory.get();

Using the KieServices, we can access a number of factories, services, and utility methods used along with Rule Engine instances. We will use KieServices to create a new instance of KieContainer, which defines the scope of the rules that will be used to create new instances of the Rule Engine. A KieContainer can host a KieModule and its dependencies. It means that a hierarchical structure of KieModules can be loaded into an instance of the KieContainer.

The relations among these concepts are depicted in the following diagram:

Understanding the Drools runtime instances

In Drools 6, everything is created around KieModules. Each KieModule contains business assets (business rules, business processes, decision tables, and so on) related to a certain area or domain. These KieModules can include other KieModules, allowing us to compose a top-level KieModule, containing several assets from different domains.

A KieModule is a standard Java-Maven project containing the rules, business process and other assets among its resources. A special file called kmodule.xml (in the META-INF/ directory) that defines the internal configuration about how to group and consume these particular assets must also be present in it.

As we can see in the previous diagram, KieContainer will allow us to instantiate a KieModule in order to create one or multiple instances of the Rule Engine. These instances can be all configured to have the same rules in it or a completely different setup. The previous diagram also shows how to decide which KieModule to load; for example, we can decide to load KieModule A as we are just going to use the Rules defined in it or we can load KieModule Parent, which depends on KieModule A and KieModule B, therefore, every configuration in these modules will be loaded.

The following section will go deep into the KieModule and KieContainer specifics. In the beginning, this might sound confusing as there are too many options, however, that's exactly the reason behind the flexibility that Drools provides to configure and instantiate the Rule Engine. Towards the end of this book, we expect you to be an intermediate Drools user that can configure and tune the engine to your specific needs.

KieModule & KieContainer

Once we get hold of the KieServices, we can create new KieContainers. Internally, the KieContainer has references to all the business assets (rules, processes, spreadsheets, PMML documents, and so on) that will be loaded when we create new Rule Engine instances. As it was depicted in the previous section, a KieContainer can spawn multiple Rule Engine instances with different configurations and hosting different rule sets, depending on what our application requires.

In Drools 6, we can choose between two options to define the scope of the resources and configurations that will be included in an instance of KieContainer, as shown in the following:

  • Based on the classpath
  • Using Maven dependency resolution techniques (KIE-CI)

The first option will look at all the business assets in the application classpath and allow us to load them in different instances of the rule engine. The second option will delegate the responsibility of finding out predefined artifacts and their transitive dependencies into Maven to find out all the resources that need to be included.

Let's look at these two options in detail. First, we will start by looking at how the classpath is being scanned and rules are picked up. The best way for us to understand how this works is to take a look at the projects provided in this chapter.

This chapter provides the following five projects to demonstrate the different setups that can be used for our KieModules:

  • chapter-03-classpath-tests: This project provides tests showing the classpath resolution strategy to create KieContainers.
  • chapter-03-maven-tests: This project provides tests showing how to leverage the power of Maven to resolve the KieModules as Maven artifacts.
  • chapter-03-kjar-simple-discounts: This is a KieModule containing a set of rules for simple discounts.
  • chapter-03-kjar-premium-discounts: This is another KieModule containing a set of rules for premium discounts.
  • chapter-03-kjar-parent: This is a KieModule that contains a reference to the previous KieModules. It doesn't include any asset internally, but it makes references to the simple and premium discounts so that they can be referenced together.

The next two sections will cover the following two alternatives:

  • Loading rules from the classpath
  • Loading rules using Maven artifacts (using Kie-CI)

Loading rules from the classpath

For this section, we will be using the chapter-03-classpath-tests project, therefore, we recommend you to open this project in your IDE to review the provided tests together, while we discuss the different options that we have to set up our Rule Engine instances.

The first test in the KieContainerClassPathTests class (located in src/test/java/) called loadingRulesFromClassPath() demonstrates how a new KieContainer can be created by scanning the current application classpath. In this case, we are bootstrapping Drools in a JUnit Test and Maven takes care of setting up the classpath for us based on the definitions in the pom.xml file. If we are not using Maven to run the application (or the Unit Test), Drools will scan the application classpath, no matter how it is defined. For this example, as we are in a Maven project, all the dependencies defined for the project and the dependencies defined with the test scope will be added to the classpath before running the test.

If we take a look at the pom.xml file (located in the root of project /chapter-03/chapter-03-tests/pom.xml), we will notice that there are three dependencies to projects that contain the kmodule.xml file, as follows:

<!-- Start dependencies for the other KieModules -->
<dependency>
<groupId>org.drools.devguide</groupId>
<artifactId>chapter-03-kjar-simple-discounts</artifactId>  
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.drools.devguide</groupId>
<artifactId>chapter-03-kjar-premium-discounts</artifactId>  
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.drools.devguide</groupId>
<artifactId>chapter-03-kjar-parent</artifactId>  
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<!-- End dependencies for the other KieModules -->

When we create a new KieContainer based on the classpath, all the available jars will be scanned. In order to create a new KieContainer, we use the KieServices (ks) to provide us with a new instance of the KieContainer, as follows:

KieContainer kContainer = ks.newKieClasspathContainer();

Drools will scan all the jars in the classpath looking for the kmodule.xml file in the META-INF/ directory. When found, this file will load the provided configurations to make them available to use in our applications. For this particular example that we are reviewing, four kmodule.xml files will be found: the three in the dependencies defined in the pom.xml file and the one included in the project itself (in the src/test/resources/META-INF/ directory). All the configurations in each of the kmodule.xml files will be loaded and made available to the KieContainer, ready to be used. Also, notice that there is a DRL file called classpath-discount-rules.drl in the src/test/resources/rules/cp/discount directory:

Loading rules from the classpath

Now, our KieContainer has loaded all these configurations and we can start using them right away. Going back to our loadingRulesFromClassPath()test, which shows how the rules can be loaded from the classpath, as follows:

...
KieServices ks = KieServices.Factory.get();
KieContainer kContainer = ks.newKieClasspathContainer();

// Let's verify that all the resources are loaded correctly
Results results = kContainer.verify();
// We can iterate the results
results.getMessages().stream()
       .forEach((message) -> {
         System.out.println(">> Message ( "+message.getLevel()+" ): "+message.getText());
        });
// If there is an Error we need to stop and correct it
assertThat(false, is(results.hasMessages(Message.Level.ERROR)));

//Here we make sure that all the KieBases and KieSessions
//  that we are expecting are loaded.
kContainer.getKieBaseNames().stream().map((kieBase) -> {
            System.out.println(">> Loading KieBase: "+ kieBase );
            return kieBase;
        }).forEach((kieBase) -> {
            kContainer.getKieSessionNamesInKieBase(kieBase).stream().forEach((kieSession) -> {
                System.out.println("	 >> Containing KieSession: "+ kieSession );
            });
        });

As we can see, the verify() method will allow us to make sure that our business assets are correct and are loaded correctly in the KieContainer instance. If the verify() method returns Results containing errors, we should stop and correct these errors before moving forward. Notice that the Message object returned by the results.getMessages() method also contains the line and column in the file where the problem is. If everything is okay, we can move forward and check whether all the KieBases and KieSessions that we are expecting to use are loaded. For this test, we are only printing them out, however, making assertions is recommended here.

As mentioned earlier, the kmodule.xml file contains what will be made available to the KieContainer, therefore, let's take a look at the kmodule.xml file content to understand a little bit more of what is loaded when we create a KieContainer classpath.

Briefing up, this simple test demonstrates how all the KieModules are loaded in the container based on a classpath scanning. Make sure that you run this test to verify the results and understand why the different configurations are loaded.

Let's quickly take a look at the second approach before looking at the kmodule.xml configurations in detail.

Loading rules using Maven artifacts (Kie-CI)

As we saw in the previous section, all the KieModules that are found in the classpath of the application will be loaded in the KieContainer. There are some cases where we don't want that to happen. Maybe because we don't have these jars locally to the application or we want to decouple our application dependencies from the business rules artifact dependencies, therefore, it is clear what is required for the application to run and what are the Knowledge Artifacts that will provide the business logic.

If we now open the chapter-03-maven-tests project and look at the pom.xml file, we will notice a huge difference. There is no KieModule dependency in this pom.xml file, however, a new dependency was added to a kie-ci module, as follows:

<dependency>
  <groupId>org.kie</groupId>
  <artifactId>kie-ci</artifactId>

  <scope>test</scope>
</dependency>

This new dependency will enable Drools to use the Maven mechanism to resolve artifacts that are outside of the application classpath. Let's take a look at the tests in the KieContainerMavenTests class. We will see that now we can use the KieServices to create a new container in a different way, as shown in the following:

KieContainer kContainer = ks.newKieContainer(ks.newReleaseId("org.drools.devguide", "chapter-03-kjar-simple-discounts","1.0.0"));

Notice that now, instead of adding the dependency to our project/application, we are letting the KieContainer resolve an artifact that we are providing based on GroupId, ArtifactId, and Version (also referred as GAV). As you can see, the newKieContainer() method is expecting a ReleaseId object, which is also created using a helper method of the KieServices.

The Drools API allows us to not only use this ReleaseId specific version of the KieModule, but also upgrade it to a newer version, should it be necessary, through the updateToVersion() method. This method will recreate the KieContainer to become an access point to the KieBases and KieSessions of a newer version of the KieModule.

Using this mechanism, the container will only load the configurations from this artifact and all its dependencies. If you execute these tests, you will see that each of the individual tests is only loading the kmodule.xml configuration contained in the artifact that is being requested at the time of the KieContainer creation.

It is time to look at the content of the kmodule.xml file, which allows us to configure our KieBases and KieSessions to define exactly how the rule engine instances will be created for us to use in our applications.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.139.97.202