Red Hat Training
A Red Hat training course is available for JBoss Enterprise SOA Platform
Chapter 5. Decision Tables
5.1. Decision Tables
Decision tables are a way of representing conditional logic. They are well suited to business level rules.
5.2. Decision Tables in Spreadsheets
JBoss Rules supports managing rules in a spreadsheet format. Supported formats are Excel (XLS) and CSV. This means that a variety of spreadsheet programs (such as Microsoft Excel, OpenOffice.org Calc, and others) can be utilized.
5.3. Open Office Example
Figure 5.1. Open Office Screenshot
In the above examples, the technical aspects of the decision table have been collapsed away (using a standard spreadsheet feature).
The rules start from row 17, with each row resulting in a rule. The conditions are in columns C, D, E, etc., and the actions are off-screen. The values' meanings are indicated by the headers in Row 16. (Column B is just a description.)
Note
Although the decision tables look like they process top down, this is not necessarily the case. Ideally, rules are authored without regard for the order of rows. This makes maintenance easier, as rows will not need to be shifted around all the time.
5.4. Rules and Spreadsheets
- Rules inserted into rows
- As each row is a rule, the same principles apply as with written code. As the rule engine processes the facts, any rules that match may fire.
- Agendas
- It is possible to clear the agenda when a rule fires and simulate a very simple decision table where only the first match effects an action.
- Multiple tables
- You can have multiple tables on one spreadsheet. This way, rules can be grouped where they share common templates, but are still all combined into one rule package.
5.5. The RuleTable Keyword
When using decision tables, the spreadsheet searches for the RuleTable keyword to indicate the start of a rule table (both the starting row and column).
Important
Keywords should all be in the same column.
5.6. The RuleSet Keyword
The RuleSet keyword indicates the name to be used in the rule package that will encompass all the rules. This name is optional, using a default, but it must have the RuleSet keyword in the cell immediately to the right.
5.7. Rule Template Example
Figure 5.2. Rule Template
- The RuleSet keyword indicates the name to be used in the rule package that will encompass all the rules. This name is optional, using a default, but it must have the RuleSet keyword in the cell immediately to the right. The other keywords visible in Column C are Import and Sequential.
- After the RuleTable keyword there is a name, used to prefix the names of the generated rules. The row numbers are appended to guarantee unique rule names.
- The column of RuleTable indicates the column in which the rules start; columns to the left are ignored.
- Referring to row 14 (the row immediately after RuleTable), the keywords CONDITION and ACTION indicate that the data in the columns below are for either the LHS or the RHS parts of a rule. There are other attributes on the rule which can also be optionally set this way.
- Row 15 contains declarations of ObjectTypes. The content in this row is optional, but if this option is not in use, the row must be left blank. When using this row, the values in the cells below (row 16) become constraints on that object type. In the above case, it generates
Person(age=="42")
andCheese(type=="stilton")
, where 42 and "stilton" come from row 18. In the above example, the "==" is implicit. If just a field name is given, the translator assumes that it is to generate an exact match. - Row 16 contains the rule templates themselves. They can use the "$param" placeholder to indicate where data from the cells below should be interpolated. (For multiple insertions, use "$1", "$2", etc., indicating parameters from a comma-separated list in a cell below.) Row 17 is ignored. It may contain textual descriptions of the column's purpose.
- Rows 18 and 19 show data, which will be combined (interpolated) with the templates in row 15, to generate rules. If a cell contains no data, then its template is ignored. (This would mean that some condition or action does not apply for that rule row.) Rule rows are read until there is a blank row. Multiple RuleTables can exist in a sheet.
- Row 20 contains another keyword, and a value. The row positions of keywords like this do not matter (most people put them at the top) but their column should be the same one where the RuleTable or RuleSet keywords should appear. In our case column C has been chosen to be significant, but any other column could be used instead.
Note
An ObjectType declaration can span columns (via merged cells), meaning that all columns below the merged range are to be combined into one set of constraints within a single pattern matching a single fact at a time, as opposed to non-merged cells containing the same ObjectType, but resulting in different patterns, potentially matching different or identical facts.
In the above example, rules would be rendered like the following (as it uses the "ObjectType" row):
//row 18 rule "Cheese_fans_18" when Person(age=="42") Cheese(type=="stilton") then list.add("Old man stilton"); end
Note
The constraints
age=="42"
and type=="stilton"
are interpreted as single constraints, to be added to the respective ObjectType in the cell above. If the cells above were spanned, then there could be multiple constraints on one "column".
Warning
Very large decision tables may have very large memory requirements.
5.8. Data-Defining Cells
There are two types of rectangular areas defining data that is used for generating a DRL file. One, marked by a cell labelled
RuleSet
, defines all DRL items except rules. The other one may occur repeatedly and is to the right and below a cell whose contents begin with RuleTable
. These areas represent the actual decision tables, each area resulting in a set of rules of similar structure.
A Rule Set area may contain cell pairs, one below the
RuleSet
cell and containing a keyword designating the kind of value contained in the other one that follows in the same row.
5.9. Rule Table Columns
The columns of a Rule Table area define patterns and constraints for the left hand sides of the rules derived from it, actions for the consequences of the rules, and the values of individual rule attributes. A Rule Table area should contain one or more columns, both for conditions and actions, and an arbitrary selection of columns for rule attributes, at most one column for each of these. The first four rows following the row with the cell marked with
RuleTable
are earmarked as header area, mostly used for the definition of code to construct the rules. It is any additional row below these four header rows that spawns another rule, with its data providing for variations in the code defined in the Rule Table header.
Note
All keywords are case insensitive.
Only the first worksheet is examined for decision tables.
5.10. Rule Set Entries
Entries in a Rule Set area may define DRL constructs (except rules), and specify rule attributes. While entries for constructs may be used repeatedly, each rule attribute may be given at most once, and it applies to all rules unless it is overruled by the same attribute being defined within the Rule Table area.
Entries must be given in a vertically stacked sequence of cell pairs. The first one contains a keyword and the one to its right the value. This sequence of cell pairs may be interrupted by blank rows or even a Rule Table, as long as the column marked by
RuleSet
is upheld as the one containing the keyword.
5.11. Entries in the Rule Set Area
Table 5.1. Entries in the Rule Set area
Keyword | Value | Usage |
---|---|---|
RuleSet | The package name for the generated DRL file. Optional, the default is rule_table . | Must be First entry. |
Sequential | "true" or "false". If "true", then salience is used to ensure that rules fire from the top down. | Optional, at most once. If omitted, no firing order is imposed. |
EscapeQuotes | "true" or "false". If "true", then quotation marks are escaped so that they appear literally in the DRL. | Optional, at most once. If omitted, quotation marks are escaped. |
Import | A comma-separated list of Java classes to import. | Optional, may be used repeatedly. |
Variables | Declarations of DRL globals, i.e., a type followed by a variable name. Multiple global definitions must be separated with a comma. | Optional, may be used repeatedly. |
Functions | One or more function definitions, according to DRL syntax. | Optional, may be used repeatedly. |
Queries | One or more query definitions, according to DRL syntax. | Optional, may be used repeatedly. |
Declare | One or more declarative types, according to DRL syntax. | Optional, may be used repeatedly. |
5.12. Rule Attribute Entries in the Rule Set Area
Table 5.2. Rule Attribute Entries in the Rule Set Area
Keyword | Initial | Value |
---|---|---|
PRIORITY | P | An integer defining the "salience" value for the rule. Overridden by the "Sequential" flag. |
DURATION | D | A long integer value defining the "duration" value for the rule. |
TIMER | T | A timer definition. See "Timers and Calendars". |
CALENDARS | E | A calendars definition. See "Timers and Calendars". |
NO-LOOP | U | A Boolean value. "true" inhibits looping of rules due to changes made by its consequence. |
LOCK-ON-ACTIVE | L | A Boolean value. "true" inhibits additional activations of all rules with this flag set within the same ruleflow or agenda group. |
AUTO-FOCUS | F | A Boolean value. "true" for a rule within an agenda group causes activations of the rule to automatically give the focus to the group. |
ACTIVATION-GROUP | X | A string identifying an activation (or XOR) group. Only one rule within an activation group will fire, i.e., the first one to fire cancels any existing activations of other rules within the same group. |
AGENDA-GROUP | G | A string identifying an agenda group, which has to be activated by giving it the "focus", which is one way of controlling the flow between groups of rules. |
RULEFLOW-GROUP | R | A string identifying a rule-flow group. |
5.13. The RuleTable Cell
All Rule Tables begin with a cell containing "RuleTable", optionally followed by a string within the same cell. The string is used as the initial part of the name for all rules derived from this Rule Table, with the row number appended for distinction. (This automatic naming can be overridden by using a NAME column.) All other cells defining rules of this Rule Table are below and to the right of this cell.
5.14. Column Types
The next row after the RuleTable cell defines the column type. Each column results in a part of the condition or the consequence, or provides some rule attribute, the rule name or a comment. Each attribute column may be used at most once.
5.15. Column Headers in the Rule Table
Table 5.3. Column Headers in the Rule Table
Keyword | Initial | Value | Usage |
---|---|---|---|
NAME | N | Provides the name for the rule generated from that row. The default is constructed from the text following the RuleTable tag and the row number. | At most one column |
DESCRIPTION | I | A text, resulting in a comment within the generated rule. | At most one column |
CONDITION | C | Code snippet and interpolated values for constructing a constraint within a pattern in a condition. | At least one per rule table |
ACTION | A | Code snippet and interpolated values for constructing an action for the consequence of the rule. | At least one per rule table |
METADATA | @ | Code snippet and interpolated values for constructing a metadata entry for the rule. | Optional, any number of columns |
5.16. Conditional Elements
Given a column headed CONDITION, the cells in successive lines result in a conditional element.
- Text in the first cell below CONDITION develops into a pattern for the rule condition, with the snippet in the next line becoming a constraint. If the cell is merged with one or more neighbours, a single pattern with multiple constraints is formed: all constraints are combined into a parenthesized list and appended to the text in this cell. The cell may be left blank, which means that the code snippet in the next row must result in a valid conditional element on its own.To include a pattern without constraints, you can write the pattern in front of the text for another pattern.The pattern may be written with or without an empty pair of parentheses. A "from" clause may be appended to the pattern.If the pattern ends with "eval", code snippets are supposed to produce boolean expressions for inclusion into a pair of parentheses after "eval".
- Text in the second cell below CONDITION is processed in two steps.
- The code snippet in this cell is modified by interpolating values from cells farther down in the column. If you want to create a constraint consisting of a comparison using "==" with the value from the cells below, the field selector alone is sufficient. Any other comparison operator must be specified as the last item within the snippet, and the value from the cells below is appended. For all other constraint forms, you must mark the position for including the contents of a cell with the symbol
$param
. Multiple insertions are possible by using the symbols$1
,$2
, etc., and a comma-separated list of values in the cells below.A text according to the patternforall(
delimiter){
snippet}
is expanded by repeating the snippet once for each of the values of the comma-separated list of values in each of the cells below, inserting the value in place of the symbol$
and by joining these expansions by the given delimiter. Note that the forall construct may be surrounded by other text. - If the cell in the preceding row is not empty, the completed code snippet is added to the conditional element from that cell. A pair of parentheses is provided automatically, as well as a separating comma if multiple constraints are added to a pattern in a merged cell.If the cell above is empty, the interpolated result is used as is.
- Text in the third cell below CONDITION is for documentation only. It should be used to indicate the column's purpose to a human reader.
- From the fourth row on, non-blank entries provide data for interpolation as described above. A blank cell results in the omission of the conditional element or constraint for this rule.
5.17. Action Statements
Given a column headed ACTION, the cells in successive lines result in an action statement:
- Text in the first cell below ACTION is optional. If present, it is interpreted as an object reference.
- Text in the second cell below ACTION is processed in two steps.
- The code snippet in this cell is modified by interpolating values from cells farther down in the column. For a singular insertion, mark the position for including the contents of a cell with the symbol
$param
. Multiple insertions are possible by using the symbols$1
,$2
, etc., and a comma-separated list of values in the cells below.A method call without interpolation can be achieved by a text without any marker symbols. In this case, use any non-blank entry in a row below to include the statement.The forall construct is available here, too. - If the first cell is not empty, its text, followed by a period, the text in the second cell and a terminating semicolon are stringed together, resulting in a method call which is added as an action statement for the consequence.If the cell above is empty, the interpolated result is used as is.
- Text in the third cell below ACTION is for documentation only. It should be used to indicate the column's purpose to a human reader.
- From the fourth row on, non-blank entries provide data for interpolation as described above. A blank cell results in the omission of the action statement for this rule.
Note
Using
$1
instead of $param
will fail if the replacement text contains a comma.
5.18. Metadata Statements
Given a column headed METADATA, the cells in successive lines result in a metadata annotation for the generated rules:
- Text in the first cell below METADATA is ignored.
- Text in the second cell below METADATA is subject to interpolation, as described above, using values from the cells in the rule rows. The metadata marker character
@
is prefixed automatically, and should not be included in the text for this cell. - Text in the third cell below METADATA is for documentation only. It should be used to indicate the column's purpose to a human reader.
- From the fourth row on, non-blank entries provide data for interpolation as described above. A blank cell results in the omission of the metadata annotation for this rule.
5.19. Interpolating Cell Data Example
- If the template is
Foo(bar == $param)
and the cell is42
, then the result isFoo(bar == 42)
. - If the template is
Foo(bar < $1, baz == $2)
and the cell contains42,43
, the result will beFoo(bar < 42, baz ==43)
. - The template
forall(&&){bar != $}
with a cell containing42,43
results inbar != 42 && bar != 43
.
5.20. Tips for Working Within Cells
- Multiple package names within the same cell must be comma-separated.
- Pairs of type and variable names must be comma-separated.
- Functions must be written as they appear in a DRL file. This should appear in the same column as the "RuleSet" keyword. It can be above, between or below all the rule rows.
- You can use Import, Variables, Functions and Queries repeatedly instead of packing several definitions into a single cell.
- Trailing insertion markers can be omitted.
- You can provide the definition of a binding variable.
- Anything can be placed in the object type row. Apart from the definition of a binding variable, it could also be an additional pattern that is to be inserted literally.
- The cell below the ACTION header can be left blank. Using this style, anything can be placed in the consequence, not just a single method call. (The same technique is applicable within a CONDITION column.)
5.21. The SpreadsheetCompiler Class
The
SpreadsheetCompiler
class is the main class used with API spreadsheet-based decision tables in the drools-decisiontables module. This class takes spreadsheets in various formats and generates rules in DRL.
The
SpreadsheetCompiler
can be used to generate partial rule files and assemble them into a complete rule package after the fact. This allows the separation of technical and non-technical aspects of the rules if needed.
5.22. Using Spreadsheet-Based Decision Tables
Procedure 5.1. Task
- Generate a sample spreadsheet can to use as the base.
- If the Rule Workbench IDE plug-in is being used, use the wizard to generate a spreadsheet from a template.
- Use an XSL-compatible spreadsheet editor to modify the XSL.
5.23. Lists
In Excel, you can create
lists
of values. These can be stored in other worksheets to provide valid lists of values for cells.
5.24. Revision Control
When changes are being made to rules over time, older versions are archived. Some applications in JBoss Rules provide a limited ability to keep a history of changes, but it is recommended to use an alternative means of revision control.
5.25. Tabular Data Sources
A tabular data source can be used as a source of rule data. It can populate a template to generate many rules. This can allow both for more flexible spreadsheets, but also rules in existing databases for instance (at the cost of developing the template up front to generate the rules).
5.26. Rule Template Capabilities
With Rule Templates the data is separated from the rule and there are no restrictions on which part of the rule is data-driven. So while you can do everything you could do in decision tables you can also do the following:
- store your data in a database (or any other format)
- conditionally generate rules based on the values in the data
- use data for any part of your rules (e.g. condition operator, class name, property name)
- run different templates over the same data
5.27. Rule Template Example
This is what a rule template looks like:
1 template header 2 age 3 type 4 log 5 6 package org.drools.examples.templates; 7 8 global java.util.List list; 9 10 template "employees" 11 12 rule "Current employee_@{row.rowNumber}" 13 when 14 Name(location == @{location}) 15 role(type == "@{type}") 16 then 17 list.add("@{log}"); 18 end 19 20 end template
This is what the above example comprises:
- Line 1: All rule templates start with
template header
. - Lines 2-4: Following the header is the list of columns in the order they appear in the data. In this case we are calling the first column
location
, the secondtype
and the thirdlog
. - Line 5: An empty line signifies the end of the column definitions.
- Lines 6-9: Standard rule header text. This is standard rule DRL and will appear at the top of the generated DRL. Put the package statement and any imports and global and function definitions into this section.
- Line 10: The keyword
template
signals the start of a rule template. There can be more than one template in a template file, but each template should have a unique name. - Lines 11-18: The rule template.
- Line 20: The keywords
end template
signify the end of the template.
This example template would generate the following rule:
package org.drools.examples.templates; global java.util.List list; rule "Current employee_1" when Person(location == Melbourne) role(type == "receptionist") then list.add("melbourne admin"); end rule "Current employee_2" when Person(location == Sydney) Cheese(type == "recruiter") then list.add("sydney HR"); end
5.28. Executing Rule Templates
Procedure 5.2. Task
- Run this code to execute your rule template:
DecisionTableConfiguration dtableconfiguration = KnowledgeBuilderFactory.newDecisionTableConfiguration(); dtableconfiguration.setInputType( DecisionTableInputType.XLS ); KnowledgeBuilder kbuilder = KnowledgeBuilderFactory.newKnowledgeBuilder(); kbuilder.add( ResourceFactory.newClassPathResource( getSpreadsheetName(), getClass() ), ResourceType.DTABLE, dtableconfiguration );
5.29. Extended Changeset Example
The example below is expanded to load the rules from a http URL location, and an Excel decision table from the classpath:
<change-set xmlns='http://drools.org/drools-5.0/change-set' xmlns:xs='http://www.w3.org/2001/XMLSchema-instance' xs:schemaLocation='http://drools.org/drools-5.0/change-set.xsd http://anonsvn.jboss.org/repos/labs/labs/jbossrules/trunk/drools-api/src/main/resources/change-set-1.0.0.xsd' > <add> <resource source='http:org/domain/myrules.drl' type='DRL' /> <resource source='classpath:data/IntegrationExampleTest.xls' type="DTABLE"> <decisiontable-conf input-type="XLS" worksheet-name="Tables_2" /> </resource> </add> </change-set>
5.30. Changesets and Directories Example
You can specify a directory to put content into. It is expected that all the files are of the specified type, since type is not yet inferred from the file name extensions:
<change-set xmlns='http://drools.org/drools-5.0/change-set' xmlns:xs='http://www.w3.org/2001/XMLSchema-instance' xs:schemaLocation='http://drools.org/drools-5.0/change-set.xsd http://anonsvn.jboss.org/repos/labs/labs/jbossrules/trunk/drools-api/src/main/resources/change-set-1.0.0.xsd' > <add> <resource source='file://myfolder/' type='DRL' /> </add> </change-set>
5.31. Knowledge Agent
The Knowledge Agent provides automatic loading, caching and re-loading of resources and is configured from a Knowledge Base properties files. The Knowledge Agent can update or rebuild a Knowledge Base as the resources it uses are changed. The strategy for this is determined by the configuration given to the factory, but it is typically pull-based using regular polling.
5.32. Knowledge Agent Example
This is what the Knowledge Agent looks like:
KnowledgeAgent kagent = KnowledgeAgentFactory.newKnowledgeAgent( "MyAgent" ); kagent.applyChangeSet( ResourceFactory.newUrlResource( url ) ); KnowledgeBase kbase = kagent.getKnowledgeBase();
5.33. KnowledgeAgent Objects
A
KnowledgeAgent
object will continuously scan all resources using a default polling interval of 60 seconds. When a modification date is updated, it will applied the changes into the cached Knowledge Base using the new resources. The previous KnowledgeBase
reference will still exist and you'll have to call getKnowledgeBase()
to access the newly built KnowledgeBase
. If a directory is specified as part of the change set, the entire contents of that directory will be scanned for changes. The way modifications are applied depends on drools.agent.newInstance
property present in the KnowledgeAgentConfiguration object passed to the agent.
5.34. Starting Polling Services
Procedure 5.3. Task
- For polling to occur, the polling and notifier services must be started. Use this code:
ResourceFactory.getResourceChangeNotifierService().start(); ResourceFactory.getResourceChangeScannerService().start();
5.35. Custom ClassLoaders for KnowledgeBuilder
Procedure 5.4. Task
- Open a KnowledgeBuilderConfiguration and specify a custom classloader.
- If you need to pass custom configuration to these compilers, sends a KnowledgeBuilderConfiguration object to KnowledgeAgentFactory.newKnowledgeAgent().
5.36. Reusing the KnowledgeBase ClassLoader
Most of the time, the classloader used in the compilation process of remote resources is the same needed in the agent's kbase so the rules could be executed.
If you want to use this approach, you will need to setup the desired ClassLoader to the agent kbase and use the
drools.agent.useKBaseClassLoaderForCompiling
property of KnowledgeAgentConfiguration object.
This approach lets you modify agent's kbuilder classloader in runtime by modifying the classloader the agent's kbase uses. This will serve also when not using incremental change set processing. When the kbase is recreated its configuration is reused, so the classloader is maintained.
5.37. KnowledgeAgentConfiguration Example
The following is an example of the KnowledgeAgentConfiguration property:
KnowledgeBaseConfiguration kbaseConfig = KnowledgeBaseFactory.newKnowledgeBaseConfiguration(null, customClassLoader); KnowledgeBase kbase = KnowledgeBaseFactory.newKnowledgeBase(kbaseConfig); //kbase with custom classloader KnowledgeAgentConfiguration aconf = KnowledgeAgentFactory.newKnowledgeAgentConfiguration(); aconf.setProperty("drools.agent.newInstance", "false"); //incremental change set processing enabled aconf.setProperty("drools.agent.useKBaseClassLoaderForCompiling", "true"); KnowledgeAgent kagent = KnowledgeAgentFactory.newKnowledgeAgent( "test agent", kbase, aconf);
5.38. The newInstance Property
The newInstance property assists in processing change sets.
A Knowledge Agent can process change sets in two different ways: recreating the knowledge base every time a new change set is processed or applying the change set in the cached knowledge base without destroying it. This behavior is controlled by the KnowledgeAgentConfiguration object's newInstance property when it is passed to the Agent's constructor.
5.39. Using the newInstance Property
When newInstance is set to true (the default value), the agent will destroy the cached Knowledge Base it contains and populate a new one containing the change set modifications. When newInstance is set to false, change sets are applied directly to the cached Knowledge Base. The rules that were not modified in the change sets' resources are not replaced in the Knowledge Base, the modified or deleted rules are modified or deleted from the cached Knowledge Base. Functions, Queries and Definition Types are always replaced in the cached Knowledge Base whether they are modified or not.
5.40. newInstance Example
The following code snippet creates a new Knowledge Agent with its newInstance property set to false:
KnowledgeAgentConfiguration aconf = KnowledgeAgentFactory.newKnowledgeAgentConfiguration(); aconf.setProperty("drools.agent.newInstance", "false"); KnowledgeAgent kagent = KnowledgeAgentFactory.newKnowledgeAgent("test agent", null, aconf);
5.41. Remote HTTP Resource Caching
The knowledge agent is able to remotely "pull" resources from a http(s) URL.
5.42. Restoring Resource Caching After a Restart
Procedure 5.5. Task
- To survive a restart when a resource is no longer available remotely (for example, the remote server is being restarted), set a System Property:
drools.resource.urlcache
. (Make sure it is set to a directory that has write permissions for the application.) The Knowledge Agent will cache copies of the remote resources in that directory.For example, using the java command line-Ddrools.resource.urlcache=/users/someone/KnowledgeCache
- will keep local copies of the resources (rules, packages etc) in that directory, for the agent to use should it be restarted. (When a remote resource becomes available, and is updated, it will automatically update the local cache copy.)