Rules engine Drools learning details

Posted by JPnyc on Sun, 26 Dec 2021 21:35:18 +0100

1. Overview of rule engine

1.1 what is a rule engine

The full name of rule engine is business rule management system, and its English name is BRMS (Business Rule Management System). The main idea of rule engine is to separate the business decision-making part of the application, and write business decisions (business rules) using predefined semantic modules, which can be configured and managed by users or developers when needed.

It should be noted that the rule engine is not a specific technical framework, but a kind of system, that is, business rule management system. At present, specific rule engine products on the market include drools, VisualRules, iLog, etc.

The rule engine separates business decisions from application code, receives data input, interprets business rules, and makes business decisions according to business rules. The rule engine is actually an input and output platform.

After using the rule engine in the above credit card application business scenario, the effect is as follows:

After the rule engine is introduced into the system, the business rules no longer reside in the system in the form of program code, but are replaced by the rule engine for processing rules. The business rules are stored in the rule base and completely independent of the program. Business personnel can manage business rules like managing data, such as querying, adding, updating, statistics, submitting business rules, etc. Business rules are loaded into the rule engine to supply system calls.

1.2 advantages of using rule engine

The advantages of using rule engine are as follows

  1. Business rules are separated from system code to realize centralized management of business rules

  2. Business rules can be extended and maintained at any time without restarting the service

  3. Business rules can be dynamically modified to quickly respond to changes in requirements

  4. The rule engine is relatively independent and only cares about business rules, so that business analysts can also participate in editing and maintaining system business rules

  5. Reduces the cost and risk of hard coding business rules

  6. Using the rule editing tool provided by the rule engine makes the implementation of complex business rules easier

1.3 application scenario of rule engine

For some systems with complex business rules and frequent changes in business rules, it is more suitable to use the rule engine, as follows:

  1. Risk control system - risk loan, risk assessment

  2. Anti fraud project - bank loan, credit investigation and verification

  3. Decision making platform system - financial calculation

  4. Promotion platform system - full reduction, discount and price increase purchase

1.4 introduction to drools

drools is an open source rule engine developed based on Java language provided by JBoss organization. It can liberate complex and changeable business rules from hard coding and store them in files or specific storage media (such as database) in the form of rule scripts, so that the change of business rules does not need to modify the project code Restart the server and the online environment will take effect immediately.
drools official website address: https://drools.org/
Drools source code download address: https://github.com/kiegroup/drools

When using drools in a project, it can be used alone or integrated with spring. If used alone, you only need to import the following maven coordinates:

<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-compiler</artifactId>
    <version>7.6.0.Final</version>
</dependency>

If we use IDEA to develop drools applications, the drools plug-in has been integrated into the IDEA. If you use eclipse to develop drools applications, you also need to install the drools plug-in separately.
The development steps of drools API are as follows:

2. Introduction to drools

In this section, an introductory case of Drools is used to give you a preliminary understanding of how Drools is used and an overall concept of Drools.

2.1 business scenario description

Business scenario: when consumers buy books in the book mall, they need to display the price after preferential order on the payment page after placing an order. The specific preferential rules are as follows:

Rule numberRule namedescribe
1Rule oneThere is no discount for books with a total price of less than 100 yuan
2Rule 2If the total price of the books purchased is 100 to 200 yuan, the discount is 20 yuan
3Rule threeIf the total price of the books purchased is 200 to 300 yuan, the discount is 50 yuan
4Rule 4If the total price of the books purchased is more than 300 yuan, the discount is 100 yuan

Now you need to calculate the preferential price according to the above rules.

2.2 development and Implementation

Step 1: create maven project drools_quickstart and import maven coordinates related to drools

<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-compiler</artifactId>
    <version>7.10.0.Final</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

Step 2: create resources / meta-inf / kmodule according to the requirements of drools XML configuration file

<?xml version="1.0" encoding="UTF-8" ?>
<kmodule xmlns="http://www.drools.org/xsd/kmodule">
    <!--
        name:appoint kbase The name of can be arbitrary, but it needs to be unique
        packages:Specify the directory of the rule file, which needs to be filled in according to the actual situation, otherwise it cannot be loaded into the rule file
        default:Specify current kbase Default
    -->
    <kbase name="myKbase1" packages="rules" default="true">
        <!--
            name:appoint ksession The name can be arbitrary, but it needs to be unique
            default:Specify current session Default
        -->
        <ksession name="ksession-rule" default="true"/>
    </kbase>
</kmodule>

Note: the name and location of the above configuration file are fixed and cannot be changed

Step 3: create the entity class Order

package com.test.drools.entity;
​
/**
 * order
 */
public class Order {
    private Double originalPrice;//The original price of the order, i.e. the price before the discount
    private Double realPrice;//The real price of the order, that is, the price after discount
​
    public String toString() {
        return "Order{" +
                "originalPrice=" + originalPrice +
                ", realPrice=" + realPrice +
                '}';
    }
​
    public Double getOriginalPrice() {
        return originalPrice;
    }
​
    public void setOriginalPrice(Double originalPrice) {
        this.originalPrice = originalPrice;
    }
​
    public Double getRealPrice() {
        return realPrice;
    }
​
    public void setRealPrice(Double realPrice) {
        this.realPrice = realPrice;
    }
}

Step 4: create the rules file resources / rules / bookdiscount drl

//Book discount rules
package book.discount
import com.itheima.drools.entity.Order
​
//Rule 1: there is no discount for books with a total price of less than 100 yuan
rule "book_discount_1"
    when
        $order:Order(originalPrice < 100)
    then
        $order.setRealPrice($order.getOriginalPrice());
        System.out.println("Successfully matched to rule 1: there is no discount if the total price of the purchased books is less than 100 yuan");
end
​
//Rule 2: if the total price of the purchased books is 100 to 200 yuan, the discount is 20 yuan
rule "book_discount_2"
    when
        $order:Order(originalPrice < 200 && originalPrice >= 100)
    then
        $order.setRealPrice($order.getOriginalPrice() - 20);
        System.out.println("Successfully matched to rule 2: 20 yuan discount for books with a total price of 100 to 200 yuan");
end
​
//Rule 3: if the total price of the purchased books is 200 to 300 yuan, the discount is 50 yuan
rule "book_discount_3"
    when
        $order:Order(originalPrice <= 300 && originalPrice >= 200)
    then
        $order.setRealPrice($order.getOriginalPrice() - 50);
        System.out.println("Successfully matched to rule 3: 50 yuan discount for books with a total price of 200 to 300 yuan");
end
​
//Rule 4: if the total price of the books purchased is more than 300 yuan, the discount is 100 yuan
rule "book_discount_4"
    when
        $order:Order(originalPrice >= 300)
    then
        $order.setRealPrice($order.getOriginalPrice() - 100);
        System.out.println("Successfully matched to rule 4: if the total price of the purchased books is more than 300 yuan, the discount is 100 yuan");
end

Step 5: write unit tests

@Test
public void test1(){
    KieServices kieServices = KieServices.Factory.get();
    KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
    //Session object for interacting with the rule engine
    KieSession kieSession = kieClasspathContainer.newKieSession();
​
    //Construct the order object, set the original price, and the rule engine calculates the preferential price according to the preferential rules
    Order order = new Order();
    order.setOriginalPrice(210D);
​
    //Provide the data to the rule engine, and the rule engine will match the rules according to the provided data
    kieSession.insert(order);
​
    //Activate the rule engine. If the rules match successfully, execute the rules
    kieSession.fireAllRules();
    //Close session
    kieSession.dispose();
​
    System.out.println("Original price before offer:" + order.getOriginalPrice() +
                       ",Price after discount:" + order.getRealPrice());
}

Through the above introductory case, we can find that the main work of using drools rule engine is to write rule files and define business-related business rules in the rule files. For example, this case defines book discount rules. After the rules are defined, you need to call the API provided by drools to provide the data to the rule engine for rule pattern matching. The rule engine will execute the successfully matched rules and return the calculation results to us.

You may have a question, that is, although we do not write the judgment logic of rules in the code, we still write business rules in the rule file. What is the essential difference between writing rules in the code?

As we mentioned earlier, business rules can be managed dynamically when using the rule engine. Business personnel can manage business rules like managing data, such as querying, adding, updating, statistics, submitting business rules, etc. In this way, the business rules can be adjusted without restarting the service.

3.Drools basic syntax

3.1 composition of rule documents

When using Drools, a very important job is to write rule files. Usually, the suffix of rule files is drl.

drl is the abbreviation of Drools Rule Language. Write specific rules in the rules file.

The contents of a complete set of rules documents are as follows:

keyworddescribe
packagePackage name is limited to logical management. Queries or functions under the same package name can be called directly
importUsed to import classes or static methods
globalglobal variable
functionCustom function
queryquery
rule endRegular body

The rule files supported by Drools are not only in drl form, but also in Excel file type.

3.2 regular syntax structure

Rule body is an important part of the content of rule file. It is the part for judging business rules and processing business results.

The syntax structure of rule body is as follows:

rule "ruleName"
    attributes
    when
        LHS
    then
        RHS
end

Rule: keyword, indicating the beginning of the rule. The parameter is the unique name of the rule.
attributes: rule attribute. It is a parameter between rule and when. It is optional.
when: keyword, followed by the condition part of the rule.
LHS(Left Hand Side): it is the common name of the condition part of the rule. It consists of zero or more conditional elements. If LHS is empty, it is treated as a condition element that is always true.
then: keyword, followed by the result part of the rule.
RHS(Right Hand Side): it is the common name of the consequence or action part of the rule.
End: keyword indicating the end of a rule.

3.3 notes

The annotations used in drl form rule files are consistent with those used in Java classes, which are divided into single line annotations and multi line annotations.

Single line comments are marked with "/ /", and multi line comments start with "/" and end with "/". Examples are as follows:

//The annotation of rule rule1, which is a single line annotation
rule "rule1"
    when
    then
        System.out.println("rule1 trigger");
end
​
/*
Comments for rule rule2,
This is a multiline comment
*/
rule "rule2"
    when
    then
        System.out.println("rule2 trigger");
end

3.4 Pattern matching

We already know that the matcher in Drools can pattern match all rules in Rule Base with the Fact object in Working Memory, so we need to define rules and pattern match in the LHS part of the rule body. The LHS part consists of one or more conditions, which are also called patterns.

The syntax structure of pattern is: binding variable name: Object(Field constraint)

The bound variable name can be omitted. Generally, it is recommended to start with $. If the binding variable name is defined, you can use this binding variable name in the RHS part of the rule body to operate the corresponding Fact object. The Field constraint part is 0 or more expressions that need to return true or false.

For example, in our introductory case:

//Rule 2: if the total price of the purchased books is 100 to 200 yuan, the discount is 20 yuan
rule "book_discount_2"
    when
        //Order is a type constraint and originalPrice is an attribute constraint
        $order:Order(originalPrice < 200 && originalPrice >= 100)
    then
        $order.setRealPrice($order.getOriginalPrice() - 20);
        System.out.println("Successfully matched to rule 2: 20 yuan discount for books with a total price of 100 to 200 yuan");
end

From the above example, we can know that the matching conditions are:

  1. There must be a Fact object of type Order in the working memory ----- type constraint
  2. The originalPrice property value of the Fact object must be less than 200 ------ property constraint
  3. The originalPrice property value of the Fact object must be greater than or equal to 100 ------ property constraint

The above conditions must meet the current rules at the same time before they can be activated.
Binding variables can be used on both objects and object properties. For example, the above example can be changed to:

//Rule 2: if the total price of the purchased books is 100 to 200 yuan, the discount is 20 yuan
rule "book_discount_2"
    when
        $order:Order($op:originalPrice < 200 && originalPrice >= 100)
    then
        System.out.println("$op=" + $op);
        $order.setRealPrice($order.getOriginalPrice() - 20);
        System.out.println("Successfully matched to rule 2: 20 yuan discount for books with a total price of 100 to 200 yuan");
end

The LHS part can also define multiple patterns. Multiple patterns can be connected with and or without writing. The default connection is and.

//Rule 2: if the total price of the purchased books is 100 to 200 yuan, the discount is 20 yuan
rule "book_discount_2"
    when
        $order:Order($op:originalPrice < 200 && originalPrice >= 100) and
        $customer:Customer(age > 20 && gender=='male')
    then
        System.out.println("$op=" + $op);
        $order.setRealPrice($order.getOriginalPrice() - 20);
        System.out.println("Successfully matched to rule 2: 20 yuan discount for books with a total price of 100 to 200 yuan");
end

3.5 comparison operator

The comparison operators provided by Drools are as follows:

Symbolexplain
>greater than
<less than
>=Greater than or equal to
<=Less than or equal to
==be equal to
!=Not equal to
containsCheck whether a property value of a Fact object contains a specified object value
not containsCheck whether a property value of a Fact object does not contain a specified object value
memberOfJudge whether a property of a Fact object is in one or more collections
not memberOfDetermine whether a property of a Fact object is not in one or more collections
matchesDetermine whether the properties of a Fact object match the provided standard Java regular expression
not matchesDetermine whether the properties of a Fact object do not match the provided standard Java regular expression

3.5. 1 Grammar

contains | not contains syntax structure

Object(Field[Collection/Array] contains value)

Object(Field[Collection/Array] not contains value)

memberOf | not memberOf syntax structure

Object(field memberOf value[Collection/Array])

Object(field not memberOf value[Collection/Array])

matches | not matches syntax structure

Object(field matches "regular expressions")

Object(field not matches "regular expression")

3.5. 2 operation steps

Step 1: create an entity class to test the comparison operator

package com.test.drools.entity;
import java.util.List;
​
/**
 * Entity class
 * Comparison operator for testing
 */
public class ComparisonOperatorEntity {
    private String names;
    private List<String> list;
​
    public String getNames() {
        return names;
    }
​
    public void setNames(String names) {
        this.names = names;
    }
​
    public List<String> getList() {
        return list;
    }
​
    public void setList(List<String> list) {
        this.list = list;
    }
}

Step 2: create the rule file comparisoperator. Under / resources/rules drl

package comparisonOperator
import com.test.drools.entity.ComparisonOperatorEntity
/*
 The current rule file is used to test the comparison operators provided by Drools
*/
​
//Test comparison operator contains
rule "rule_comparison_contains"
    when
        ComparisonOperatorEntity(names contains "Zhang San")
        ComparisonOperatorEntity(list contains names)
    then
        System.out.println("rule rule_comparison_contains trigger");
end
​
//Test comparison operator not contains
rule "rule_comparison_notContains"
    when
        ComparisonOperatorEntity(names not contains "Zhang San")
        ComparisonOperatorEntity(list not contains names)
    then
        System.out.println("rule rule_comparison_notContains trigger");
end
​
//Test comparison operator memberOf
rule "rule_comparison_memberOf"
    when
        ComparisonOperatorEntity(names memberOf list)
    then
        System.out.println("rule rule_comparison_memberOf trigger");
end
​
//Test comparison operator not memberOf
rule "rule_comparison_notMemberOf"
    when
        ComparisonOperatorEntity(names not memberOf list)
    then
        System.out.println("rule rule_comparison_notMemberOf trigger");
end
​
//Test comparison operator matches
rule "rule_comparison_matches"
    when
        ComparisonOperatorEntity(names matches "Zhang.*")
    then
        System.out.println("rule rule_comparison_matches trigger");
end
​
//Test comparison operator not matches
rule "rule_comparison_notMatches"
    when
        ComparisonOperatorEntity(names not matches "Zhang.*")
    then
        System.out.println("rule rule_comparison_notMatches trigger");
end

Step 3: write unit tests

//Test comparison operator
@Test
public void test3(){
    KieServices kieServices = KieServices.Factory.get();
    KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
    KieSession kieSession = kieClasspathContainer.newKieSession();
​
    ComparisonOperatorEntity comparisonOperatorEntity = new ComparisonOperatorEntity();
    comparisonOperatorEntity.setNames("Zhang San");
    List<String> list = new ArrayList<String>();
    list.add("Zhang San");
    list.add("Li Si");
    comparisonOperatorEntity.setList(list);
​
    //Provide the data to the rule engine. The rule engine will match the rules according to the provided data. If the rules match successfully, the rules will be executed
    kieSession.insert(comparisonOperatorEntity);
​
    kieSession.fireAllRules();
    kieSession.dispose();
}

3.6 implementation of specified rules

As can be seen from the previous case, when we call the rule code, the rules that meet the conditions will be executed. So if we only want to execute one of these rules, how to implement it?

Drools provides us with a way to execute specified rules through rule filters. There is no need to modify the rule file, just modify the Java code, as follows:

KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
​
ComparisonOperatorEntity comparisonOperatorEntity = new ComparisonOperatorEntity();
comparisonOperatorEntity.setNames("Zhang San");
List<String> list = new ArrayList<String>();
list.add("Zhang San");
list.add("Li Si");
comparisonOperatorEntity.setList(list);
kieSession.insert(comparisonOperatorEntity);
​
//Only the specified rules are executed through the rule filter
kieSession.fireAllRules(new RuleNameEqualsAgendaFilter("rule_comparison_memberOf"));
​
kieSession.dispose();

3.7 keywords

Drools' keywords are divided into hard keywords and soft keywords.

Hard keywords are clearly not used when we define the package name or rule name in the rule file, otherwise the program will report an error. Although soft keywords can be used, they are not recommended.

Hard keywords include: true false null

Soft keywords include: lock on active date effective date expires no loop auto focus activation group agenda group ruleflow group entry point duration package import dialog sales enabled attributes rule extend when then template query declare function global Eval not in or exists for all aggregate collect from action reverse result end over init

3.8 Drools built-in method

The main function of the RHS part of the rule file is to control the execution of the rule engine by inserting, deleting or modifying the Fact data in the working memory. Drools provides some methods to operate the data in the working memory. After the operation is completed, the rule engine will re match the relevant rules. The rules that did not match successfully may be matched successfully after we modify the data.

Create the following entity classes:

package com.test.drools.entity;
​
import java.util.List;
​
/**
 * student
 */
public class Student {
    private int id;
    private String name;
    private int age;
    
    public int getId() {
        return id;
    }
​
    public void setId(int id) {
        this.id = id;
    }
​
    public String getName() {
        return name;
    }
​
    public void setName(String name) {
        this.name = name;
    }
​
    public int getAge() {
        return age;
    }
​
    public void setAge(int age) {
        this.age = age;
    }
}

3.8. 1. Update method

The update method is used to update the data in the working memory and re match the relevant rules.

Step 1: write a rule file / resources / rules / student DRL, the contents of the file are as follows

package student
import com.test.drools.entity.Student
​
/*
 The current rule file is used to test the built-in methods provided by Drools
*/
​
rule "rule_student_age Less than 10 years old"
    when
        $s:Student(age < 10)
    then
        $s.setAge(15);
        update($s);//Update the data, causing the relevant rules to re match
        System.out.println("rule rule_student_age Triggered when less than 10 years old");
end
​
rule "rule_student_age Less than 20 years old and more than 10 years old"
    when
        $s:Student(age < 20 && age > 10)
    then
        $s.setAge(25);
        update($s);//Update the data, causing the relevant rules to re match
        System.out.println("rule rule_student_age Triggered when less than 20 years old and more than 10 years old");
end
​
rule "rule_student_age Over 20 years old"
    when
        $s:Student(age > 20)
    then
        System.out.println("rule rule_student_age Trigger over 20 years old");
end

Step 2: write unit tests

KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
​
Student student = new Student();
student.setAge(5);
​
//Provide the data to the rule engine. The rule engine will match the rules according to the provided data. If the rules match successfully, the rules will be executed
kieSession.insert(student);
​
kieSession.fireAllRules();
kieSession.dispose();

From the console output, you can see that the three rules defined in the rule file are triggered.

Care should be taken to prevent life and death cycles when updating data.

3.8.2 insert method

The function of the insert method is to insert data into the working memory and re match the relevant rules.

Step 1: modify student The contents of the DRL file are as follows

package student
import com.test.drools.entity.Student
​
/*
 The current rule file is used to test the built-in methods provided by Drools
*/
​
rule "rule_student_age Equal to 10 years old"
    when
        $s:Student(age == 10)
    then
        Student student = new Student();
        student.setAge(5);
        insert(student);//Insert data, causing the relevant rules to re match
        System.out.println("rule rule_student_age Equal to 10 years old");
end
​
rule "rule_student_age Less than 10 years old"
    when
        $s:Student(age < 10)
    then
        $s.setAge(15);
        update($s);
        System.out.println("rule rule_student_age Triggered when less than 10 years old");
end
​
rule "rule_student_age Less than 20 years old and more than 10 years old"
    when
        $s:Student(age < 20 && age > 10)
    then
        $s.setAge(25);
        update($s);
        System.out.println("rule rule_student_age Triggered when less than 20 years old and more than 10 years old");
end
​
rule "rule_student_age Over 20 years old"
    when
        $s:Student(age > 20)
    then
        System.out.println("rule rule_student_age Trigger over 20 years old");
end

Step 2: write unit tests

KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
​
Student student = new Student();
student.setAge(10);
​
//Provide the data to the rule engine. The rule engine will match the rules according to the provided data. If the rules match successfully, the rules will be executed
kieSession.insert(student);
​
kieSession.fireAllRules();
kieSession.dispose();

It can be found from the console output that all four rules are triggered because only the first rule can be matched successfully when matching the rules first, but a data is inserted into the working memory in the first rule, resulting in the re matching of the rules. At this time, the second rule can be matched successfully. The data modification in the second rule leads to the successful matching of the third rule, and so on. Finally, all four rules are matched and executed successfully.

3.8. 3. Retract method

The function of the retract method is to delete the data in the working memory and re match the relevant rules.

Step 1: modify student The contents of the DRL file are as follows

package student
import com.test.drools.entity.Student
​
/*
 The current rule file is used to test the built-in methods provided by Drools
*/
​
rule "rule_student_age Delete data when equal to 10 years old"
    /*
    salience: Set the execution priority of the current rule. The higher the value, the higher the priority. The default value is 0
    Because the matching conditions of the current rule are the same as those of the following rules, priority needs to be set to ensure that the current rule is executed first
    */
    salience 100 
    when
        $s:Student(age == 10)
    then
        retract($s);//The function of the retract method is to delete the data in the working memory and re match the relevant rules.
        System.out.println("rule rule_student_age Delete data trigger when equal to 10 years old");
end
​
rule "rule_student_age Equal to 10 years old"
    when
        $s:Student(age == 10)
    then
        Student student = new Student();
        student.setAge(5);
        insert(student);
        System.out.println("rule rule_student_age Equal to 10 years old");
end
​
rule "rule_student_age Less than 10 years old"
    when
        $s:Student(age < 10)
    then
        $s.setAge(15);
        update($s);
        System.out.println("rule rule_student_age Triggered when less than 10 years old");
end
​
rule "rule_student_age Less than 20 years old and more than 10 years old"
    when
        $s:Student(age < 20 && age > 10)
    then
        $s.setAge(25);
        update($s);
        System.out.println("rule rule_student_age Triggered when less than 20 years old and more than 10 years old");
end
​
rule "rule_student_age Over 20 years old"
    when
        $s:Student(age > 20)
    then
        System.out.println("rule rule_student_age Trigger over 20 years old");
end

Step 2: write unit tests

KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
​
Student student = new Student();
student.setAge(10);
​
//Provide the data to the rule engine. The rule engine will match the rules according to the provided data. If the rules match successfully, the rules will be executed
kieSession.insert(student);
​
kieSession.fireAllRules();
kieSession.dispose();

It can be found from the console output that only the first rule is triggered, because the data in the working memory is deleted in the first rule, resulting in the second rule not matching successfully.

4. Rule attributes

We already know the composition of the rule body as follows:

rule "ruleName"
    attributes
    when
        LHS
    then
        RHS
end

This chapter explains the attributes attribute of the rule body. The attributes provided in Drools are shown in the following table (some attributes):

Attribute nameexplain
salienceSpecify rule execution priority
dialectSpecifies the language type used by the rule. The values are java and mvel
enabledSpecifies whether the rule is enabled
activation-groupActivate a group. Only one rule with the same group name can be triggered
no-loopPrevent dead circulation

4.1 enabled attribute

The values corresponding to the enabled property are true and false, and the default value is true.

It is used to specify whether the current rule is enabled. If the value set is false, the current rule will not be triggered whether it is successfully matched or not.

r

ule "rule_comparison_notMemberOf"
    //Specifies that the current rule is not available. The current rule will not be executed regardless of whether it matches successfully or not
    enabled false
    when
        ComparisonOperatorEntity(names not memberOf list)
    then
        System.out.println("rule rule_comparison_notMemberOf trigger");
end

4.2 dialect attribute

The dialect attribute is used to specify the language type used by the current rule. The values are java and mvel. The default value is java.

Note: mvel is an expression language based on java syntax.

Like regular expressions, mvel has operators that directly support set, array, and string matching.

mvel also provides a template language for configuring and constructing strings.

mvel expression includes attribute expression, Boolean expression, method call, variable assignment, function definition, etc.

4.3 salience attribute

The salience property is used to specify the execution priority of rules. The value type is Integer. The higher the value, the higher the priority. Each rule has a default execution order. If the salience property is not set, the execution order of the rule body is from top to bottom.

You can create a rule file salience DRL to test the salience attribute, as follows:

package test.salience
​
rule "rule_1"
    when
        eval(true)
    then
        System.out.println("rule rule_1 trigger");
end
    
rule "rule_2"
    when
        eval(true)
    then
        System.out.println("rule rule_2 trigger");
end
​
rule "rule_3"
    when
        eval(true)
    then
        System.out.println("rule rule_3 trigger");
end

You can see from the console that since the salience attribute is not set for the above three rules, the order of execution is from top to bottom according to the order of rules in the rule file. Next, let's modify the contents of the file:

package testsalience
​
rule "rule_1"
    salience 9
    when
        eval(true)
    then
        System.out.println("rule rule_1 trigger");
end
​
rule "rule_2"
    salience 10
    when
        eval(true)
    then
        System.out.println("rule rule_2 trigger");
end
​
rule "rule_3"
    salience 8
    when
        eval(true)
    then
        System.out.println("rule rule_3 trigger");
end

You can see from the console that the execution order of the rule file is from the largest to the smallest according to the salience value we set.

It is recommended to use the salience attribute to specify the execution priority explicitly when writing rules.

4.4 no loop attribute

The no loop attribute is used to prevent an endless loop. When the rule modifies the Fact object through functions such as update, the current rule may be activated again, resulting in an endless loop. The value type is Boolean, and the default value is false. The test steps are as follows:

Step 1: write a rule file / resource / rules / noloop drl

package testnoloop
import com.test.drools.entity.Student
/*
    This rule file is used to test the no loop attribute
*/
rule "rule_noloop"
    // no-loop true
    when
        $student:Student(age == 25)
    then
        update($student);//Note that executing update here will cause the current rule to be reactivated
        System.out.println("rule rule_noloop trigger");
end

Step 2: write unit tests

KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
​
Student student = new Student();
student.setAge(25);
​
//Provide the data to the rule engine. The rule engine will match the rules according to the provided data. If the rules match successfully, the rules will be executed
kieSession.insert(student);
​
kieSession.fireAllRules();
kieSession.dispose();

From the console, we can see that an endless loop occurs because we do not set the value of the no loop attribute. Next, set the value of no loop to true. If you test again, there will be no life and death cycle.

4.5 activation group attribute

The activation group property refers to the activation group. The value is String type. Only one rule with the same group name can be triggered.

Step 1: write a rule file / resources / rules / activationgroup drl

package testactivationgroup
/*
    This rule file is used to test the activation group attribute
*/
    
rule "rule_activationgroup_1"
    activation-group "mygroup"
    when
    then
        System.out.println("rule rule_activationgroup_1 trigger");
end
​
rule "rule_activationgroup_2"
    activation-group "mygroup"
    when
    then
        System.out.println("rule rule_activationgroup_2 trigger");
end

Step 2: write unit tests

KieServices kieServices = KieServices.Factory.get();
KieContainer kieClasspathContainer = kieServices.getKieClasspathContainer();
KieSession kieSession = kieClasspathContainer.newKieSession();
kieSession.fireAllRules();
kieSession.dispose();

It can be found from the console that only one of the above two rules is triggered because they belong to the same group. If multiple rules in the same group can be matched successfully, which one can be triggered can be determined through the salience attribute.

5. Spring Boot integrates Drools

At present, Spring Boot has become the mainstream in enterprise development. In this section, we will integrate Spring Boot with Drools. Specific operation steps:

Step 1: create maven project springboot_drools and configure POM xml

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.5.RELEASE</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.cesgroup</groupId>
    <artifactId>springboot_drools</artifactId>
    <version>1.0-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <!--drools Rule engine-->
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-core</artifactId>
            <version>7.10.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.drools</groupId>
            <artifactId>drools-compiler</artifactId>
            <version>7.10.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.kie</groupId>
            <artifactId>kie-api</artifactId>
            <version>7.10.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.kie</groupId>
            <artifactId>kie-spring</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-tx</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-beans</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-core</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.springframework</groupId>
                    <artifactId>spring-context</artifactId>
                </exclusion>
            </exclusions>
            <version>7.10.0.Final</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Step 2: create / resources / application YML file

server:
  port: 8080
spring:
  application:
    name: drools_springboot

Step 3: create the rule file / resources / rules / HelloWorld drl

package helloworld
rule "rule_helloworld"
    when
        eval(true)
    then
        System.out.println("Rules: rule_helloworld trigger...");
end

Step 4: write the configuration class DroolsConfig

package com.test.drools.config;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieRepository;
import org.kie.api.runtime.KieContainer;
import org.kie.api.runtime.KieSession;
import org.kie.internal.io.ResourceFactory;
import org.kie.spring.KModuleBeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.Resource;
import java.io.IOException;
/**
 * Rule engine configuration class
 */
@Configuration
public class DroolsConfig {
    //Specify the directory where the rule file is stored
    private static final String RULES_PATH = "rules/";
    private final KieServices kieServices = KieServices.Factory.get();
    @Bean
    @ConditionalOnMissingBean
    public KieFileSystem kieFileSystem() throws IOException {
        KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
        ResourcePatternResolver resourcePatternResolver =  new PathMatchingResourcePatternResolver();
        Resource[] files = resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "*.*");
        String path = null;
        for (Resource file : files) {
            path = RULES_PATH + file.getFilename();
            kieFileSystem.write(ResourceFactory.newClassPathResource(path, "UTF-8"));
        }
        return kieFileSystem;
    }
    @Bean
    @ConditionalOnMissingBean
    public KieContainer kieContainer() throws IOException {
        KieRepository kieRepository = kieServices.getRepository();
        kieRepository.addKieModule(kieRepository::getDefaultReleaseId);
        KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem());
        kieBuilder.buildAll();
        return kieServices.newKieContainer(kieRepository.getDefaultReleaseId());
    }
    @Bean
    @ConditionalOnMissingBean
    public KieBase kieBase() throws IOException {
        return kieContainer().getKieBase();
    }
    @Bean
    @ConditionalOnMissingBean
    public KModuleBeanFactoryPostProcessor kiePostProcessor() {
        return new KModuleBeanFactoryPostProcessor();
    }
}

Step 5: create RuleService class

package com.test.drools.service;
​
import org.kie.api.KieBase;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
​
@Service
public class RuleService {
    @Autowired
    private KieBase kieBase;
    public void rule(){
        KieSession kieSession = kieBase.newKieSession();
        kieSession.fireAllRules();
        kieSession.dispose();
    }
}

Step 6: create HelloController class

package com.test.drools.controller;
​
import com.test.drools.service.RuleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
​
@RestController
@RequestMapping("/hello")
public class HelloController {
    @Autowired
    private RuleService ruleService;
    @RequestMapping("/rule")
    public String rule(){
        ruleService.rule();
        return "OK";
    }
}

Step 7: create the startup class DroolsApplication

package com.test.drools;
​
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
​
@SpringBootApplication
public class DroolsApplication {
    public static void main(String[] args) {
        SpringApplication.run(DroolsApplication.class,args);
    }
}

Step 8: start the service and access http://localhost:8080/hello/rule

Topics: Java Spring Spring Boot Back-end