What is “Easy Rules”?
Easy Rules is a simple yet powerful Java Rules engine that provides the following features:
- Lightweight framework and easy-to-learn API
- Pojo-based development and annotation programming model
- Define abstract business rules and apply them easily
- Supports the ability to create composite rules from simple rules
- Support for the ability to define rules using expression languages such as MVEL and SpEL
In a very interesting article on rules engines, Martin Fowler says:
You can build a simple rules engine yourself. All you need to do is create a set of objects with conditions and actions, store them in a collection, and run them to evaluate conditions and perform actions.
This is what Easy Rules does. It provides abstract Rules to create Rules with conditions and actions. The RulesEngine API runs a series of Rules to evaluate conditions and execute actions.
Runtime environment
Easy Rules is a Java library that needs to run in Java 1.7 or above.
Maven rely on
<! -- Easy Rules core library -->
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-core</artifactId>
<version>3.3.0</version>
</dependency>
<! -- Rule defines file format, supports JSON, YAML, etc. -->
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-support</artifactId>
<version>3.3.0</version>
</dependency>
<! -- support mVEL rule syntax library -->
<dependency>
<groupId>org.jeasy</groupId>
<artifactId>easy-rules-mvel</artifactId>
<version>3.3.0</version>
</dependency>
Copy the code
Define the rules
Most business rules can be represented by the following definitions:
- Name: The unique rule name in the rule namespace
- Description: A brief description of the rules
- Priority: Rule priority relative to other rules
- Facts: a set of known facts to match a rule
- Conditions: A set of conditions that should be met given certain facts in order to match this rule
- Action: a set of actions to be performed when a condition is met (facts can be added/deleted/modified)
Easy Rules provides an abstraction for each of the key points that define business Rules.
In Easy Rules, a Rule is represented by the Rule interface:
public interface Rule {
/** * Conditions *@returnReturn true if the facts provided apply to this rule, false */ otherwise
boolean evaluate(Facts facts);
/** * Change the method to encapsulate the rule actions *@throwsAn Exception */ is thrown if an error occurs during execution
void execute(Facts facts) throws Exception;
//Getters and setters for rule name, description and priority omitted.
}
Copy the code
The evaluate method encapsulates the condition that must be evaluated to TRUE to trigger the rule.
The execute method encapsulates the actions that should be performed if the rule conditions are met. ConditionandAction interface representation.
Rules can be defined in two different ways:
- Define it declaratively by adding comments to the POJO
- Defined programmatically through the RuleBuilder API
1. Define rules with annotations
These are the most common ways to define rules, but you can also implement the Rulei interface or inherit the BasicRule class if you want.
@Rule(name = "my rule", description = "my rule description", priority = 1)
public class MyRule {
@Condition
public boolean when(@Fact("fact") fact) {
//my rule conditions
return true;
}
@Action(order = 1)
public void then(Facts facts) throws Exception {
//my actions
}
@Action(order = 2)
public void finally(a) throws Exception {
//my final actions}}Copy the code
The @condition annotation marks the method for calculating rule conditions. This method must be public, can have one or more arguments annotated with @fact, and return a Boolean type. Only one method can be annotated with @condition.
The @action annotation marks the method to perform the rule Action. Rules can have more than one operation. You can use the order attribute to perform the actions in the specified order. By default, the order of operations is 0.
2. Define rules using the RuleBuilder API
Rule rule = new RuleBuilder()
.name("myRule")
.description("myRuleDescription")
.priority(3)
.when(condition)
.then(action1)
.then(action2)
.build();
Copy the code
In this example, the Condition instance Condition, and the Action instances are Action1 and Action2.
Define the fact
The Facts API is an abstraction of a set of Facts that are checked for rules. Internally, Facts instances hold HashMap<String, Object>, which means:
- Facts need to be named. They should have a unique name and cannot be empty
- Any Java object can act as a fact
Here is an instance defining fact:
Facts facts = new Facts();
facts.add("rain".true);
Copy the code
Facts can be injected with rule conditions, and the action method uses the @fact annotation. In the following rule, the rain fact is injected into the rain parameter of the itRains method:
@Rule
class WeatherRule {
@Condition
public boolean itRains(@Fact("rain") boolean rain) {
return rain;
}
@Action
public void takeAnUmbrella(Facts facts) {
System.out.println("It rains, take an umbrella!");
// can add/remove/modify facts}}Copy the code
The Facts type parameter is injected into known Facts (like the action method takeAnUmbrella).
If an injected FACT is missing, the engine throws a RuntimeException.
Defining a rule engine
As of version 3.1, Easy Rules provides two implementations of the RulesEngine interface:
- DefaultRulesEngine: Rules are applied according to their natural order (priority by default).
- InferenceRulesEngine: Continue applying rules to known facts until the rules are no longer applied.
Create a rules engine
To create a rules engine, use the constructor for each implementation:
RulesEngine rulesEngine = new DefaultRulesEngine();
// or
RulesEngine rulesEngine = new InferenceRulesEngine();
Copy the code
You can then trigger the registration rule as follows:
rulesEngine.fire(rules, facts);
Copy the code
Rule engine parameters
The Easy Rules engine can configure the following parameters:
Parameter Type Required Default
rulePriorityThreshold int no MaxInt
skipOnFirstAppliedRule boolean no false
skipOnFirstFailedRule boolean no false
skipOnFirstNonTriggeredRule boolean no false
Copy the code
- SkipOnFirstAppliedRule: Tells the engine to skip subsequent rules when a rule is triggered.
- SkipOnFirstFailedRule: Tells the engine to skip subsequent rules if they fail.
- SkipOnFirstNonTriggeredRule: tell a rule engine will not be triggered to skip the back of the rules.
- RulePriorityThreshold: Tells the engine to skip the next rule if the priority exceeds the defined threshold. Version 3.3 is not supported. The default value is MaxInt.
These parameters can be specified using the RulesEngineParameters API:
RulesEngineParameters parameters = new RulesEngineParameters()
.rulePriorityThreshold(10)
.skipOnFirstAppliedRule(true)
.skipOnFirstFailedRule(true)
.skipOnFirstNonTriggeredRule(true);
RulesEngine rulesEngine = new DefaultRulesEngine(parameters);
Copy the code
To get parameters from the engine, use the following code snippet:
RulesEngineParameters parameters = myEngine.getParameters();
Copy the code
This allows you to reset the engine parameters after the engine is created.
Hello world example
We will create a rule that always fires, printing “Hello World” to the console when executed. The rules are as follows:
@Rule(name = "Hello World rule", description = "Always say hello world")
public class HelloWorldRule {
@Condition
public boolean when(a) {
return true;
}
@Action
public void then(a) throws Exception {
System.out.println("hello world"); }}Copy the code
Now, let’s create a rule engine and fire this rule
public class Launcher {
public static void main(String[] args) {
// create facts
Facts facts = new Facts();
// create rules
Rules rules = new Rules();
rules.register(new HelloWorldRule());
// create a rules engine and fire rules on known facts
RulesEngine rulesEngine = newDefaultRulesEngine(); rulesEngine.fire(rules, facts); }}Copy the code
Output results:
INFO: Engine parameters { skipOnFirstAppliedRule = false, skipOnFirstNonTriggeredRule = false, skipOnFirstFailedRule = false, priorityThreshold = 2147483647 }
INFO: Registered rules:
INFO: Rule { name = 'Hello World rule', description = 'Always say hello world', priority = '2147483646'}
INFO: Rules evaluation started
INFO: Rule 'Hello World rule' triggered
Hello world
INFO: Rule 'Hello World rule' performed successfully
Copy the code
Ok, we’re done.