Chapter 5. The Rule Language

5.1. Rule Language

5.1.1. Overview

Drools has a native rule language. This format is very light in terms of punctuation, and supports natural and domain specific languages via "expanders" that allow the language to morph to your problem domain.

5.1.2. A rule file

A rule file is typically a file with a .drl extension. In a DRL file you can have multiple rules, queries and functions, as well as some resource declarations like imports, globals, and attributes that are assigned and used by your rules and queries. However, you are also able to spread your rules across multiple rule files (in that case, the extension .rule is suggested, but not required) - spreading rules across files can help with managing large numbers of rules. A DRL file is simply a text file.

5.1.3. The structure of a rule file

The overall structure of a rule file is the following:

Example 5.1. Rules file

package package-name

imports

globals

functions

queries

rules
The order in which the elements are declared is not important, except for the package name that, if declared, must be the first element in the rules file. All elements are optional, so you will use only those you need.

5.1.4. What is a rule

For the inpatients, just as an early view, a rule has the following rough structure:
rule "name"
    attributes
    when
        LHS
    then
        RHS
end
Mostly punctuation is not needed, even the double quotes for "name" are optional, as are newlines. Attributes are simple (always optional) hints to how the rule should behave. LHS is the conditional parts of the rule, which follows a certain syntax which is covered below. RHS is basically a block that allows dialect specific semantic code to be executed.
It is important to note that white space is not important, except in the case of domain specific languages, where lines are processed one by one and spaces may be significant to the domain language.

5.1.5. Hard Keywords

Hard keywords are words which you cannot use when naming your domain objects, properties, methods, functions and other elements that are used in the rule text. The hard keywords are true, false, and null.

5.1.6. Soft Keywords

Soft keywords can be used for naming domain objects, properties, methods, functions and other elements. The rules engine recognizes their context and processes them accordingly.

5.1.7. List of Soft Keywords

  • lock-on-active
  • date-effective
  • date-expires
  • no-loop
  • auto-focus
  • activation-group
  • agenda-group
  • ruleflow-group
  • entry-point
  • duration
  • package
  • import
  • dialect
  • salience
  • enabled
  • attributes
  • rule
  • extend
  • when
  • then
  • template
  • query
  • declare
  • function
  • global
  • eval
  • not
  • in
  • or
  • and
  • exists
  • forall
  • accumulate
  • collect
  • from
  • action
  • reverse
  • result
  • end
  • over
  • init

5.1.8. Comments

Comments are sections of text that are ignored by the rule engine. They are stripped out when they are encountered, except inside semantic code blocks (like a rule's RHS).

5.1.9. Single Line Comment Example

This is what a single line comment looks like. To create single line comments, you can use '//'. The parser will ignore anything in the line after the comment symbol:
rule "Testing Comments"
when
    // this is a single line comment
    eval( true ) // this is a comment in the same line of a pattern
then
    // this is a comment inside a semantic code block
end

5.1.10. Multi-Line Comment Example

This is what a multi-line comment looks like. This configuration comments out blocks of text, both in and outside semantic code blocks:
rule "Test Multi-line Comments"
when
    /* this is a multi-line comment
       in the left hand side of a rule */
    eval( true )
then
    /* and this is a multi-line comment
       in the right hand side of a rule */
end

5.1.11. Error Messages

JBoss Rules introduces standardized error messages. This standardization aims to help users to find and resolve problems in a easier and faster way.

5.1.12. Error Message Format

This is the standard error message format.
Error Message Format Example

Figure 5.1. Error Message Format Example

1st Block: This area identifies the error code.
2nd Block: Line and column information.
3rd Block: Some text describing the problem.
4th Block: This is the first context. Usually indicates the rule, function, template or query where the error occurred. This block is not mandatory.
5th Block: Identifies the pattern where the error occurred. This block is not mandatory.

5.1.13. Error Messages Description

Table 5.1. Error Messages

Error Message Description Example  
[ERR 101] Line 4:4 no viable alternative at input 'exits' in rule one
Indicates when the parser came to a decision point but couldn't identify an alternative.
1: rule one
2:   when
3:     exists Foo()
4:     exits Bar()
5:   then
6: end
 
[ERR 101] Line 3:2 no viable alternative at input 'WHEN'
This message means the parser has encountered the token WHEN (a hard keyword) which is in the wrong place, since the rule name is missing.
1: package org.drools;
2: rule
3:   when
4:     Object()
5:   then
6:     System.out.println("A RHS");
7: end
 
[ERR 101] Line 0:-1 no viable alternative at input '<eof>' in rule simple_rule in pattern [name]
Indicates an open quote, apostrophe or parentheses.
1: rule simple_rule
2:   when
3:     Student( name == "Andy )
4:   then
5: end
 
[ERR 102] Line 0:-1 mismatched input '<eof>' expecting ')' in rule simple_rule in pattern Bar
Indicates that the parser was looking for a particular symbol that it didn't end at the current input position.
1: rule simple_rule
2:   when
3:     foo3 : Bar(
 
[ERR 102] Line 0:-1 mismatched input '<eof>' expecting ')' in rule simple_rule in pattern [name]
This error is the result of an incomplete rule statement. Usually when you get a 0:-1 position, it means that parser reached the end of source. To fix this problem, it is necessary to complete the rule statement.
1: package org.drools;
2:
3: rule "Avoid NPE on wrong syntax"
4:   when
5:     not( Cheese( ( type == "stilton", price == 10 ) || ( type == "brie", price == 15 ) ) from $cheeseList )
6:   then
7:     System.out.println("OK");
8: end
 
[ERR 103] Line 7:0 rule 'rule_key' failed predicate: {(validateIdentifierKey(DroolsSoftKeywords.RULE))}? in rule
A validating semantic predicate evaluated to false. Usually these semantic predicates are used to identify soft keywords.
1: package nesting;
 2: dialect "mvel"
 3:
 4: import org.drools.Person
 5: import org.drools.Address
 6: 
 7: fdsfdsfds
 8: 
 9: rule "test something"
10:   when
11:     p: Person( name=="Michael" )
12:   then
13:     p.name = "other";
14:     System.out.println(p.name);
15: end
 
[ERR 104] Line 3:4 trailing semi-colon not allowed in rule simple_rule
This error is associated with the eval clause, where its expression may not be terminated with a semicolon. This problem is simple to fix: just remove the semi-colon.
1: rule simple_rule
2:   when
3:     eval(abc();)
4:   then
5: end
 
[ERR 105] Line 2:2 required (...)+ loop did not match anything at input 'aa' in template test_error
The recognizer came to a subrule in the grammar that must match an alternative at least once, but the subrule did not match anything. To fix this problem it is necessary to remove the numeric value as it is neither a valid data type which might begin a new template slot nor a possible start for any other rule file construct.
1: template test_error
2:   aa s  11;
3: end
 

5.1.14. Package

A package is a collection of rules and other related constructs, such as imports and globals. The package members are typically related to each other, such as HR rules. A package represents a namespace, which ideally is kept unique for a given grouping of rules. The package name itself is the namespace, and is not related to files or folders in any way.
It is possible to assemble rules from multiple rule sources, and have one top level package configuration that all the rules are kept under (when the rules are assembled). It is not possible to merge into the same package resources declared under different names. A single Rulebase may, however, contain multiple packages built on it. A common structure is to have all the rules for a package in the same file as the package declaration (so that is it entirely self-contained).

5.1.15. Import Statements

Import statements work like import statements in Java. You need to specify the fully qualified paths and type names for any objects you want to use in the rules. JBoss Rules automatically imports classes from the Java package of the same name, and also from the package java.lang.

5.1.16. Using Globals

In order to use globals you must:
  1. Declare the global variable in the rules file and use it in rules. Example:
    global java.util.List myGlobalList;
    
    rule "Using a global"
    when
        eval( true )
    then
        myGlobalList.add( "Hello World" );
    end
    
  2. Set the global value on the working memory. It is best practice to set all global values before asserting any fact to the working memory. Example:
    List list = new ArrayList();
    WorkingMemory wm = rulebase.newStatefulSession();
    wm.setGlobal( "myGlobalList", list );
    

5.1.17. The From Element

The from element allows you to pass a Hibernate session as a global. It also lets you pull data from a named Hibernate query.

5.1.18. Using Globals with an e-Mail Service

Procedure 5.1. Task

  1. Open the integration code that is calling the rule engine.
  2. Obtain your emailService object and then set it in the working memory.
  3. In the DRL, declare that you have a global of type emailService and give it the name "email".
  4. In your rule consequences, you can use things like email.sendSMS(number, message).

    Warning

    Globals are not designed to share data between rules and they should never be used for that purpose. Rules always reason and react to the working memory state, so if you want to pass data from rule to rule, assert the data as facts into the working memory.

    Important

    Do not set or change a global value from inside the rules. We recommend to you always set the value from your application using the working memory interface.