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
-
Business rules are separated from system code to realize centralized management of business rules
-
Business rules can be extended and maintained at any time without restarting the service
-
Business rules can be dynamically modified to quickly respond to changes in requirements
-
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
-
Reduces the cost and risk of hard coding business rules
-
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:
-
Risk control system - risk loan, risk assessment
-
Anti fraud project - bank loan, credit investigation and verification
-
Decision making platform system - financial calculation
-
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 number | Rule name | describe |
---|---|---|
1 | Rule one | There is no discount for books with a total price of less than 100 yuan |
2 | Rule 2 | If the total price of the books purchased is 100 to 200 yuan, the discount is 20 yuan |
3 | Rule three | If the total price of the books purchased is 200 to 300 yuan, the discount is 50 yuan |
4 | Rule 4 | If 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:
keyword | describe |
---|---|
package | Package name is limited to logical management. Queries or functions under the same package name can be called directly |
import | Used to import classes or static methods |
global | global variable |
function | Custom function |
query | query |
rule end | Regular 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:
- There must be a Fact object of type Order in the working memory ----- type constraint
- The originalPrice property value of the Fact object must be less than 200 ------ property constraint
- 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:
Symbol | explain |
---|---|
> | greater than |
< | less than |
>= | Greater than or equal to |
<= | Less than or equal to |
== | be equal to |
!= | Not equal to |
contains | Check whether a property value of a Fact object contains a specified object value |
not contains | Check whether a property value of a Fact object does not contain a specified object value |
memberOf | Judge whether a property of a Fact object is in one or more collections |
not memberOf | Determine whether a property of a Fact object is not in one or more collections |
matches | Determine whether the properties of a Fact object match the provided standard Java regular expression |
not matches | Determine 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 name | explain |
---|---|
salience | Specify rule execution priority |
dialect | Specifies the language type used by the rule. The values are java and mvel |
enabled | Specifies whether the rule is enabled |
activation-group | Activate a group. Only one rule with the same group name can be triggered |
no-loop | Prevent 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