站内搜索: 请输入搜索关键词
当前页面: 在线文档首页 > JBoss RULES 4.0.0.11754MR2 manual 英文版使用指南文档

3.5. Rule - JBoss RULES 4.0.0.11754MR2 manual 英文版使用指南文档

3.5. Rule

rule

Figure 3.8. rule


The Rule construct is where is clearly the most important construct. Rules are of the form "IF" something "THEN" action (of course we chose the keywords "when" and "then") - in the style of production rules.

A rule must have a name, and be a unique name for a rule package. If a rule name is to have spaces, then it will need to be in double quotes (its best to always use double quotes).

Attributes are optional, and are described below (they are best kept as one per line).

The LHS of the rule follows the "when" keyword (ideally on a new line), similarly the RHS follows the "then" keyword (ideally on a newline). The rule is terminated by the keyword "end". Rules cannot be nested of course.

3.5.1. Left Hand Side

The Left Hand Side (LHS) is a common name for the conditional part of the rule.

To interpret the following diagram, refer to the sections below for the details.

Left Hand Side

Figure 3.9. Left Hand Side


pattern

Figure 3.10. pattern


3.5.2. The Right Hand Side

The Right Hand Side (RHS) is a common name for the consequence or action part of the rule. The purpose of the right hand side is to retract or add facts to working memory, and also invoke arbitary actions specific to your application. In practical terms, the RHS is a block of code that is executed when the rule fires.

There are a few convenience methods you can use to modify working memory:

"modify(obj);" will tell the engine that an object has changed (one that has been bound to something on the LHS) and rules may need to be reconsidered.

"assert(new Something());" will place a new object of your creation in working memory.

"assertLogical(new Something());" is similar to assert, but the object will be automatically retracted when there are no more facts to support the truth of the currently firing rule.

"retract(obj);" removes an object from working memory.

These convenience methods are basically macros that provide short cuts to the KnowldgeHelper instance (refer to the KnowledgeHelper interface for more advanced operations). The KnowledgeHelper interface is made available to the RHS code block as a variable called "drools". If you provide "Property Change Listeners" to your java beans that you are asserting into the engine, you can avoid the need to call "modify" when the object changes.

3.5.3. Rule Attriutes

rule attributes

Figure 3.11. rule attributes


3.5.3.1. no-loop

default value : false

type : Boolean

When the Rule's consequence modifies a fact it may cause the Rule to activate again, causing recursion. Setting no-loop to true means the attempt to create the Activation will be ignored.

3.5.3.2. salience

default value : 0

type : integer

Each rule has a salience attribute that can be assigned an Integer number, defaults to zero, the Integer and can be negative or positive. Salience is a form of priority where rules with higher salience values are given higher priority when ordered in the Activation queue.

3.5.3.3. agenda-group

default value : MAIN

type : String

Agenda group's allow the user to partition the Agenda providing more execution control. Only rules in the focus group are allowed to fire.

3.5.3.4. auto-focus

default value false

type : Boolean

When a rule is activated if the auto-focus value is true and the Rule's agenda-group does not have focus then it is given focus, allowing the rule to potentially fire.

3.5.3.5. activation-group

default value N/A

type : String

Rules that belong to the same named activation-group will only fire exclusively. In other words, the first rule in an activation-group to fire will cancel the other rules activations (stop them from firing). The Activtion group attribute is any string, as long as the string is identical for all the rules you need to be in the one group.

NOTE: this used to be called Xor group, but technically its not quite an Xor, but you may hear people mention Xor group, just swap that term in your mind with activation-group.

3.5.3.6. duration

default value : no default value

type : long

Example 3.1. Some attribute examples

rule "my rule"
  salience 42
  agenda-group "number 1"
    when ...

3.5.4. Column

Column

Figure 3.12. Column


Example 3.2. Column

Cheese( )
Cheese( type == "stilton", price < 10 )

A Rule consists of Field Constraints on one or more Object Types. Internally each matched Object Type instance is stored in an array. If we match against three objects Person, Person and Pet we have a three element array; in Drools we refer to this list of Facts as a Tuple - each element in the array is a Column. Each Object Type instance is filtered through zero or more Field Constraints - the term Column is used to refer to this list of constraints on the Object type. The first example has no constraints and will match any Cheese instance in the Working Memory, regardless of its field values. The second case refers to two Literal Field Constraints on against instances of a Cheese object - they are seperated by a comma, which implicitly means "and".

Column

Figure 3.13. Column


Example 3.3. Bound Column

cheapStilton : Cheese( type == "stilton", price < 10 )

This is similar to the previous case, but in this case we are binding a variable to that instance of Cheese that the rule engine will match. This means you can use cheapStilton in another condition, or perhaps in the consequence part of the rule. You can also eat it, but I wouldn't.

3.5.4.1. Field Constraints

Field constraints place constraints on the Fact objects for the rule engine to match/select out of working memory. They work comparing/evaluating "field" values from the fact object instances.

A "field" is not a field in the sense of a public or private member of a class. A field is an accessible method. If your model objects follow the java bean pattern, then fields are exposed using "getXXX" or "isXXX" methods (these are methods that take no arguments, and return something). You can access fields either by using the bean-name convention (so "getType" can be accessed as "type").

For example, refering to our Cheese class, the following : Cheese(type == ...) uses the getType() method on the a cheese instance. You can also access non getter methods, like "toString()" on the Object for instance (in which case, you do Cheese(toString == ..) - you use the full name of the method with correct capitalisation, but not brackets). Do please make sure that you are accessing methods that take no parameters, and are in-fact "accessors" (as in, they don't change the state of the object in a way that may effect the rules - remember that the rule engine effectively caches the results of its matching inbetween invocations to make it faster).

Note that if primitive types are used for a field, Drools will autobox them to their corresponding object types (even if you are using java 1.4) - however on java 1.4 there is currently no auto-unboxing when inside code expressions or blocks. On the whole, it is probably best to use the non primitive types in the model objects you are using in your rules. If you use Java 5, then you get the best of both worlds (you can let the compiler autobox for you - much neater) if you use Java 5, Drools will honor that if you use the JDT semantic compiler (JANINO does not yet support Java 5).

3.5.4.1.1. JavaBeans as facts

A note on JavaBeans: The JavaBean convention is followed, but as shown above, you can access non getter methods by using the method name. The syntax is case sensitive. In the case of getter methods like "getURI" which uses capitals, the property name is "URI" as there is more then one capital letter after the "get" - this is exactly as per the JavaBean standard (in fact, the Instrospector utility is used).

3.5.4.1.2. Operators
Operators

Figure 3.14. Operators


There are a number of opreators that can be used with the various Field Constraints. Valid operators are dependent on the field type. Generally they are self explanatory based on the type of data: for instance, for date fields, "<" means "before" and so on. "Matches" is only applicable to string fields, "contains" and "excludes" is only applicable to Collection type fields.

3.5.4.1.3. Literal Constraints

The most basic of Field Constraints is the Literal Constraint which allows the user to constrain a field to a given value.

A note on nulls: you can do checks against fields that are or maybe null, using == and != as you would expect, and the literal "null" keyword, like: Cheese(type != null). Literal Constraints, specifically the '==' operator, provide for very fast execution as we can use hashing to improve performance.

Literal Constraints

Figure 3.15. Literal Constraints


Numeric

All standard java numeric primitives are supported

Valid operators:

  • ==

  • !=

  • >

  • <

  • >=

  • <=

Example 3.4. Numeric Literal Constraint

Cheese( quantity == 5 )

Date

Currently only "dd-mmm-yyyy" date format is supported by default. You can customise this by providing an alternative date format mask as a System property ("drools.dateformat" is the name of the property). If more control is required, use the predicate constraint.

Valid operators:

  • ==

  • !=

  • >

  • <

  • >=

  • <=

Example 3.5. Date Literal Constraint

Cheese( bestBefore < "27-Oct-2007" )

String

Any valid Java String is allowed.

Valid operators:

  • ==

  • !=

Example 3.6. String Literal Constraint

Cheese( type == "stilton" )

Boolean

only true or false can be used. 0 and 1 are not recognised, nor is Cheese ( smelly ) is not allowed

Valid operators:

  • true

  • false

Example 3.7. Boolean Literal Constraint

Cheese( smelly == true )

Matches Operator

Any valid Java Regular Expression can be used to match String fields.

Example 3.8. Regular Expression Constraint

Cheese( type matches "(Buffulo)?\\S*Mozerella" )

Contains Operator

'contains' is a special operator that can be used to check if a field's Collection contains an object.

Example 3.9. Literal Constraints with Collections

CheeseCounter( cheeses contains "stilton" )
CheeseCounter( cheeses excludes "chedder" )

Excludes Operator

'excludes' is a special operator that can be used to check if a field's Collection does not contains an object.

Example 3.10. Literal Constraints with Collections

CheeseCounter( cheeses excludes "cheddar" )

3.5.4.1.4. Bound Variable Constraint
Bound Variable Declaration

Figure 3.16. Bound Variable Declaration


Variables can be bound to Facts and their Fields and then used in subsequent Field Constraints. A bound variable is called a Declaration. Declarations cannot be used with 'matches', although it works with 'contains'. Valid operators are determined by the type of the field being constrained. Bound Variables, specifically the '==' and '=!' operators, provide for very fast execution as we can use hashing to improve performance.

Example 3.11. Bound Field using '==' operator

Person( likes : favouriteCheese )
Cheese( type == likes )

'likes' is our variable, our Declaration, that is bound to the favouriteCheese field for any matching Person instance and is used to constrain the type of Cheese in the following Column. Any valid java variable name can be used, including '$'; which you will often see used to help differentiate declarations from fields. The exampe below shows a declaration bound to the Columns Object Type instance itself and used with a 'contains' operator, note the optional use of '$' this time.


Example 3.12. Bound Fact using 'contains' operator

$stilton : Cheese( type == "stilton" )
Cheesery( cheeses contains $stilton )

3.5.4.1.5. Predicate Constraints
Predicate expression

Figure 3.17. Predicate expression


A Predicate constraint can use any valid Java expression as long as it evaluated to a primitive boolean - avoid using any Drools keywords as Declaration identifiers. Previously bound declarations can be used in the expression. Functions used in a Predicate Constraint must return time constant results. All bound primitive declarations are boxed, there is currently no auto-unboxing (if you use java 5, this is all automatic).

This example will find all pairs of male/femal people where the male is 2 years older than the female.

Example 3.13. Return Value operator

Person( girlAge : age, sex = "F" )
Person( boyAge : age -> ( girlAge.intValue() == boyAge.intValue() + 2 ), sex = 'M' )

3.5.4.1.6. Return Value Constraints
Return Value expression

Figure 3.18. Return Value expression


A Return Value constraint can use any valid Java expression as long as it returns an object, it cannot return primitives - avoid using any Drools keywords as Declaration identifiers. Functions used in a Return value Constraint must return time constant results. Previously bound declarations can be used in the expression. All bound primitive declarations are boxed, there is currently no auto-unboxing. The returned value must be boxed if its a primitive..

Like the Predicate example this will find all pairs of male/femal people where the male is 2 years older than the female. Notice here we didn't have to bind the boyAge, making it a little simpler to read.

Example 3.14. Return Value operator

Person( girlAge : age, sex == "F" )
Person( age == ( new Integer(girlAge.intValue() + 2) ), sex == 'M' )

3.5.5. Conditional Elements

Conditional elements work on one or more Columns (which were described above). The most common one is "and" which is implicit when you have multiple Columns in the LHS of a rule that are not connected in anyway. Note that an 'and' cannot have a leading declaration binding like 'or' - this is obvious when you think about it. A declaration can only reference a single Fact, when the 'and' is satisfied it matches more than one fact - which fact would the declaration bind to?

3.5.5.1. 'and'

and

Figure 3.19. and


valid children : and, or, not, exists, column

Example 3.15. Column

Cheese( cheeseType : type ) && Person( favouriteCheese == cheeseType )
Cheese( cheeseType : type ) and Person( favouriteCheese == cheeseType )

3.5.5.2. 'or'

or

Figure 3.20. or


valid children : and, or, not, exists, column

Example 3.16. or

Person( sex == "f", age > 60 ) || Person( sex == "m", age > 65 )
Person( sex == "f", age > 60 ) or Person( sex == "m", age > 65 )

or

Figure 3.21. or


Example 3.17. or with binding

pensioner : ( Person( sex == "f", age > 60 ) or Person( sex == "m", age > 65 ) )

The 'or' conditional element results in multipe rule generation, called sub rules, for each possible logically outcome. the example above would result in the internal generation of two rules. These two rules work independently within the Working Memory, which means both can match, activate and fire - there is no shortcutting.

The best way to think of the OR conditional element is as a shortcut for generating 2 additional rules. When you think of it that way, its clear that for a single rule there could be multiple activations if both sides of the OR conditional element are true.

3.5.5.3. 'eval'

eval

Figure 3.22. eval


valid children : none

Eval is essentially a catch all which allows any semantic code (that returns a primitive primitive boolean) to be executed. This can refer to variables that were bound in the LHS of the rule, and functions in the rule package. An eval should be the last conditional element in the LHS of a rule. You can have multiple evals in a rule. Generally you would combine them with some column constraints.

Evals cannot be indexed and thus are not as optimal as using Field Constraints. However this makes them ideal for being used when functions return values that change over time, which is not allowed within Field Constraints. An Eval will be checked each time if all the other conditions in the rules are met.

For folks who are familiar with Drools 2.x lineage, the old Drools paramater and condition tags are equivalent to binding a variable to an appropriate type, and then using it in an eval node.

Example 3.18. eval

p1 : Parameter() 
p2 : Parameter()
eval( p1.getList().containsKey(p2.getItem()) )
eval( isValid(p1, p2) ) //this is how you call a function in the LHS - a function called "isValid"

3.5.5.4. 'not'

not

Figure 3.23. not


valid children: Column

'not' is first order logic's Existential Quantifier and checks for the non existence of something in the Working Memory. Currently only Columns may be nested in a 'not' but future versions of will allow 'and' and 'or' to be nested.

Example 3.19. No Busses

not Bus()

Example 3.20. No red Busses

not Bus(color == "red")
not ( Bus(color == "red", number == 42) ) //brackets are optional

3.5.5.5. 'exists'

exists

Figure 3.24. exists


valid children: Column

'exists' is first order logic's Existential Quantifier and checks for the existence of something in the Working Memory. Think of exist as meaning "at least one..". It is different from just having the Column on its own. If you had the column on its on, its kind of like saying "for each one of...". if you use exist with a Column, then the rule will only activate once regardless of how much data there is in working memory that matches that condition.

Currently only Columns may be nested in a 'exists' but future versions of will allow 'and' and 'or' to be nested.

Example 3.21. Atleast one Bus

exists Bus()

Example 3.22. Atleast one red Bus

exists Bus(color == "red")

3.5.5.6. 'group'

group

Figure 3.25. group


Grouping is similar to using parentheses in algebra, it makes the order of operations explicit.

Example 3.23. Example of groups

...
( 
  Message( status == Message.HELLO ) and Message(message != null) 
      or Message(status == null)  
)
...

Example 3.24. A rule example

rule "Approve if not rejected"
  salience -100 
  agenda-group "approval"
    when
        not Rejection() 
        p : Policy(approved == false, policyState:status)
        exists Driver(age > 25)
        Process(status == policyState)
    then
        log("APPROVED: due to no objections."); 
        p.setApproved(true);
end

3.5.6. A note on autoboxing and primitive types

Java 5 supports autoboxing and unboxing between primitives of appropriate types. This makes for very convenient and easy to read code. However, as drools runs in J2SE 1.4 as well, we can't rely on this. Thus we have to autobox at times. Fields that are referred to are autoboxed in the corresponding object type automatically (if they are already an object, then there is no change). However, it is important to note that they are not "unboxed" automatically. Thus if you bind to a field that is an "int" in your object model, it will behave like an Object in the rule (ie predicates, return value constraints and the RHS).

As a general rule, if possible, make your fields object types (at least until java 5), or at least think of your fields as object types even if they are not to start with). Another special note, is that for return value constraints, the return value snippet of code must return an Object (not a primitive). Now, I bet you can't wait for Java 5 to be the minimum ! The fact that not quite *everything* in java is an object causes headaches like this (keep those tablets handy).