
Writing and executing our first rule
Now that we have our project structure ready, we can write our first rule. For that, we will create a new empty text file. This will be a static resource, therefore, we need to place it in the src/main/resources
directory. This text file needs to have the .drl
extension so that it can be picked up as a rule file. In the .drl
files, we will write as many rules as we want. Now, we will start easy with just one rule.
Let's write our first rule to classify the items based on what they cost us. Our rules.drl
text file will look similar to the following:
package myfirstproject.rules import org.drools.devguide.eshop.model.Item; import org.drools.devguide.eshop.model.Item.Category; rule "Classify Item - Low Range" when $i: Item(cost < 200) then $i.setCategory(Category.LOW_RANGE); end
This rule checks for each item that costs less than 200
USD and automatically tags it with a category, in this case, LOW_RANGE
. For our shop, it makes sense to differentiate our items in different ranges so that we can apply different discounts and marketing strategies for them. This classification process can be done automatically using rules, which centralize the point where we have this business definition of what LOW_RANGE
, MID_RANGE
or HIGH_RANGE
items they are.
In general, these files will be structured as follow:
- Package definition: This is the same as in Java, we will declare a package for our rules
- Imports section: We need to import all the classes that we are going to use in of our rules
- (Optional) declared types and events: We will look at this in more detail in Chapter 4, Improving our Rule Syntax
- Rules: (1..N)/Queries (1..N)
Before analyzing further, let's try to execute the rule and see what happens. In order to execute and test our new rule, we need to create a Java class, where we bootstrap the Rule Engine and provide the required information for it to work. We can just create an empty class with a main method, bootstrap the engine, and start using it right away.
In order to do this, we just create an App.java
class for this example and place it in the src/main/java/
directory so that it can be compiled by Maven. For this example, we have created the org/drools6/book
directory that follows the standard Java package structure, as follows:
package org.drools6.book; import ... public class App { public static void main( String[] args ) { System.out.println( "Bootstrapping the Rule Engine ..." ); //1) Bootstrapping a Rule Engine Session KieServices ks = KieServices.Factory.get(); KieContainer kContainer = ks.getKieClasspathContainer(); KieSession kSession = kContainer.newKieSession(); Item item = new Item("A", 123.0,234.0); System.out.println( "Item Category: " + item.getCategory()); //2) Provide information to the Rule Engine Context kSession.insert(item); //3) Execute the rules that are matching int fired = kSession.fireAllRules(); System.out.println( "Number of Rules executed = " + fired ); System.out.println( "Item Category: " + item.getCategory()); } }
As you can see in the previous example, there are three main stages, as shown in the following:
- Bootstrapping the Rule Engine session: We will look at what
KieServices
,KieContainer
, and KieSession's main responsibilities are in Chapter 3, Drools Runtime. For now, we need to know thatKieSession
represents a running instance of the Rule Engine with a specific configuration and set of rules. It holds the evaluation algorithm used to match the rules against our domain objects. - Letting the Rule Engine know about our data: We are responsible for providing all the information to the engine so that it can operate on it. In order to do this, we use the
insert()
method onKieSession
. We can also remove the information from the Rule Engine context using thedelete()
method or update the information using themodify()
method. We will look at theKieSession
operations in Chapter 3, Drools Runtime. - If the information that we provided matches with one or more defined rules, we will get Matches. Calling the
fireAllRules()
method will execute these matches. We will learn more about Matches in Chapter 3, Drools Runtime. - You will need to compile the project in order to execute this class and you can do this by executing from the terminal or your IDE, as follows:
> mvn clean install
This will compile and package your project, look for the Build Success output in the terminal. After executing this line, you will find the /target
directory containing a myfirst-drools-project-1.0.0.jar
jar file that you can use to execute the previously compiled class using the following line:
mvn exec:java -Dexec.mainClass="org.drools6.book.App"
You will see the following output on the terminal:
user$ mvn exec:java -Dexec.mainClass="org.drools6.book.App"
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building myfirst-drools-project 1.0.0
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] --- exec-maven-plugin:1.4.0:java (default-cli) @ myfirstproject ---
Bootstrapping the Rule Engine ...
...
Item Category: NA
Number of Rules executed = 1
Item Category: LOW_RANGE
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 2.945 s
[INFO] Finished at: 2015-04-02T09:55:08+01:00
[INFO] Final Memory: 20M/257M
[INFO] ------------------------------------------------------------------------
As you can see, the rule was executed as the category of the item was changed accordingly with what the rule says. I would encourage you to change the cost of the item to effectively see that the rule will not be executed if the cost is over the value defined in the rule. You can also add more items with different costs to KieSession
and see what happens.
Using CDI to bootstrap the Rule Engine
The Contexs and Dependency Injection (CDI) http://www.cdi-spec.org is a set of standardized APIs defined to provide our applications with these features, while it allows us to choose the context and dependency injection container that we want. CDI is now becoming a part of the Java SE specification and its adoption is growing every year. For this reason and due to the Drools Project added a lot of support for the CDI environment, this section briefly shows how to simplify our Hello World example that we wrote in the previous section.
In order to use CDI in our projects, we need to add a couple of dependencies to our project, as follows:
<dependencies> ... <dependency> <groupId>javax.enterprise</groupId> <artifactId>cdi-api</artifactId> </dependency> <dependency> <groupId>org.jboss.weld.se</groupId> <artifactId>weld-se-core</artifactId> </dependency> </dependencies>
The javax.enterprise:cdi-api
artifact contains all the interfaces defined in the CDI specification and org.jboss.weld.se:weld-se-core
is the container that we are going to use, which implements the CDI interfaces. By adding these dependencies, we will be able to @Inject
our KieSessions
and the Weld container will take care of bootstrapping the Rule Engine for us.
CDI works based on the principle of convention over configuration and it introduced the need to add a new file in src/main/resources/META-INF
/ called beans.xml
, which will be used to configure how the container has the access to our beans in the projects and some other configurations. Notice the similarity with the kmodule.xml
file that we introduced earlier. This is an example content of an empty beans.xml
file, which is used by the CDI containers to know the jars that need to be parsed and made available to the container to @Inject
beans, as follows:
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd"> </beans>
As soon as we have the dependencies to the container and the beans.xml
file, the container can scan the class path (meaning our project and its dependencies) and look for beans to inject, we can start using these features in our application.
The following class represents the same simple example that creates a default KieSession
with our first rule and then interacts with it.
The following code snippet initializes KieSession
via CDI and interacts with it, as follows:
public class App { @Inject @KSession KieSession kSession; public void go(PrintStream out){ Item item = new Item("A", 123.0,234.0); out.println( "Item Category: " + item.getCategory()); kSession.insert(item); int fired = kSession.fireAllRules(); out.println( "Number of Rules executed = " + fired ); out.println( "Item Category: " + item.getCategory()); } public static void main( String[] args ) { // Bootstraping the WELD CDI container Weld w = new Weld(); WeldContainer wc = w.initialize(); App bean = wc.instance().select(App.class).get(); bean.go(System.out); w.shutdown(); } }
Notice that the main(...)
method is now bootstrapping the Weld container and for this reason, our bean (App) can inject any bean. In this case, the @KSession
annotation is in charge of bootstrapping the engine and creating a fresh instance for us to use. We will look at the annotations provided by the CDI extension in Chapter 3, Drools Runtime.
Consider this as another very valid option to interact with the Drools Rule Engine. If you are working with a Java EE container such as WildFly AS (http://www.wildfly.org), which is constructed on top of a core that is purely based on CDI, this way of working will be the way to go.
For this example, we are using WELD, which is the reference CDI implementation at http://weld.cdi-spec.org. Notice that you can use any other CDI implementation, such as Apache Open Web Beans at http://openwebbeans.apache.org.
Now, in order to understand how the rule is being applied and executed, we should clearly understand the language that we are using to write the rules called Drools Rule Language (DRL). The following section covers a more detailed view of this language. The next chapter will cover the execution side in more detail, explaining what is going on when we bootstrap the rule engine session and how to configure it for different purposes.