The Rule language

Now that we have executed our first rule, it is time to learn a little bit more about the language that we use to define them. In order to do this, we will start by analyzing the rule that we wrote previously and then we will start creating more advanced rules.

All the rules and examples contained in this section can be found in the chapter-02/chapter-02-kjar/ project. We will use this project throughout the rest of the chapter to store different rules.

As it was mentioned in the first chapter, the rule structure is composed of the conditions and consequence, as follows:

rule "name"
when
    (Conditions) - also called Left Hand Side of the Rule (LHS)
then
    (Actions/Consequence) - also called Right Hand Side of the Rule (RHS)
end

The Conditions (LHS) of the rule are written following the DRL language, which for the sake of simplicity, will not be entirely explained in here. We will be looking at the most common usage of the DRL language throughout the book examples. We will try to cover as much of the language as possible. You can always refer to the official documentation for a complete and detailed explanation of its structure at http://docs.jboss.org/drools/release/6.3.0.Final/drools-docs/html_single/index.html#DroolsLanguageReferenceChapter.

The LHS of the rule is composed by conditional elements, which serve as the filters to define the conditions that need to be met for the rule to evaluate true. This conditional element filter facts, which in our case are the object instances as we are working in Java.

If you take a look at our first rule LHS, the condition expressed is quite simple, as shown in the following:

rule "Classify Item - Low Range"
    when
        $i: Item(cost < 200)
    then
        $i.setCategory(Category.LOW_RANGE);
end

The line in the Left Hand Side of the rule can be separated in the following three sections:

  • The Item(...) filter for the item object type. This filter will pick up all the item objects that we insert into our session and filter them for processing.
  • The cost < 200 filter will take a look in the item objects and make sure that the field cost contains a value under 200.
  • The $i represents a variable binding, which is used to later reference the matched object. Notice that we are using the $ symbol to name the variables so that we can easily identify them in contrast with the object fields. Consider this as good practice.

To summarize, we are filtering based on Objects and their properties. It is important to understand that we will be filtering the Object instances that matches with these conditions. For each item instance that evaluates to true to all the conditions, the rule engine will create a match.

A little bit more complex rule could be to categorize our customers by the size of the order that they make. A big difference between this new rule and the previous one is that now the rule will need to evaluate orders and customers. Take a look at how the rule looks:

//File classify-customer-rules.drl
 
rule "Classify Customer by order size"
    when
        $o: Order( orderLines.size >= 5, $customer: customer )
        $c: Customer(this == $customer, category == Customer.Category.NA)
    then
        ;
        modify($c){
       	setCategory(Customer.Category.SILVER)
        };
end

In this rule, we are evaluating the orders with more than five order lines, which means five different items. Then, we look for the customer associated to this order and set this customer category. The relationship between the customer and order is achieved by binding the customer reference in the Order object to a variable called $customer and comparing Customer that we are evaluating against that reference by doing the following: Customer(this == $customer…).The order of the conditional elements is only defined by the bindings that we need. In this case, we are picking Order.getCustomer() to match the customer fact. However, we can do it the other way around as well and it will work in the same way, as shown in the following:

  $c: Customer(category == Customer.Category.NA)
  $o: Order( orderLines.size >= 5, customer == $c )

An important thing to understand at this point is that Customer() and Order() need to be facts, in other words, they need to be explicitly inserted to KieSession using the insert() method. While Order.getCustomer() is not a fact, it is an object in a fact.

For this rule to evaluate true, we need an Order and Customer object instances that make all these conditions true. Between the Order(..) and Customer(...) filters, there is an implicit AND, therefore, the rule can be read When there is an order with more than 20 items AND a customer that is associated to that order, Then. This is also equivalent and valid in the DRL language, as follows:

  $o: Order( orderLines.size >= 5, $customer: customer )
  and
  $c: Customer(this == $customer, category == Customer.Category.NA)

You may also have noticed the modify($c); sentence on the right-hand side of the rule. This modify() method is another operation provided by the Rule Engine to make sure that the engine knows that a fact has been changed and the change needs to be notified to other rules that might be looking to match these changes. In this case, we are letting the engine know about the modification of the category of the customer. If you omit modify($c), no other rule will know about the change in the category, which means that rules that depend on already categorized items will not be matched. For this reason, you will notice that we are also updating the item fact when the category is set in the rules located in the classify-item-rules.drl file.

Now that our rules have become more complex, it is important to notice the fact that we are clearly separating the business definition from our application code. We are extracting the definition of how to categorize customers to these rules and we will be able to update this definition if the business definition changes without modifying the rest of the application. This is one of the core concepts of using business rules.

Now, based on this categorization, we can create different types of coupons for different customers, allowing us to treat each of our customers differently, based on their loyalty and previous orders:

coupons-creation.drl:
rule "Create Coupons for Silver Customers"
    when
        $o: Order( $customer: customer )
        $c: Customer(this == $customer, category == Category.SILVER)
    then
        insert(new Coupon($c, $o, Coupon.CouponType.POINTS));        
end

Like the previous example, here, the rule is filtering by Orders and Customers; however, as you can see in the rule RHS, we are creating a new Object of the Coupon type and making it available to the Rule Engine using the insert() method. This means that as soon as this rule gets executed by the Rule Engine, it will trigger any other rule that is expecting Coupons. Here the things become a little bit more interesting. We saw how the rules can generate new data and chain different rules together as soon as we make the new data available to the Rule Engine.

If you feel the need to experiment, we encourage you to write a rule in the coupons-creation.drl file to match the created coupon and see what happens.

Now let's make things a little bit more complex, let's imagine that we want to check an order with two or more items that only contain HIGH_RANGE items and we want to apply some discounts to these specific orders.

In order to write a rule that check for this situation, we will also need to evaluate the OrderLine objects (we will need to add this import as well). This can be translated to adding more filters to our rules. Now, we will need to put constraints on the Order object, OrderLines and Item associated. The following UML diagram shows the relationships among these objects:

The Rule language

The following rule file expresses the previously introduced rule for discounts:

rule "High Range Order - 10% Discount"
    when
        $o: Order( $lines : orderLines.size >= 2, discount == null )
        forall( OrderLine( this memberOf $lines,  $item : item)
                Item(this == $item, category == Item.Category.HIGH_RANGE)
        )
    then
        
modify($o){
setDiscount(new Discount(10.0))
};
end

We have three different object filters for Order(), OrderLine(), and Item(). Notice that this rule also depends on having our items classified; however, there is no explicit relationship between this rule and our first rule that categorize our items. One new thing introduced by this rule is the conditional element forall, which makes sure that all OrderLines and the associated items of the order are categorized as HIGH_RANGE items. If there is at least one item with a different category set associated with the current order, this rule will not get activated and fired. In the same way as earlier, we are updating the order so that if another rule is looking at the discount applied, the information is available to the engine.

You can find these rules in the chapter-02/chapter-02-kjar/src/resources/ directory. In the following section, we will analyze a little bit of these project structures and tests provided to execute these rules.

We will be covering more of the DRL language in this book, but feel free to access the following official documentation if you need a detailed explanation of each of the DRL constructs:

http://docs.jboss.org/drools/release/6.3.0.Final/drools-docs/html_single/index.html#DroolsLanguageReferenceChapter

As you will see in the previous link, there is no way to include a detailed explanation about the constructs in a book; however, we will make sure to cover the most common ones using examples.

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

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