Table of Contents
Artificial Intelligence (A.I.) is a very broad research area that focuses on "Making computers think like people" and includes disciplines like Neural Networks, Genetic Algorithms, Decision Trees, Frame Systems and Expert Systems. Knowledge representation is the area of A.I. concerned with how knowledge is represented and manipulated. Expert Systems use Knowledge representation to facilitate the codification of knowledge into a knowledge base which can be used for reasoning - i.e. we can process data with this knowledge base to infer conclusions. Expert Systems are also known as Knowledge-based Systems and Knowledge-based Expert System and are considered 'applied artificial intelligence'; the process of developing with an Expert System is Knowledge Engineering. EMYCIN was one of the first "shells" for an Expert System, which was created from the MYCIN medical diagnosis Expert System. Where early Expert Systems had their logic hard coded "shells" separated the logic from the system, providing an easy to use environment for user input. Drools is a Rule Engine that uses the Rule Based approached to implement an Expert System and is more correctly classified as a Production Rule System.
The term "Production Rule" originates from formal grammer - where it is described as "an abstract structure that describes a formal language precisely, i.e., a set of rules that mathematically delineates a (usually infinite) set of finite-length strings over a (usually finite) alphabet" (wikipedia).
Business Rule Management Systems build value on top of an Rule Engine providing systems for rule management, deployment, collaboration, analysis and end user tools for business users. Further to this the "Business Rules Approach" is a fast evolving and popular methodology helping to formalise the role of Rule Engines in the enterprise.
The term Rule Engine is quite ambiguous in that it can be any system that uses rules, in any form, that can be applied to data to produce outcomes; which includes simple systems like form validation and dynamic expression engines: "How to Build a Business Rules Engine (2004)" by Malcolm Chisholm exemplifies this ambiguity. The book is actually about how to build and alter a database schema to hold validation rules which it then shows how to generate VB code from those validation rules to validate data entry - while a very valid and useful topic for some, it caused quite a suprise to this author, unaware at the time in the subtleties of Rules Engines differences, who was hoping to find some hidden secrets to help improve the Drools engine. jBPM uses expressions and delegates in its Decision nodes; which controls the transitions in a Workflow. At each node it evaluates a rule that dicates the transition to undertake - this is also a Rule Engine. While a Production Rule System is a kind of Rule Engine and also Expert System, the validation and expression evaluation Rule Engines mention previously are not Expert Systems.
A Production Rule System is turing complete with a focus on knowledge representation to expression propositional and first order logic in a concise, non ambigious and declarative manner. The brain of a Production Rules System is an Inference Engine that is able to scale to a large number of rules and facts. The Inference Engine matches facts, the data, against Production Rules, also called Productions or just Rules, to infer conclusions which result in actions. A Production Rule is a two-part structure using First Order Logic for knowledge representation.
when
<conditions>
then
<actions>The process of matching the new or existing facts against Production Rules is called Pattern Matching, which is performed by the Inference Engine. There are a number of algorithms used for Pattern Matching by Inference Engines including:
Linear
Rete
Treat
Leaps
Drools has implementations for both Rete and Leaps; Leaps is considered experimental, as it is quite new. The Drools Rete implementation is called ReteOO signifying that Drools has an enhanced and optimised implementation of the Rete algorithm for Object Oriented systems. Other Rete based engines also have marketing terms for their proprietary enhancements to Rete, like RetePlus and Rete III. It is important to understand that names like Rete III are purely marketing where, unlike the original published Rete Algorithm, no details of implementation are published; thus asking a question like "Does Drools implement Rete III?" is nonsensical. The most common enhancements are covered in "Production Matching for Large Learning Systems (Rete/UL)" (1995) by Robert B. Doorenbos.
The Rules are stored in the the Production Memory and the facts that the Inference Engine matches against the Working Memory. Facts are asserted into the Working Memory where they may then be modiied or retracted. A system with a large number of rules and facts may result in many rules being true for the same fact assertion, these rules are said to be in conflict. The Agenda manages the execution order of these conflicuting rules using a Conflict Resolution stategy.
A Production Rule System's Inference Engine is stateful and able to enforce truthfulness - called Truth Maintence. A logical relationship can be declared by actions which means the action's state depends on the inference remaining true; when it is no longer true the logical dependant action is undone. The "Honest Politician" is an example of Truth Maintenance, which always ensures that hope can only exist for a decomcracy while we have honest politicians.
when
an honest Politician exists
then
logically assert Hope
when
Hope exists
then
print "Hurrah!!! Democracy Lives"
when
Hope does not exist
then
print "Democracy is Doomed"
There are two methods of execution for a Production Rule Systems - Forward Chaining and Backward Chaining; systems that implement both are called Hybrid Production Rule Systems. Understanding these two modes of operation are key to understanding why a Production Rule System is different and how to get the best from them. Forward chaing is 'data-driven' and thus reactionary - facts are asserted into the working memory which results in one or more rules being concurrently true and scheduled for execution by the Agenda - we start with a fact, it propagates and we end in a conclusion. Drools is a forward chaining engine.
Backward chaining is 'goal-driven', we start with a conclusion which the engine tries to satisfy. If it can't it searches for conclusions, 'sub goals', that help satisfy an unknown part for the current goal - it continues this process untill either the initial conclusion is proven or there are no more sub goals. Prolog is an example of a Backward Chaining engine; Drools will adding support for Backward Chaining in its next major release.
Some questions often asked are
We will attempt to adderess these below.
Declarative Programming
Rule englines allow you to say "What to do" not "How to do it".
They key advantage of this point is that it can make it easy to express solutions to hard problems, and consequently have those solutions verified (rules are much easier to read then code).
Rule systems are capable of solving very very hard problems, yet providing a solution that is able to explain why a "decision" was made (not so easy with other types of AI systems like neural networks, or say, my brain - I have no idea why I scratched the side of the car).
Logic and Data Separation
Your data is in your domain objects, the logic is in the rules. This is fundamentally breaking the OO coupling of data and logic (this can be an advantage as well as a disadvantage depending on your point of view). The upshot is that the logic can be much easier to maintain as there are changes in the future, as the logic is all layed out in rules.
Speed and Scalability
The Rete algorithm, Leaps algorithm, and its descendents such as Drools' Reteoo (and Leaps), provide very efficient ways of matching rule patterns to your domain object data. These are especially efficient when you have datasets that do not change entirely (as the rule engine can remember past matches). These algorithms are battle proven.
Centralisation of Knowledge
By using rules, you are creating a repository of knowlegde (a knowledebase) which is executable. This means its a single point of truth, for business policy (for instance) - ideally rules are so readable, they also serve as the documentation.
Tool Integration
Tools such as eclipse (and in future, Web based UIs) provide ways to edit and manage rules and get immediate feedback, validation and content assistance. Auditing and debugging tools are also available.
Explanation facility
Rule systems effectively provide an "explanation facility" by being able to log the "decisions" made by the rule engine (and why the decisions were made).
Understandable rules (readable by domain experts)
By creating object models (and optionally Domain Specific Languages) that model your problem domain, rules can look very close to natural language. They lend themselves to logic that is understandable to domain experts who may be non technical (as all the program plumbing is in the usual code, hidden away).
The shortest answer to this is "when there is no satisfactory traditional programming approach to solve the problem.". Given that short answer, some more explanation is required. The reason why there is no "traditional" approach is possibly one of the following:
The problem is just too fiddly for traditional code.
The problem may not be complex, but you can't see a non fragile way of building it.
The problem is beyond any obvious algorithm based solution.
It is a complex problem to solve, there are no obvious traditional solutions or basically the problem isn't fully understood.
The logic changes often
The logic itself may be simple (but doesn't have to be) but the rules change just too often. In many organisations, software releases are few and far between, and rules can help provide the "agility" that is needed, and expected these days (in a reasonably safe way).
Domain experts (or business analysts) are readily available, but are non technical.
Non technical domain experts are often a wealth of knowledge about business rules. They typically are non technical, but can be very logical. Rules can allow them to express the logic in their own terms. Of course, they still have to think critically and be capable of logical thinking (many people in "soft" non technical positions are not, so be careful, as by codifying business knowledge in rules, you will often be exposing flaws in the way the business rules are at present).
Of course if rules are a new technology in your project teams experience, the overhead in getting going must be factored in. Its not a trivial technology, but we try to make it easier.
Typically in a modern OO application you would use a rule engine to contain key parts of your business logic (what that means of course depends on the application) - ESPECIALLY the REALLY MESSY parts !. This is an inversion of the OO concept of encapsulating all the logic inside your objects. This is not to say that you throw out OO practices, on the contrary in any real world application, business logic is just one part of the application. If you ever notice lots of "if" "else" "switch" and other messy logic in your code that just doesn't feel right (and you keep coming back to fix it - either because you got it wrong, or the logic/your understanding changes) - think about using rules. If you are faced with tough problems of which there are no algorithms or patterns for, consider rules.
Rules could be used embedded in your application, or perhaps as a service. Often rules work best as "stateful" component - hence they are often an integral part of an application. However, there have been successful cases of creating reusable rul eservices which are stateless.
In your organisation, it is important to think about the process you may want to/have to use for updating rules in systems that are in production (the options are many, but different organisations have different requirements - often they are out of the control of the application vendors/project teams).
To quote a Drools mailing list regular (Dave Hamu): "It seems to me that in the excitement of working with rules engines, that people forget that a rules engine is only one piece of a complex application or solution. Rules engines are not really intended to handle workflow or process executions nor are workflow engines or process management tools designed to do rules. Use the right tool for the job. Sure, a pair of pliers can be used as a hammering tool in a pinch, but that's not what it's designed for."
As rule engines are dynamic (dynamic in the sense that the rules can be stored and managed and updates as data), they are often looked at as a solution to the problem of deploying software (most IT departments seem to exist for the purpose of preventing software being rolled out). If this is the reason you wish to use a rule engine, be aware that rule engines work best when you are able to write declarative rules. As an alternative, you can consider data-driven designs (lookup tables), or script/process engines where the scripts are managed in a database, and able to be updated on the fly.
Hopefully the preceeding sections have explained when you may want to use a rule engine.
Alternatives are script-based engines that provide the dynamicness for "changes on the fly" (there are many solutions here).
Alternatively Process engines (also capable of workflow) such as jBPM allow you to graphically (or programmatically) describe steps in a process - those steps can also involve decision point which are in themselves a simple rule. Process engines and rules often can work nicely together, so its not an either-or proposition.
One key point to note with rule engines, is that some rule-engines are really scripting engines. The downside of scripting engines is that you are tightly coupling your application to the scripts (if they are rules, you are effectively calling rules directly) and this may cause more difficulty in future maintenance, as they tend to grow in complexity over time. The upside of scripting engines is they can be easier to implement at first, and you can get quick results (and conceptually simpler for imperative programmers !).
Many people have also implemented data-driven systems successfully in the past (where there are control tables that store meta-data that changes your applications behaviour) - these can work well when the control can remain very limited. However, they can quickly grow out of control if extended to much (such that only the original creators can change the applications behaviour), or, they cause the application to stagnate as they are too inflexible.
No doubt you have heard terms like "tight coupling" and "loose coupling" in systems design. Generally people assert that loose or "weak" coupling is preferable in design terms, due to the added flexibility. Similarly with rules, you can have "strongly coupled" and "weakly coupled" rules. Strongly coupled in this sense means that one rule "firing" will clearly result in another rule firing etc. in other words there is a clear (probably obvious) chain of logic. If your rules are all strongly coupled, the chances are that the rules will have future inflexibility, and more significantly, that perhaps a rule engine is overkill (as the logic is a clear chain of rules - and can be hard coded). This is not to say that strong or weak coupling is inherently bad, but its a point to keep in mind when considering a rule engine, and also in how you capture the rules. "Loosely" coupled rules should result in a system that allows rules to be changed, removed, added without requiring changes to other rules that are unrelated.
A Production Rule, or Rule, in Drools is a two part structure with a Left Hand Side (LHS) and a Right Hand Side (RHS) additionally a Rule may have the following attributes:
salience
agenda-group
auto-focus
activation-group
no-loop
duration
rule “<name>”
<attribute> <value>
when
<LHS>
then
<RHS>
end
The LHS of a Rule consists of Conditional Elements (CE) and Columns; to facilate the encoding of propositional and first order logic. The term Column is used to indicate Field Constraints on a Fact.
Drools currently supports the follows CEs:
'and'
'or',
'not',
'exists'
'forall' and 'accumulate' will be added shortley. The following Field Constraints are allowed:
Literal Constraint
Bound Variable Constraint
Return Value
Predicate
The Language Guide chapter provides more indepth information for each of these.
As facts are asserted and modified in the Working Memory it matches the facts against the LHS conditions, when all the conditions are met and true the Rule plus those match facts are activated. When a Rule is activated it is placed onto the Agenda for potential execution, where the actions of RHS, called t he Consequence, are executed. The LHS and the RHS is analogous to:
if ( <LHS> ) {
<RHS>
}However 'if' is considered procedural in that it is one part of a possible execution flow - if this.... else if.... else ..... Rules use 'when' to more semantically recognise that the rule will activate when, and only when, its LHS is matched.
Rules are associated with a namespace via the
package keyword; other Rule Engines may call this a
Rule Set. A Package declares imports, global variables,
functions and rules.
package com.sample
import java.util.List
import com.sample.Cheese
global List cheeses
function void exampleFunction(Cheese cheese) {
System.out.println( cheese );
}
rule “A Cheesey Rule”
when
cheese : Cheese( type == "stilton" )
then
exampleFunction( cheese );
cheeses.add( cheese );
endThe following example shows a LHS with a single Column that has a single Literal Field Constraint used with a Cheese Fact:
rule "Cheddar Cheese"
when
Cheese( type == "cheddar" )
then
System.out.println( "cheddar" );
endThe example above is analogous to :
public void cheddarCheese(Cheese cheese) {
if ( cheese.getType().equals("cheddar") {
System.out.println( "cheddar" );
}
}Rule are a complete de-coupling of data from the logic. Rules cannot be called directly as they are not methods or functions instead Rules fire in response to changes in Working Memory's data. Rules are also fully declarative in that they describe "what" not "how" like imperative languages such as java.
Rules are written using First Order Logic, or predicate logic, which extends Propositional Logic. Emil Leon Post was the first to develop an inference based system using symbols to express logic - as a consequence of this he was able to prove that any logical system (including mathematics) could be expressed with such a system.
A proposition is a statement that can be classified as true or false. If the truth can be determined from statement alone it is said to be a "closed statement". In programming terms this is an expression that does not reference any variables:
10 = = 2 * 5
Expressions that evaluate against one or more variables, facts, are "open statements", in that we cannot determine whether the statement is true until we have a variable instance to evaluate against:
Person.sex == "male"
With SQL if we look at the conclusion's action as simply returning the matched fact to the user:
select * from People where People.sex == "male"
For any rows, which represent our facts, that are returned we have inferred that those facts are male people. The following diagram shows how the above SQL statement and People table can be represented in terms of an Inference Engine.
So in java we can say that a simple proposition is of the form 'variable' 'operator' 'value' - where we often refer to 'value' as being a literal value - a proposition can be thought as a field constraint. Further to this propositions can be combined with conjuntive and disjuntive connectives, which is the logic theorists way of saying '&&' and '||'. The following shows two open propositional statements connected together with a single disjunctive connective.
person.getEyeColor().equals("blue") || person.getEyeColor().equals("green") This can be expressed using a disjunctive Conditional Element connective - which actually results in the generation of two rules, to represent the two possible logic outcomes.
Person( eyeColour == "blue" ) || Person( eyeColor == "green" )
Disjunctive field constraints connectives could also be used - although they are not currently supported in Drools 3.0 - and would not result in multiple rule generation.
Person( eyeColour == "blue"||"green" )
However Propositional Logic is not Turing complete in that you are limited to the problems you can define because it cannot express criteria for the structure of data. First Order Logic (FOL), or Predicate Logic, extends Propositional Logic with two new quantifier concepts to allow expressions defining structure - specifically universal and existential quantifiers. Universal quantifiers allow you to check that something is true for everything; normally supported by the 'forall' conditional element, but not yet implemented in Drools 3.0. Existential quantifiers check for the existence of something, in that it occurs at least once - this is supported with 'not' and 'exists' conditional elements.
Imagine we have two classes - Student and Module. Module represents each of the courses the Student attended for that semester, referenced by the List collection. At the end of the semester each Module has a score. If the Student has a Module score below 40 then they will fail that semester - the existential quantifier can be used used with the "less than 40" open proposition to check for the existence of a Module that is true for the specified criteria.
public class Student {
private String name;
private List modules;
...
}public Class Module {
private String name;
private String studentName;
private int score;Java is Turing complete in that you can write code, among other things, to iterate data structures to check for existence. The following should return a List of students who have failed the semester.
List failedStudents = new ArrayList();
for ( Iterator studentIter = students.iterator(); studentIter.hasNext() {
Student student = ( Student ) studentIter.next();
for ( Iterator it = student.getModules.iterator(); it.hasNext(); ) {
Module module = ( Module ) it.next();
if ( module.getScore() < 40 ) {
failedStudents.add( student ) ;
break;
}
}
}Early SQL implementations where not Turing complete as they did not provide quantifiers to asses the structure of data. However modern SQL engines allow nesting of SQL which can be combined with keywords like 'exists' and 'in': The following query would return a set of Students who have failed the semester.
select
*
from
Students s
where exists (
select
*
from
Modules m
where
m.student_name = s.name and
m.score < 40
)rule
when
exists( $student : Student() && Module( student == $student, score < 40 ) )The RETE algorithm was invented by Dr. Charles Forgy and documented in his PHd thesis in 1978-79. A simplified version of the paper was published in 1982 (http://citeseer.ist.psu.edu/context/505087/0).The word RETE is latin for "net" meaning network. RETE algorithm can be broken into 2 parts: rule compilation and runtime execution.
The compilation algorithm describes how the Rules in the Production Memory to generate an efficient descrimination network. In non-technical terms, a descrimination network is used to filter data. The idea is to filter data as it propogates through the network. At the top, the nodes would have many matches. As we go down the network, there would be fewer matches. At the very bottom of the network are the terminal nodes. In Dr Forgy's 1982 paper, he described 4 basic nodes: root, 1-input, 2-input and terminal.
The root node is where all objects enter the network. From there, it
immediately goes to the ObjectTypeNode. The purpose of the ObjectTypeNode is
to make sure the engine doesn't do more work than it needs to. For example,
say we have 2 objects: Account and Order. If the rule engine tried to
evaluate every single node against every object, it would waste a lot of
cycles. To make things efficient, the engine should only pass the object to
the nodes that match the object type. The easiest way to do this is to
create an ObjectTypeNode and have all 1-input and 2-input nodes descend from
it. This way, if an application asserts a new account, it won't propogate to
the nodes for the Order object. In Drools when an object is asserted it
retrieves the List of valid ObjectTypesNodes via a lookup in a HashMap from
the object's Class; if this list does'nt exist it scans all the ObjectTypde
nodes finding valid matches which it caches in the List. This enables Drools
to match against any Class type that matches with an
instanceof of check.
ObjectTypdeNodes can propagate to AlphaNodes, LeftInputAdapterNodes and BetaNodes. AlphaNodes are used to evaluate literal conditions. Although the 1982 paper only covers equality conditions, many RETE implementations support other operations. For example, Account.name == "Mr Trout" is a literal condition. When a rule has multiple literal conditions for a single object type, they are linked together. This means that if an application asserts an account object, it must first satisfy the first literal condition before it can proceed to the next AlphaNode. In Dr. Forgy's paper, he refers to these as IntraElement conditions. The following shows the AlphaNode combinations for Cheese( name == "cheddar, strength == "strong" ):
Drools extends Rete by optimising the propagation from ObjectTypdeNode to AlphaNode using hashing. Each time an AlphaNode is added to an ObjectTypdeNode it adds the literal value as a key to the HashMap with the AlphaNode as the value. When a new instance enters the ObjectTypde node, rather than propagating to each AlphaNode, it can instead retrieve the correct AlphaNode from the HashMap - avoiding unecessary literal checks.
There are two two-input nodes; JoinNode and NotNode - both are referred as BetaNodes. BetaNodes are use to compare 2 objects, and their fields, to each other. The objects may be the same or different types. By convention we refer to the two inputs as left and right. The left input for a BetaNode is generally a list of objects, in Drools this is a Tuple. The right input is a single object. Two Nots can be used to implement 'exists' checks. BetaNodes also have memory. The left input is called the Beta Memory and remembers all incoming tuples. The right input is called the Alpha Memory and remembers all incoming objects. Drools extends Rete by performaning indexing on the BetaNodes. For instance if we know that a BetaNode is peforming a check on a String field, as each object enters we can do a hash lookup on that String value. This means when facts enter from the opposite side, instead of iterating over all the facts to find valid joins we instead do a lookup returning potentially valid candidates. At any point a valid join is found the Tuple is joined with the Object, which is referred to as a partial match, and then propagated to the next node.
To enable the first Object, in the above case Cheese, to enter the network we use a LeftInputNodeAdapter - this takes an Object as an input and propagates a single Object Tuple.
Terminal nodes are used to indicate a single rule has matched all its conditions - at this point we say the rule has a full match. A rule with an 'or' conditional disjunctive connective results in subrule generation for each possible logically branch; thus one rule can have multiple terminal nodes.
Drools also performs node sharing. Many rules repeat the same patterns, node sharing allows us to collapse those patterns so that they don't have to be re-evaluated for every single instance. The following two rules share the first same pattern, but not the last:
rule
when
Cheese( $chedddar : name == "cheddar" )
$person : Person( favouriteCheese == $cheddar )
then
System.out.println( $person.getName() + " likes cheddar" );
endrule
when
Cheese( $chedddar : name == "cheddar" )
$person : Person( favouriteCheese != $cheddar )
then
System.out.println( $person.getName() + " does not like cheddar" );
endAs you can see the compiled Rete network shows the alpha node is shared, but the beta nodes are not, each with their own TerminalNode. Had the second pattern been the same it would have also been shared.
Leaps algorithm for production systems uses a "lazy" approach to condition evaluations. A modified version of this algorithm, implemented as part of Drools v3, attempts to take the best features of both Leaps and Rete approaches in processing facts in the working memory. Leaps is not being described here in detail but only some relevant sections to described how current implementation deviates from "classical" leaps algorithm.
The "classical" Leaps approach puts all incoming (asserted) facts on the main stack according to the order facts were asserted in the working memory (FIFO). It inspects facts one by one trying to find a match for each relevant (based on type of the fact and data types required by CEs) rule by iterating over collections of facts that match datatype of each CE. As soon as such match is found system remembers iteration position to resume such iteration later and fires the rule consequence. After execution of consequence is completed, systems is trying to resume processing by inspecting a fact at the top of the main processing stack and either start processing from beginning or resumes the processing if it was stopped due to finding a match for a rule and consequence firing.
Please note that Leaps allows for rule firing before ALL cross-fact matches were attempted as per RETE approach that explains reported significant performance gains for this algorithm. It's made possible by pushing conflict resolution upfront (sort order of facts on the stack and rules matching order), before matching begins while with RETE all matches should be attempted, activations sorted based on the conflict resolution strategy and head element's consequence fired.
The current implementation allows for flexible conflict resolution strategy selection. Even that it's not exposed as a pluggable feature one can either use supplied conflict resolution strategies (org.drools.leaps.conflict) or develop new ones to modify default behavior. Up to date the general approach was to state conflict resolution stategy without specifying if order activation is based on fact or rules attributes. Current implementation allows for specifying separate ordering based on fact attribute and rule attribute conflict resolution strategy that makes it more apparent.
The main deviation from the "classical" leaps in this implementation lays in the way it deals with "negative" and "exists" CE processing. "classical" approach makes use of "shadow" fact stack, full scan of relevant collections to determine presence of certain facts matching NOT or EXISTS conditions, and conversion of source rules to account for the instances where retracted facts "release" rule activations that were previously blocked by it. Current implementation takes a different approach. Its functionality is similar to the way RETE negative node by creating tuples with deferred activation in "lazy" manner.
After finding match for all "positive" conditions, current implementation starts looking for facts that might satisfy "not" and "exists" conditions. As soon as such fact found the system stops looking for a given CE and stores found fact handle. When all "not" and "exists" CEs are checked the tuple is being evaluated if it eligible for activation - no matching "not" facts and all "exists" CEs have matching facts. Tuple is either activated or if it has blocking conditions ("not" facts or missing "exists") than it's being deferred for further inspection.
All fact assertions are being used to see if it can activate tuples with deferred activation from above. At the same time all fact that are being retracted inspected to see they remove blocking condition from the deferred tuples or deactivate activation triggered by matching given fact in "exists" condition.
Drools is split into two main parts Authoring and Runtime.
The authoring process involves the creation of drl or xml files for rules which are fed into a parser, which is then passed using antlr 3 grammer. The parser checks for correctly formed grammer and produces an intermediate structure for "descr"; where descr indicates the AST "describes" the rules. The AST is then passed to the Package Builder which produces Packages. Package Builder also undertakes any code generation and compilation that is necessary for the creation of the Pacakge. A Packge object is a self contained and deployeable, in that it's serializable, object consisting of one or more rules.
A RuleBase is a runtime component which consists of one or more Package's. Packages can be added and removed from the RuleBase at any time. A Rule Base can instantiate one or more WorkingMemories at any time; a weak reference is maintained, unless it's told otherwise. The Working Memory consists of a number of sub components inculding Working Memory Event Support, Truth Maintenance System, Agenda and Agenda Event Support. Object assertion may result in the creation of one or more Activations, the agenda is resonpsible for scheduling the execution of these Activations.
Three classes are used for authoring DrlParser, XmlParser and PackageBuilder. The two parser classes produce desc AST models from a provided Reader instance. PackageBuilder provides convienience APIs so that you can mostly forget about those classes. The two convienience methods are "addPackageFromDrl" and "addPackageFromXml" - both take an instance of Reader as an argument. The example below shows how to build a package which includes rules from both an xml and drl source which are in the classpath. Note that all added package sources must be of the same package namespace for the current PackageBuilder instance.
Example 1.1. Building a Package from multiple sources
PackageBuilder builder = new PackageBuilder(); builder.addPackageFromDrl( new InputStreamReader( getClass().getResourceAsStream( "package1.drl" ) ) ); builder.addPackageFromXml( new InputStreamReader( getClass().getResourceAsStream( "package2.drl" ) ) ); Package pkg = builder.getPackage();
PackagBuilder is configurable, using PackageBuilderConfiguration. It has default values that can be overriden programmatically via setters or on first use via property settings. Currently it allows alternative compilers (Janino, Eclipse JDT) to be specified, different jdk source levels ("1.4" and "1.5") and a parent class loader. The default compiler is Eclipse JDT Core at source level "1.4" with the parent class loader set to "Thread.currentThread().getContextClassLoader()".
The following show how to specify the JANINO compiler programmatically:
Example 1.2. Configuring the PackageBuilder to use JANINO
PackageBuilderConfiguration conf = new PackageBuilderConfiguration(); conf.setCompiler( PackageBuilderConfiguration.JANINO ); PackageBuilder builder = new PackageBuilder( conf );
This could also be done with a property file setting "drools.compiler=JANINO".
Example 1.3. Configuring the PackageBuilder to build with JDk 1.5 compatability
PackageBuilderConfiguration conf = new PackageBuilderConfiguration(); conf.setJavaLanguageLevel( "1.5" ); PackageBuilder builder = new PackageBuilder( conf );
This could also be done with a property file setting "drools.compiler.languagelevel=1.5".
A Rule Base contains one more more packages of rules, ready to be used (ie they have been validated/compiled etc). A Rule Base is serializable so it can be deployed to JNDI, or other such services. Typically, a rulebase would be generated and cached on first use; to save on the continually re-generation of the Rule Base; which is expensive. A RuleBase is instantiated using the RuleBaseFactory, by default this returns a ReteOO RuleBase. Arguments can be used to specify ReteOO or Leaps. pks are added, in turn, using the addPackage method. You may specify packages of any namespace and multiple packages of the same namespace may be added.
RuleBase ruleBase = RuleBaseFactory.newRuleBase(); ruleBase.addPackage( pkg );
A Rule Base instance is threadsafe, in the sense that you can have the one instance shared accross threads in your application (which may be a web application, for instance). The most common operation on a rulebase is to create a newWorkingMemory.
The Rule Base also holds weak references to any working memories that it has spawned, so if rules are changing (or being added/removed etc) for long running working memories, they can be updated with the latest rules (without necessarily having to restart the working memory). You can specify not to maintain a weak reference, but only do so if you know the Rule Base will not be updated.
ruleBase.newWorkingMemory(); // maintains a weak reference. ruleBase.newWorkingMemory( false ); // do not maintain a weak reference
At any time Packages can be added and removed - all changes will be propated to the existing Working Memories, don't forget to call fireAllRules() for resulting Activations to fire.
ruleBase.addPackage( pkg ); // Add a package instance ruleBase.addPackage( "org.com.sample" ); // remove a package, and all its parts, by it's namespace ruleBase.removeRule( "org.com.sample", "my rule" ); // remove a specific rule from a namespace
While there is a method to remove an indivual rule, there is no method to add an individual rule - to achieve this just add a new packge with a single rule in it.
RuleBaseConfigurator can be used to specify additional behaviour of the RuleBase. RuleBaseConfiguration is set immutable after it has been added to a Rule Base.
RuleBaseConfiguration conf = new RuleBaseConfiguration();
conf.setProperty( RuleBaseConfiguration.PROPERTY_ASSERT_BEHAVIOR,
RuleBaseConfiguration..WM_BEHAVIOR_EQUALITY );
RuleBase ruleBase = new ReteooRuleBase( conf );The two main propertiesto be aware of PROPERT_ASSERT_BEHAVIOR and PROPERTY_LOGICAL_OVERRIDE_BEHAVIOR, which are explain in later sections. All properties and their values are public static field constants on RuleBaseConfiguration.
The Working Memory is the main Class for using the Rule Engine at runtime. It holds references to all data that has been "asserted" into it (until retracted) and it is the place where the interaction with your application occurs. Working memories are stateful objects. They may be shortlived, or longlived. If you are interacting with an engine in a stateless manner, that means you would use the RuleBase object to create a newWorkingMemory for each session, and then discard the working memory when finished (creating a working memory is a cheap operation). An alternative pattern is a working memory that is kept around for a longer time (such as a conversation) - and kept updated with new facts. When you wish to dispose of WorkingMemory it is best pactice to use the dispose() method, so that it removes the reference in the parent Rule Base - however this is a weak reference, so it should eventually me garbage collected anyway. The term Working Memory Action is used to describe assertions, retractions and modifications with the Working Memory.
Facts are objects (beans) from your application that you assert into the working memory. Facts are any java objects which the rules can access. The rule engine does not "clone" facts at all, it is all references/pointers at the end of the day. Facts are your applications data. Strings and other classes without getters and setters are not valid Facts and can't be used with Field Constraints which rely on the JavaBean standard of getters and setters to interact with the object.
"Assertion" is the act of telling the working memory about the facts. WorkingMemory.assertObject(yourObject) for example. When you assert a fact, it is examined for matches against the rules etc. This means ALL of the work is done during assertion; however, no rules are executedl you call "fireAllRules()" after you have finished asserting your facts. This is a common misunderstanding by people who think the work happens when you call "fireAllRules()".
When an Object is asserted it returns a FactHandle. This FactHandle is the token used to represent your asserted Object inside the WorkingMemory, it is also how you will interact with the Working Memory when you wish to retract or modify an object.
Cheese stilton = new Cheese("stilton");
FactHandle stiltonHandle = workingMemory.assertObject( stilton );As mentioned in the Rule Base section a Working Memory may operate in two assertions modes equality and identity - identity is default.
Identity means the Working Memory uses an IdentityHashMap to store all asserted Objects. New instance assertions always result in the return of a new FactHandle, if an instance is asserted twice then it returns the previous fact handle – i.e. it ignores the second assertion for the same fact.
Equality means the Working Memory uses a HashMap to store all asserted Objects. New instance assertions will only return a new FactHandle if a no equal classes have been asserted.
"Retraction" is when you retract a fact from the Working Memory, which means it will no longer track and match that fact, and any rules that are Activated and dependent on that fact will be cancelled. Note that it is possible to have rules that depend on the "non existence" of a fact, in which case retracting a fact may cause a rule to activate (see the 'not' and 'exist' keywords). Retraction is done using the FactHandle that was returned during the assert.
Cheese stilton = new Cheese("stilton");
FactHandle stiltonHandle = workingMemory.assertObject( stilton );
....
workingMemory.retractObject( stiltonHandle );The Rule Engine must be notified of modified Facts, so that it can be re-process. Modification internally is actually a retract and then an assert; so it clears the WorkingMemory and then starts again. Use the modifyObject method to notify the Working Memory of changed objects, for objects that are not able to notify the Working Memory themselves. Notice modifyObject always takes the modified object as a second parameter - this allows you to specify new instances for immutable objects.
Cheese stilton = new Cheese("stilton");
FactHandle stiltonHandle = workingMemory.assertObject( stilton );
....
stilton.setPrice( 100 );
workingMemory.modifyObject( stiltonHandle, stilton );If your fact objects are java beans, you can implement a property change listener for them, and then tell the rule engine about it. This means that the engine will automatically know when a fact has changed, and behave accordingly (you don't need to tell it that it is modified). There are proxy libraries that can help automate this (a future version of drools will bundle some to make it easier). To use the Object in dynamic mode specify true for the second assertObject parameter.
Cheese stilton = new Cheese("stilton");
FactHandle stiltonHandle = workingMemory.assertObject( stilton, true ); //specifies that this is a dynamic factTo make a JavaBean dynamic add a PropertyChangeSupport field memory along with two add/remove mothods and make sure that each setter notifies the PropertyChangeSupport instance of the change.
private final PropertyChangeSupport changes = new PropertyChangeSupport( this );
...
public void addPropertyChangeListener(final PropertyChangeListener l) {
this.changes.addPropertyChangeListener( l );
}
public void removePropertyChangeListener(final PropertyChangeListener l) {
this.changes.removePropertyChangeListener( l );
}
...
public void setState(final String newState) {
String oldState = this.state;
this.state = newState;
this.changes.firePropertyChange( "state",
oldState,
newState );
}Globals are named objects that can be passed in to the rule engine; without needing to assert them. Most often these are used for static information, or services that are used in the RHS of a rule, or perhaps a means to return objects from the rule engine.
new List list = new ArrayList;
workingMemory.setGlobal("list", list);The global definition must be defined in the Rule Base and of the same type, otherwise a runtime exception will be thrown. If a rule evaluates on a global before you set it you will get a NullPointerException.
A shadow fact is a shallow copy of an asserted object. Shadow facts are cached copies of object asserted to the working memory. The term shadow facts is commonly known as a feature of JESS (Java Expert System Shell).
The origins of shadow facts traces back to the concept of truth maintenance. The basic idea is that an expert system should gaurantee the derived conclusions are accurate. A running system may alter a fact during evaluation. When this occurs, the rule engine must know a modification occurred and handle the change appropriately. There's generally two ways to gaurantee truthfullness. The first is to lock all the facts during the inference process. The second is to make a cache copy of an object and force all modifications to go through the rule engine. This way, the changes are processed in an orderly fashion. Shadow facts are particularly important in multi-threaded environments, where an engine is shared by multiple sessions. Without truth maintenance, a system has a difficult time proving the results are accurate. The primary benefit of shadow facts is it makes development easier. When developers are forced to keep track of fact modifications, it can lead to errors, which are difficult to debug. Building a moderately complex system using a rule engine is hard enough without adding the burden of tracking changes to facts and when they should notify the rule engine.
As of Drools 3.0, shadow facts hasn't been implemented, but it is planned for the future. However users can implement this facility themselves if they really need it by building their own proxy implementations.
Rule engines that are based around algorithms like RETE are technically stateful rule engines. That is, they work best when the RETE network is longer lived, being notified of changes, and accumulating facts - that is where they shine.
However, many scenarious simply require a stateless mode where all the facts (data) are supplied fresh to the rule engine and then the rules are invoked. Drools is fine in these scenarious as well - in fact, the RETE algorithm is still of benefit as often there will be scenarios in a stateless session where rules actions cause other rules to fire, or where there are large numbers of facts to match with rules.
The JSR-94 api specifies statefull and stateless modes, but the equivalent in the native API is to simply create a new working memory instance, and then discard it when the session is finished.
The Agenda is a RETE feature. During a Working Memory Action rules may become fully matched and legible for execution; a single Working Memory Action can result in multiple legible rules. When a rule is fully matched an Activation is created, referencing the Rule and the matched facts, and placed onto the Agenda. The Agenda controls the execution order of these Activations using a Conflict Resolution strategy.
The engine operates in a "2 phase" mode which is recursive:
Working Memory Actions - this is where most of the work takes place - in either the Consequence or the main java application process. Once the Consequence has finished or the main java application process calls fireAllRules() the engine switcesh to Agenda Evaluation phase.
Agenda Evaluation - attempts to select a rule to fire, if a rule is not found it exits otherwise it attempts to fire the rule switching the phase back to Working Memory Actions and the process begins again until the Agenda is empty.
The process recurses until the agenda is clear, in which case control returns to the calling application. When Working Memory Actions are taking place, no rules are being fired.
Conflict resolution is required when there are mutliple rules on the agenda. As firing a rule may have side effects on working memory, the rule engine needs to know in what order the rules should fire (for instance, firing ruleA may cause ruleB to be removed from the agenda).
The conflict resolution strategies emplyed by Drools are: Salience and LIFO (last in, first out).
The most visible one is "salience" or priority, in which case a user can specify that a certain rule has a higher priority (by giving it a higher number) then other rules. In that case, the higher salience rule will always be preferred. LIFO priorities based on the assigned Working Memory Action counter value, multiple rules created from the same action have the same value - execution of these are considered arbitrary.
As a general rule, it is a good idea not to count on the rules firing in any particular order, and try and author the rules without worrying about a "flow".
Agenda groups are a way to partition rules (activations, actually) on the agenda. At any one time, only one group has "focus" which means that the activations for rules in that group will only take effect - you can also have rules "auto focus" which means the focus for its agenda group is taken when that rules conditions are true.
They are sometimes known as "modules" in CLIPS terminology. Agenda groups are a handy way to create a "flow" between grouped rules. You can switch the group which has focus either from within the rule engine, or from the API. If you rules have a clear need for multiple "phases" or "sequences" of processing, consider using agenda-groups for this purpose.
Each time setFocus(...) is called it pushes that Agenda Group onto a stack, when the focus group is empty it is popped off and the next one of the stack evaluates. An Agenda Group can appear in multiple locations on the stack. The default Agenda Group is "MAIN", all rules which do not specify an Agenda Group are placed there, it is also always the first group on the Stack and given focus as default.
An example may make things clearer. Imagine a credit card processing application, processing transactions for a given account (and we have a working memory accumulating knowledge about a single accounts transaction). The rule engine is doing its best to decide if transactions are possibly fraudulent or not. Imagine this rule base basically has rules that kick in when there is "reason to be suspicious" and when "everything is normal".
Of course there are many rules that operate no matter what (performing standard calculations etc). Now there are possibly many reasons as to what could trigger a "reason to be suspicious": someone notifying the bank, a sequence of large transactions, transactions for geographically disparate transactions, or even reports of credit card theft. Rather then smattering all the little conditions in lots of rules, imagine there is a fact class called "SuspiciousAccount".
Then there can be a series of rules whose job is to look for things that may raise suspicion, and if they fire, they simply assert a new SuspiciousAccount() instance. All the other rules just have conditions like "not SuspiciousAccount()" or "SuspiciousAccount()" depending on their needs. Note that this has the advantage of allowing there to be many rules around raising suspicion, without touching the other rules. When the facts causing the SuspiciousAccount() assertion are removed, the rule engine reverts back to the normal "mode" of operation (and for instance, a rule with "not SuspiciousAccount()" may kick in which flushes through any interrupted transactions).
If you have followed this far, you will note that truth maintenance like logical assertions allows rules to behave a little like a human would, and can certainly make the rules more managable.
Filters are optional implementations of a the filter interface which are used to allow/or deny an activation from firing (what you filter on, is entirely up to the implementation). Drools provides the following convienience default implementations
RuleNameEndWithAgendaFilter
RuleNameEqualsAgendaFilter
RuleNameStartsWithAgendaFilter
To use a filter specify it while calling FireAllRules. The following example will filter out all rules ending with the text "Test":
workingMemory.fireAllRules( new RuleNameEndsWithAgendaFilter( "Test" ) );
In a regular assertion, you need to explicitly retract a fact. With logical assertions, the fact that was asserted will be automatically retracted when the conditions that asserted it in the first place are no longer true (its actually cleverer then this, if there are no possible conditions that could support the logical assertion, only then will it be retracted).
Normal assertions are said to be “STATED” (ie The Fact has been stated - just like the intuitive concept). Using a HashMap and a counter we track how many times a particuarly equality is STATED; this means we count how many different instances are equal. When we logical assert an object we are said to justify it and it is justified by the firing rule.For each logical assertion there can only be one equal object, each subsequent equal logical assertion increases the justification counter for this logical assretion. As each justification is removed when we have no more justifications the logical object is automatically retracted. If we logically assert an object when there is an equal STATED object it will fail and return null. If we STATE an object that has an exist equal object that is JUSTIFIED we override the Fact - how this override works depends on the configuration setting "WM_BEHAVIOR_PRESERVE". When the property is set to discard we use the existing handle and replace the existing instance with the new Object - this is the default behaviour - otherwise we overrde it to STATED but we create an new FactHandle.
This can sound confusing on a first read, so hopefully the flow charts below help. When it says that it returns a new FactHandle, this also indecates the Object was propagated through the network.
The event package provides means to be notified of rule engine events, including rules firing, objects being asserted etc. This allows you to seperate out logging/auditing activities from the main part of your application (and the rules) - as events are a cross cutting concern.
There are two types of event listeners - WorkingMemoryEventListener and AgendEventListener.
Both EventListeners have default implementations that implement each method, but do nothing, these are convienience classes that you can inherit from to save having to implement each method - DefaultAgendaEventListener and DefaultWorkingMemoryEventListener. The following showd how to extend DefaultAgendaEventListener and add it to the Working Memory - the example prints statements for only when rules are fired:
workingMemory.addEventListener( new DefaultAgendaEventListener() {
public void afterActivationFired(AfterActivationFiredEvent event) {
super.afterActivationFired( event );
System.out.println( event );
}
});Drools also provides DebugWorkingMemoryEventListener and DebugAgendaEventListener that implements each method with a debug print statement:
workingMemory.addEventListener( new DebugWorkingMemoryEventListener() );
The Eclipse based Rule IDE also provides an audit logger, and graphical viewer, so that the rule engine can log events for later viewing, and auditing.
Drools provides an eclipse based IDE (which is optional), but at its core only Java 1.4 (J2SE) is required.
A simple way to get started is to download and install the eclipse plug in. This will provide you with all the dependencies you need to get going: you can simply create a new rule project and everything will be done for you. Refer to the chapter on the Rule Workbench and IDE for detailed instructions on this. Installing the eclipse plugin is generally as simple as unzipping a file into your eclipse plugin directory.
Use of the eclipse plug in is not required. Rule files are just textual input (or spreadsheets as the case may be) and the IDE (also known as the Rule Workbench) is just a convenience. People have integrated the rule engine in many ways, there is no "one size fits all".
Alternatively, you can download the binary distribution, and include the relevant jars in your projects classpath.
Drools is broken down into a few modules, some are required during rule development/compiling, and some are required at runtime. In many cases, people will simply want to include all the dependencies at runtime, and this is fine. It allows you to have the most flexibility. However, some may prefer to have their "runtime" stripped down to the bare minimum, as they will be deploying rules in binary form - this is also possible. The core runtime engine can be quite compact, and only require a few 100 kilobytes across 2 jar files.
The following is a description of the important libraries that make up JBoss Rules
drools-core.jar - this is the core engine, runtime component. Contains both the RETE engine and the LEAPS engine. This is the only runtime dependency if you are pre-compiling rules (and deploying via Package or RuleBase objects).
drools-compiler.jar - this contains the compiler/builder components to take rule source, and build executable rule bases. This is often a runtime dependency of your application, but it need not be if you are pre-compiling your rules. This depends on drools-core
drools-jsr94.jar - this is the JSR-94 compliant implementation, this is essentially a layer over the drools-compiler component. Note that due to the nature of the JSR-94 specification, not all features are easily exposed via this interface. In some cases, it will be easier to go direct to the drools API, but in some environments the JSR-94 is mandated.
drools-decisiontables.jar - this is the decision tables 'compiler' component, which uses the drools-compiler component. This supports both excel and CSV input formats.
There are quite a few other dependencies which the above components require, most of which are for the drools-compiler, drools-jsr94 or drools-decisiontables module. Some of these (such as the XML libraries) may not be required if you run in a Java 1.5 environment. Some key ones to note are "JCI" - which is the apache Java Compiler Interface utility which provides runtime compiling capability, "POI" which provides the spreadsheet parsing ability, and "antlr" which provides the parsing for the rule language itself.
NOTE: if you are using Drools in J2EE or servlet containers and you come across classpath issues with "JDT", then you can switch to the janino compiler. Set the system property "drools.compiler": For example: -Ddrools.compiler=JANINO.
The "runtime" requirements mentioned here are if you are deploying rules as their binary form (either as Package objects, or RuleBase objects etc). This is an optional feature that allows you to keep your runtime very light. You may use drools-compiler to produce rule packages "out of process", and then deploy them to a runtime system. This runtime system only requires drools-core.jar for execution. This is an optional deployment pattern, and many people do not need to "trim" their application this much, but it is an ideal option for certain environments.
The rule workbench (for Eclipse) requires that you have eclipse 3.2 or greater. You can install it either by downloading the plugin or, or using the update site.
To install from the zip file, download and unzip the file. Inside the zip you will see a plugin directory, and the plugin jar itself. You place the plugin jar into your eclipse applications plugin directory, and restart eclipse.
Using the update site is a handy way to install the plug in, and keep it up to date (the eclipse platform will check for updates as needed). It gives you a good chance of staying up to date with improvements, fixes etc.
Some firewalls may cause trouble with using update sites in eclipse, if you have issues, then install it manually from the plugin. Also, if you have previously installed the plug in manually, you will need to manually remove it from your plug in directory.
Step 1. Use the eclipse help menu to find the feature installer.

Step 2: Choose the option for installing a new feature (obviously in future, if you want to check for updates, you use the other option !).

Step 3: This screen will show what update sites are already configured for your Eclipse instance.

Step 4: This screen is where you enter in the remote site details. You give it a name eg "JBoss Rules" and the url.
URL: http://anonsvn.labs.jboss.com/labs/jbossrules/updates/drools-ide-update/

Step 5: Select the new update site you just added. Eclipse will remember this for when it checks for updates automatically in the future.

Step 6: You should see the available features (Drools IDE) retrieved from the update site.

Step 7: The licence agreement. Choose the option to accept the licence agreement. Once this happens, the workbench will start downloading. Might be an opportune time to go have a coffee.

Step 8: Confirm that this is the feature you want.

Step 9: Press Accept to accept the fact that the feature is not digitally signed. No one signs their features, its a silly default screen in Eclipse.

Step 10: The workbench will need to restart now for the feature to take effect.

Now go have another coffee, and then take a look at the chapter on the Rule Workbench for what you can do with it.
As Drools is an open source project, instructions for building from source are part of the manual ! Building from source means you can stay on top with the latest features. Whilst aspects of Drools are quite complicated, many users have found ways to become contributors.
Drools works with JDK1.4 and above. you will need also need to have the following tools installed. Minimum requirement version numbers provided.
Ensure the executeables for ant and java are in your path. The examples given illustrative and are for a win32 system:
Path=D:\java\j2sdk1.4.2_10\bin;D:\java\apache-ant-1.6.5\bin;
Following environment variables will also need to be set. The examples given illustrative and are for a win32 system::
JAVA_HOME=D:\java\j2sdk1.4.2_10
ANT_HOME=D:\java\apache-ant-1.6.5
Past releases used to use maven 2 as the build mechanism, but now ant is used as the primary mechanism. Maven is used underneath the covers as the mechanism for managing project dependencies etc. (You can of course still have maven 2 installed and use it if needed - there is a pom.xml structure for each module - this is what is used for dependency management).
Drools is available from two Subversion repositories.
Anonymous SVN
http://anonsvn.labs.jboss.com/labs/jbossrules/trunk/
Developers secured SVN
https://svn.labs.jboss.com/labs/jbossrules/trunk/
Setup TortoiseSVN to checkout from the subversion repository and clikc
'OK' Once the checkout has finished you should see the
folders as shown below.



Now that we have the source the next step is to build and install the source. We do this with Ant, which uses maven 2 under the covers to get the needed dependencies (you don't need to have maven 2 installed to build). Note that the plug in is built seperatly, but the build process will update the plug ins dependencies (refer to the section on building the plug in if you need to build a new version of the plug in).
Type 'ant' to get a listing of what capabilities
the build script has (from the root of the project). You should then see a
listing which contains further instructions:
C:\Projects\jboss-rules-new>ant
Buildfile: build.xml
help:
[echo] Drools Build Help
[echo] -----------------
[echo]
[echo] The build process is for the following four modules:
[echo] drools-core
[echo] drools-compiler
[echo] drools-decisiontables
[echo] drools-jsr94
[echo]
[echo] Further to this there are two Eclipse project:
[echo] drools-examples - To use open Eclipse and import.
[echo] Requires the Drools Eclipse plugin.
[echo] Either build the plugin from drools-ide
[echo] or install from the Eclipse update site
[echo] http://anonsvn.labs.jboss.com/labs/jbossrules/updates
[echo] drools-ide - To use open Eclipse and import.
[echo] Select export and plugin fragements to
[echo] generate the Drools Eclipse plugin
[echo] Depends on all the depencies being copied
[echo] to its lib directory at the end of build-all,
[echo] which calls copy-deps.
[echo]
[echo] Targets
[echo] -----
[echo]
[echo] clean-all - cleans all directories
[echo]
[echo] build-all - builds all modules
[echo]
[echo] javadocs - generates the javadoc at target/docs
[echo]
[echo] manual - generates the docbook documentation in
[echo] html and single_html format at target/docs
[echo]
[echo] dist-all - generates the distribution builds for
[echo] src, bin, bin-withdeps and examples
[echo]
[echo] Each module can individually be executed with clean, compile,
[echo] test and build. To use simply use the following commands,
[echo] replace ${module} with one of the module names:
[echo] clean-${module}
[echo] compile-${module}
[echo] test-${module}
[echo] build-${module}
[echo]
[echo] Generates specific distribution builds:
[echo] dist-src
[echo] dist-bin
[echo] dist-bin-withdeps
[echo] dist-examples
[echo]
[echo] You may also pass the following system properties:
[echo] -Dtest.skip=true
[echo] -Djavadocs.skip=true
[echo] -Dmanual.skip=true
[echo]
[echo] Targets may be combined:
[echo] ant -Dtest.skip clean-all build-all
[echo]
[echo] For more help use the build system to generate the manual
[echo] or you can find the manual online at:
[echo] http://labs.jboss.com/portal/jbossrules/docs/index.html
BUILD SUCCESSFUL
The most common pair of tasks to run is 'ant clean-all build-all' which will clear old artifacts, and then test and built the source, and report on any errors.
The resulting jars are put in the /target directory from the top level of the project.
As Ant builds each module it will install the resulting jars in the
local Maven 2 repository automatically. Where it can be easily used from
other project pom.xml or copied else where.

Drools uses Docbook for this manual. Ant is used to build the documentation. The documentation produces four different formats, all share the same images directory. PDFs will attempt to use SVG images where possible.
html_single
The entire manual in a single html document
The manual is split into multiple documents and placed in a frameset. The left frame provides navigation
PDF (currently broken, due to layout issues with image sizes)
A generated PDF, with nagivation.
eclipse
Documentation suitable for including in an eclipse plugin
The manual can be generated from the project root's build.xml by
calling 'ant manual', with the generated documentation
being copied to 'target/docs'. What actually happens is
a call to a separate ant build.xml for the manual, located at
documentation/manual; the documentation is generated
into documentation/manual/build before being copied to
'target/docs'.
C:\dev\jbossrules>ant manual
Buildfile: build.xml
manual:
[delete] Deleting directory D:\dev\jbossrules\documentation\manual\build
clean:
all.doc:
lang.all:
lang.misc:
[copy] Copying 102 files to C:\dev\jbossrules\documentation\manual\build\en\shared\images
[copy] Copying 1 file to C:\dev\jbossrules\documentation\manual\build\en\shared\css
[style] Transforming into C:\dev\jbossrules\documentation\manual\build\en\shared\images
[style] Processing C:\dev\jbossrules\documentation\manual\en\Chapter-Papers\guests_at_table.svg to C:\dev\jbossrules
\documentation\manual\build\en\shared\images\Chapter-Papers\guests_at_table.svg
[style] Loading stylesheet C:\dev\jbossrules\documentation\manual\en\styles\visio_svg.xsl
[style] Processing C:\dev\jbossrules\documentation\manual\en\Chapter-Papers\make_path.svg to C:\dev\jbossrules\docum
entation\manual\build\en\shared\images\Chapter-Papers\make_path.svg
[style] Processing C:\dev\jbossrules\documentation\manual\en\Chapter-Papers\manners_activity_diagram.svg to C:\dev\j
bossrules\documentation\manual\build\en\shared\images\Chapter-Papers\manners_activity_diagram.svg
[style] Processing C:\dev\jbossrules\documentation\manual\en\Chapter-Performance_Tuning\beta_node.svg to C:\dev\jbos
...
snip
...
lang.dochtml:
[mkdir] Created dir: C:\dev\jbossrules\documentation\manual\build\en\html
[copy] Copying 1 file to C:\dev\jbossrules\documentation\manual\build\en\html
[java] Writing bk01-toc.html for book
[java] Writing pr01.html for preface(preface)
[java] ID recommended on part: Reference Manual
[java] ID recommended on chapter: The Rule Engine
[java] ID recommended on section: What is a Rule Engine
[java] ID recommended on section: Background
[java] ID recommended on figure: Inference Engine
[java] ID recommended on figure: SQL as a simplistic Inference Engine
[java] ID recommended on figure: Additional Reading
[java] ID recommended on section: Rules
[java] ID recommended on figure: A Basic Rete network
[java] ID recommended on figure: A Basic Rete network
...
snip
...
lang.dochtmlsingle:
[mkdir] Created dir: C:\dev\jbossrules\documentation\manual\build\en\html_single
[java] ID recommended on part: Reference Manual
[java] ID recommended on chapter: The Rule Engine
[java] ID recommended on section: What is a Rule Engine
[java] ID recommended on section: Background
[java] ID recommended on figure: Inference Engine
[java] ID recommended on figure: SQL as a simplistic Inference Engine
[java] ID recommended on figure: Additional Reading
[java] ID recommended on section: Rules
[java] ID recommended on figure: A Basic Rete network
[java] ID recommended on figure: A Basic Rete network
[java] ID recommended on section: Why use a Rule Engine
...
snip
...
[java] ID recommended on section: Make Path and Path Done
[java] ID recommended on figure: Rete Diagram
[java] ID recommended on section: Continue and Are We Done
[java] ID recommended on section: Conclusion
[java] ID recommended on section: Output Summary
[java] ID recommended on index
[copy] Copying 142 files to C:\dev\jbossrules\target\docs
BUILD SUCCESSFUL
Total time: 54 seconds
C:\dev\jbossrules>Figure 2.1. Generating the Manual with Ant
The generated manual can be found in the
target\docs directory, a folder per each format.

The manual was first generated into the manual's
build directory, as shown below, before being copied
across.

The drools project has eclipse projects checked in for convenience. However, these are originally generated by maven 2. If you have maven 2 installed, you can also regenerate the eclipse projects automatically, or even generate it for IntelliJ etc, see the instructions below for this (most people can ignore this section)
Maven is able to generate standard Eclipse projects, but it is not able to generate Eclipse
plugin projects. To generate the Eclipse projects for drools-core,
drools-compiler and drools-jsr94 type 'mvn
eclipse:eclipse'.


With the Eclispe project files generated they can now be imported into eclipse. When starting Eclipse open the workspace in the root of your subversion checkout.




When calling 'mvn install' all the project
dependencies were downloaded and added to the local Maven repository.
Eclipse cannot find those depenencies unless you tell it where that
repository is. To do this setup an M2_REPO claspath variable.




The drools-ide project was checked out out using subversion and is ready for exporting.




Once the plugin has been built open the output directory and copy the jar to the Eclipse plugin directory.


At this point if Eclipse is already open it will need to be restarted. At which point you show now see the new Drools menu icon and drl's should have icons and be provided with syntax highlighting and intellisense.


There is also an update site for the plug in. For developers who want to update the update site (ha) you will need to get to the update site project (or create a new one). They are kept in SVN, but in /jbossrules/update instead of /trunk. They are plain vanilla eclipse feature and site projects.
PLEASE REMEMBER that the plug in in the downloads directory, as a zip, should also be updated at the same time as the update site (as they are alternative ways ot getting the same plug in).
Eclipse refreshing plugins in features and sites seems to not work, so what is best is to manually edit the site.xml project and the feature.xml. To do this, open the site.xml file in the drools-ide-update project, it should look something like this:
<?xml version="1.0" encoding="UTF-8"?>
<site>
<!-- change both the jar and the version number, make sure the new features jar is named
the same as what you put in -->
<feature url="features/org.drools.ide_1.0.2.jar" id="org.drools.ide" version="1.0.2">
<category name="JBossRules"/>
</feature>
<category-def name="JBossRules" label="JBoss Rules"/>
</site>
Change the version attribute to be something new, and also the name of the feature jar to have a new version number at the end.
Go into the /feature directory, and unzip the feature jar to get to the feature.xml. (the feature jar really just contains the feature.xml). Open the feature.xml, and it should look like:
<?xml version="1.0" encoding="UTF-8"?> <feature id="org.drools.ide" label="Drools Rule Workbench" version="1.0.2"> <!-- UPDATE THIS !! --> <description> JBoss Rules (Drools) Workbench for developers. </description> <copyright> Copyright 2005 JBoss Inc </copyright> <license> Licensed under the Apache License, Version 2.0(the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. </license> <plugin id="org.drools.ide" download-size="0" install-size="0" version="1.0.0"/> <!-- THIS JUST HAS TO BE CONSISTENT WITH THE PLUG IN --> </feature>
Change the version number in the FEATURE tag to be the same as what you referred to in the site.xml. If you changed the version number of the main plug in, you will need to put the version number in the plug in tag (which refers to org.drools.ide plugin). Then zip up the feature.xml into a jar with the same name as you referred to in the site.xml.
Finally, drop the plugin jar into the /plugins jar directory of the update site (get the actual plug in from the exported plugin in the previous step). Now you can upload the site as is, and it will show up as a new version for Eclipse clients.
Drools 3 has a "native" rule language that is non XML textual format. 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. This chapter is mostly concered with the native rule format. The Diagrams used are known as "rail road" diagrams, and are basically flow charts for the language terms. For the technically very keen, you can also refer to "drl.g" which is the Antlr3 grammar for the rule language. If you use the Rule Workbench, a lot of the rule structure is done for you with content assistance, for example, type "ru" and press ctrl+space, and it will build the rule structure for you.
A rule file is typically a file with a .drl extension. In a drl file you can have multiple rules, functions etc. 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.
A rule has the following rough structure:
rule "name"
ATTRIBUTES
when
LHS
then
RHS
end
Its really that simple. 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 Java semantic code to be executed (this will soon support other semantic languages, like groovy, and C#). The only special keywords here are for asserting, retracting or modifying facts. Any variables bound in the LHS are available here.
It is important to note that whitepace is not important, EXCEPT in thse case of domain specific languages, in which case each line is processed before the following line (and spaces may be significant to the domain language).
Domain specific languages are implemented as an enhancement over the native rule language. They use the "expander" mechanism. The expander mechanism is an extensible API, but by default it can work with .dsl files, which contain mappings from the domain or natural language to the rule language and your domain objects. You can think of these .dsl files also as a mapping to your domain model (which provides you with some insulation). DSLs/expanders work by processing a line in the rule source as it is being compiled - this is the only time that newlines are significant. This is done to aid readability and avoid the need for punctuation. It is expected that over time, alternative expanders and DSLs will be available/prebuilt for various domains, and provide other forms of natural language parsing and analysis. It is up to you if you believe a DSL is of benefit to your application - they certainly make for very nice looking rules, but for some folks, the native rule language is ideal. Freedom is good.
There are some reserved keywords that are used in the rule language. It is wise to avoid collisions with these words when naming your domain objects, properties, methods, functions and so on that are used in the rule text. The following list are words that you should try and avoid in the rule contents if possible (often times it will work fine, but in some cases the rules may be parsed incorrectly). Of course, you can have words as part of a method name in camel case, like notSomething() - there are no issues with that scenario.
when
then
rule
end
contains
matches
and
or
modify
retract
assert
salience
function
query
exists
eval
agenda-group
no-loop
duration
->
not
auto-focus
Comments are sections of text that are ignored by the rule engine. They are effectively stripped out when they are encountered.
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 - perhaps HR rules, for instance. 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). A common structure, however, is to have all the rules for a package in the same file as the package declaration (so that is it entirely self contained).
The following rail road diagram shows all the components that may make up a package. Note that a package MUST have a namespace and be declared using standard java conventions for package names; i.e. no spaces, unlike rule names which allow spaces. In terms of the order of elements, they can appear in any order in the rule file, with the exception of the "package" and "expander" statements being at the top of the file, before any rules appear. In all cases, the semi colons are optional.
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 rule. Drools automatically imports classes from the same named java package.
The expander statement (optional) is used to specify domain specific language configurations (which are normally stored in a seperate file). This provides clues to the parser as to how to understand what you are raving on about in your rules.
Globals are global variables. If multiple packages declare globals of the same identifier they must be of the same type and the all reference the same global value. They are typically used to return data, such as a log of actions, or provide data or services that the rules use. Globals are not asserted into the Working Memory so the engine is not aware when globals are modified; for this reason globals should not be used in constraints unless their values are considered final. Incorrect use of globals in constraints may yield suprising results - surprising in a bad way, like when a doctor says "thats interesting" to a chest XRay of yours.
Note that these are just named instances of objects that you pass in from your application to the working memory. This means you can pass in any object you want: you could pass in a service locator, or perhaps a service itself.
One example may be an instance of a Email service. In your integration code that is calling the rule engine, you get your emailService object, and then set it in the working memory. In the DRL, you declare that you have a global of type EmailService, and give it a name "email". Then in your rule consequences, you can use things like email.sendSMS(number, message); etc... (you get the idea).
Functions are a way to put semantic code in your rule source, as opposed to in normal java classes. They can't do anything more then what you can do with helper classes (in fact, the compiler generates the helper class for you behind the scenes, isn't that helpful). The main advantage of using functions in a rule is that you can keep the logic all in one place, and you can change the functions as needed (this can be a good and bad thing). Functions are most useful for invoking actions on the consequence ("then") part of a rule, especially if that particular action is used over and over (perhaps with only differing parameters for each rule - for example the contents of an email message).
A typical function declaration looks like:
function String calcSomething(String arg) {
return "hola !";
}
Note that the "function" keyword is used, even though its not really part of java. Parameters to the function are just like a normal method (and you don't have to have parameters if they are not needed). Return type is just like a normal method. To call the function from within a rule (in a consequence, or perhaps an eval, simply use the function name, and toss in the parameters - just like a method call).
An alternative to a function, could be to use a static method in a helper class: Foo.doSomething(), or perhaps pass in an instance of a helper class/service as a named global (see the section on globals): foo.soSomething() (where foo is a named global variable).
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.
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.
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.
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.
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.
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.
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.
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.
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".
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.
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).
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).
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.
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 hasing to improve performance.
All standard java numeric primitives are supported
Valid operators:
==
!=
>
<
>=
<=
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:
==
!=
>
<
>=
<=
Any valid Java String is allowed.
Valid operators:
==
!=
only true or false can be used. 0 and 1 are not recognised,
nor is Cheese ( smelly ) is not allowed
Valid operators:
true
false
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
hasing 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 )
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' )
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' )
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?
valid children : and, or, not, exists, column
Example 3.15. Column
Cheese( cheeseType : type ) && Person( favouriteCheese == cheeseType ) Cheese( cheeseType : type ) and Person( favouriteCheese == cheeseType )
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 )
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.
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"
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.20. No red Busses
not Bus(color == "red") not ( Bus(color == "red", number == 42) ) //brackets are optional
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.
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);
endJava 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).
A query contains the structure of the LHS of a rule only (you don't specify "when" or "then"). It is simply a way to query the working memory for facts that match the conditions stated.
To return the results use WorkingMemory.getQueryResults("name") - where "name" is query name. Query names are global to the RuleBase, so do not add queries of the same name to different packages for the same Rule Base. This contains a list of query results, which allow you to to get to the objects that matched the query.
This example creates a simple query for all the people over the age of 30
Example 3.25. Query People over the age of 30
query "people over the age of 30"
person : Person( age > 30 )
endWe iterate over the returned QueryResults using a standard 'for' loop. Each row returns a QueryResult which we can use to access each of the columns in the Tuple. Those columns can be access by bound declaration name or index position.
Example 3.26. Query People over the age of 30
QueryResults results = workingMemory.getQueryResults( "people over the age of 30" );
System.out.println( "we have " + results.size() + " people over the age of 30" );
System.out.println( "These people are are over 30:" );
for ( Iterator it = results.iterator; it.hasNext(); ) {
QueryResult result = ( QueryResult ) it.next();
Person person = ( Person ) result.get( "person" );
System.out.println( person.getName() + "\n" );
}As mentioned previously, (or DSLs) are a way of extending the rule language to your problem domain. They are wired in to the rule language for you, and can make use of all the underlying rule langauge and engine features.
DSLs can serve as a layer of seperation between rule authoring (and rule authors) and the domain objects that the engine operates on. DSLs can also act as "templates" of conditions or actions that are used over and over in your rules, perhaps only with parameters changing each time. If your rules need to be read and validated by less technical folk, (such as Business Analysts) the DSLs are definately for you. If the conditions or consequences of your rules follow similar patterns which you can express in a template. You wish to hide away your implementation details, and focus on the business rule. You want to provide a controlled means of editing rules based on pre-defined templates.
DSLs have no impact on the rules at runtime, they are just a parse/compile time feature.
Note that Drools 3 DSLs are quite different from Drools 2 XML based DSLs. It is still possible to do Drools 2 style XML languages - if you require this, then take a look at the Drools 3 XML rule language, and consider using XSLT to map from your XML language to the Drools 3 XML language.
A DSL's configuration like most things is stored in plain text. If you use the IDE, you get a nice graphical editor (with some validation), but the format of the file is quite simple, and is basically a properties file.
Refering to the above example, the [when] refers to the scope of the expression: ie does it belong on the LHS or the RHS of a rule. The part after the [scope] is the expression that you use in the rule (typically a natural language expression, but it doesn't have to be). The part on the right of the "=" is the mapping into the rule language (of course the form of this depends on if you are talking about the RHS or the LHS - if its the LHS, then its the normal LHS syntax, if its the RHS then its fragments of java code for instance).
The parser will take the expression you specify, and extract the values that match where the {something} (named Tokens) appear in the input. The values that match the tokens are then interpolated with the corresponding {something} (named Tokens) on the right hand side of the mapping (the target expression that the rule engine actually uses).
It is important to note that the DSL expressions are processed one line at a time. This means that in the above example, all the text after "There is " to the end of the line will be included as the value for "{something}" when it is interpolated into the target string. This may not be exactly what you want, as you may want to "chain" together different DSL expressions to generate a target expression. The best way around this is to make sure that the {tokens} are enclosed with characters or words. This means that the parser will scan along the sentence, and pluck out the value BETWEEN the characters (in the example below they are doublequotes). Note that the characters that surround the token are not included in when interpolating, just the contents between them (rather then all the way to the end of the line, as would otherwise be the case).
As a rule of thumb, use quotes for textual data that a rule editor may want to enter. You can also wrap words around the {tokens} to make sure you enclose the data you want to capture (see other example).
Example 3.28. Example with quotes
[when]This is "{something}" and "{another}"=Something(something=="{something}", another=="{another}")
[when]This is {also} valid=Another(something=="{also}")It is a good idea to try and avoid punctuation in your DSL expressions where possible, other then quotes and the like - keep it simple it things will be easier. Using a DSL can make debugging slightly harder when you are first building rules, but it can make the maintenance easier (and of course the readability of the rules).
The "{" and "}" characters should only be used on the left hand side of the mapping (the expression) to mark tokens. On the right hand side you can use "{" and "}" on their own if needed - such as
if (foo) {
doSomething(); }as well as with the token names as shown above.
Don't forget that if you are capturing strings from users, you will also need the quotes on the right hand side of the mapping, just like a normal rule, as the result of the mapping must be a valid expression in the rule language.
Example 3.29. Some more examples
#This is a comment to be ignored.
[when]There is a Person with name of "{name}"=Person(name=="{name}")
[when]Person is at least {age} years old and lives in "{location}"=Person(age > {age}, location=="{location}")
[then]Log "{message}"=System.out.println("{message}");
[when]And = andReferring to the above examples, this would render the following input as shown below:
Example 3.30. Some examples as processed
There is a Person with name of "kitty" ---> Person(name="kitty")
Person is at least 42 years old and lives in "atlanta" ---> Person(age > 42, location="atlanta")
Log "boo" ---> System.out.println("boo");
There is a Person with name of "bob" and Person is at least 30 years old and lives in "atlanta"
---> Person(name="kitty") and Person(age > 30, location="atlanta")A good way to get started if you are new to Rules (and DSLs) is just write the rules as you normally would against your object model. You can unit test as you go (like a good agile citizen!). Once you feel comfortable, you can look at extracting a domain language to express what you are doing in the rules. Note that once you have started using the "expander" keyword, you will get errors if the parser does not recognise expressions you have in there - you need to move everything to the DSL. As a way around this, you can prefix each line with ">" and it will tell the parser to take that line literally, and not try and expand it (this is handy also if you are debugging why something isn't working).
As you work through building up your DSL, you will find that the DSL configuration stabilises pretty quickly, and that as you add new rules and edit rules you are reusing the same DSL expressions over and over. The aim is to make things as fluent as possible.
To use the DSL when you want to compile and run the rules, you will need to pass the DSL configuration source along with the rule source.
PackageBuilder builder = new PackageBuilder(); builder.addPackageFromDrl( source, dsl ); //source is a reader for the rule source, dsl is a reader for the DSL configuration
You will also need to specify the expander by name in the rule source file:
expander your-expander.dsl
Typically you keep the DSL in the same directory as the rule, but this is not required if you are using the above API (you only need to pass a reader). Otherwise everything is just the same.
You can chain DSL expressions together on one line, as long as it is clear to the parser what the {tokens} are (otherwise you risk reading in too much text until the end of the line). The DSL expressions are processed according to the mapping file, top to bottom in order. You can also have the resulting rule expressions span lines - this means that you can do things like:
Of course this assumes that "Or" is mapped to the "or" conditional element (which is a sensible thing to do).
A common requirement when writing rule conditions is to be able to add many constraints to fact declarations. A fact may have many (dozens) of fields, all of which could be used or not used at various times. To come up with every combination as seperate DSL statements would in many cases not be feasable.
The DSL facility allows you to achieve this however, with a simple convention. If your DSL expression starts with a "-", then it will be assumed to be a field constraint, which will be added to the declaration that is above it (one per line).
This is easier to explain with an example. Lets take look at Cheese class, with the following fields: type, price, age, country. We can express some LHS condition in normal DRL like the following
Cheese(age < 5, price == 20, type=="stilton", country=="ch")
If you know ahead of time that you will use all the fields, all the time, it is easy to do a mapping using the above techniques. However, chances are that you will have many fields, and many combinations. If this is the case, you can setup your mappings like so:
[when]There is a Cheese with=Cheese()
[when]-age is less than {age}=age<{age}
[when]-type is '{type}'=type=='{type}'
[when]-country equal to '{country}'=country=='{country}'You can then write rules with conditions like the following:
There is a Cheese with
-age is less than 42
-type is 'stilton'The parser will pick up the "-" lines (they have to be on their own line) and add them as constraints to the declaration above. So in this specific case, using the above mappings, is the equivalent to doing (in DRL):
Cheese(age<42, type=='stilton')
The parser will do all the work for you, meaning you just define mappings for individual constraints, and can combine them how you like (if you are using context assistant, if you press "-" followed by CTRL+space it will conveniently provide you with a filtered list of field constraints to choose from.
DSLs kick in when the rule is parsed. The DSL configuration is read and supplied to the parser, so the parser can "expand" the DSL expressions into the real rule language expressions.
When the parser is processing the rules, it will check if an "expander" representing a DSL is enabled, if it is, it will try to expand the expression based on the context of where it is the rule. If an expression can not be expanded, then an error will be added to the results, and the line number recorded (this insures against typos when editing the rules with a DSL). At present, the DSL expander is fairly space sensitive, but this will be made more tolerant in future releases (including tolerance for a wide range of punctuation).
The expansion itself works by trying to match a line against the expression in the DSL configuration. The values that correspond to the token place holders are stored in a map based on the name of the token, and then interpolated to the target mapping. The values that match the token placeholders are extracted by either searching until the end of the line, or until a character or word after the token place holder is matched. The "{" and "}" are not included in the values that are extracted, they are only used to demarcate the tokens - you should not use these characters in the DSL expression (but you can in the target).
Refer to the ExpanderResolver, Expander and DefaultExpander classes for more indepth information if required. As the parser works off the Expander and ExpanderResolver interfaces, it is possible to plug in your own advanced expanders if required.
DSLs can be aid with capturing rules if the rules are well known, just not in any technically usable format (ie. sitting around in people brains). Until we are able to have those little sockets in our necks like in the Matrix, our means of getting stuff into computers is still the old fashioned way.
Rules engines require a object or data model to operate on - in many cases you may know this up front. In othercases the model will be discovered with the rules. In any case, rules generally work better with simpler flatter object models. In some cases, this may mean having a rule object model which is a subset of the main applications model (perhaps mapped from it). Object models can often have complex relationships and hierachies in them - for rules you will want to simplify and flatten the model where possible, and let the rule engine infer relationships (as it provides future flexibility). As stated previously, DSLs can have an advantage of providing some insulation between the object model and the rule language.
Coming up with a DSL is a collaborative approach for both technical and domain experts. Historically there was a role called "knowledge engineer" which is someone skilled in both the rule technology, and in capturing rules. Overa short period of time, your DSL should stabilise, which means that changes to rules are done entirely using the DSL. A suggested approach if you are starting from scratch is the following workflow:
Capture rules as loose "if then" statements - this is really to get an idea of size and complexity (possibly in a text document).
Look for recurring statements in the rules captured. Also look for the rule objects/fields (and match them up with what may already be known of the object model).
Create a new DSL, and start adding statements from the above steps. Provide the "holes" for data to be edited (as many statements will be similar, with only some data changing).
Use the above DSL, and try to write the rules just like that appear in the "if then" statements from the first and second steps. Iterate this process until patterns appear and things stabilise. At this stage, you are not so worried about the rule language underneath, just the DSL.
At this stage you will need to look at the Objects, and the Fields that are needed for the rules, reconcile this with the datamodel so far.
Map the DSL statements to the rule language, based on the object model. Then repeat the process. Obviously this is best done in small steps, to make sure that things are on the right track.
A challanging concept to express in DSLs is bound variables. A tip for this is to have a DSL expressions that just bind a given variable name to an Object Type (with no conditions - you can add conditions to that bound object type in subsequent statements). You can then use that name elsewhere in the DSL (including in the action part of the rule).
As an option, Drools also supports a "native" rule language as an alternative to DRL. This allows you to capture and manage your rules as XML data. Just like the non-XML DRL format, the XML format is parsed into the internal "AST" representation - as fast as possible (using a SAX parser). There is no external transformation step required. All the features are available with XML that are available to DRL.
There are several scenarios that XML is desirable. However, we recommend that it is not a default choice, as XML is not readily human readable (unless you like headaches) and can create visually bloated rules.
If you do want to edit XML by hand, use a good schema aware editor that provides nice heirarchical views of the XML, ideally visually (commercial tools like XMLSpy, Oxygen etc are good, but cost money, but then so do headache tablets).
Other scenarious where you may want to use the XML format are if you have a tool that generates rules from some input (programmatically generated rules), or perhaps interchange from another rule language, or from another tool that emits XML (using XSLT you can easily transform between XML formats). Note you can always generate normal DRL as well.
Alternatively you may be embedding drools in a product that already uses XML for configuration, so you would like the rules to be in an XML format. You may be creating your own rule language on XML - note that you can always use the AST objects directly to create your own rule language as well (the options are many, due to the open architecture).
A full W3C standards (XMLSchema) compliant XSD is provided that describes the XML language, which will not be repeated here verbatim. A summary of the language follows.
<?xml version="1.0" encoding="UTF-8"?>
<package name="com.sample"
xmlns="http://drools.org/drools-3.0"
xmlns:xs="http://www.w3.org/2001/XMLSchema-instance"
xs:schemaLocation="http://drools.org/drools-3.0 drools-3.0.xsd">
<import name="java.util.HashMap" />
<import name="org.drools.*" />
<global identifier="x" type="com.sample.X" />
<global identifier="yada" type="com.sample.Yada" />
<function return-type="void" name="myFunc">
<parameter identifier="foo" type="Bar" />
<parameter identifier="bada" type="Bing" />
<body>
System.out.println("hello world");
</body>
</function>
<rule name="my rule">
<rule-attribute name="salience" value="10" />
<lhs>
<column object-type="Foo" />
</lhs>
<rhs>
System.out.println( "hello" );
</rhs>
</rule>
</package>
Referring to the above example: Notice the key parts, the declaration for the Drools 3, schema, imports, globals (application-data in drools 2), functions, and the rules. Most of the elements are self explanatory if you have some understanding of the Drools 3 features.
Imports: import the types you wish to use in the rule.
Globals: These are global objects that can be referred to in the rules.
Functions: this is a declaration of functions to be used in the rules. You have to specify return types, a unique name and parameters, in the body goes a snippet of code.
Rule: see below.
Example 3.33. Detail of rule element
<rule name="my rule">
<lhs>
<column object-type="Foo" />
<column identifier="bar" object-type="Bar" />
<column object-type="Foo">
<literal field-name="field1" evaluator="==" value="value1" />
<predicate field-name="field1" identifier="var1" expression="1==1" />
<return-value field-name="field1" evaluator="==" expression="1==1" />
<field-binding field-name="field1" identifier="var1" />
<bound-variable field-name="field1" evaluator="==" identifier="var1" />
</column>
<not>
<column object-type="Bar" />
</not>
<exists>
<column object-type="Bar" />
</exists>
<and>
<or>
<column object-type="Bar" />
</or>
<column object-type="Yada" />
</and>
<or>
<and>
<column object-type="Foo" />
</and>
<column object-type="Zaa" />
</or>
<eval>
1==1
</eval>
</lhs>
<rhs>
/* semantic actions here */
</rhs>
</rule>
Referring to the above rule detail:
The rule has a LHS and RHS (conditions and consequence) sections. The RHS is simple, it is just a block of semantic code that will be executed when the rule is activated. The LHS is slightly more complicated, certainly more so then Drools 2.x.
A key element of the LHS is the Column element. This allows you to specify a type (class) and perhaps bind a variable to an instance of that class. Nested under the column object are constraints that have to be met. The Predicate and Return Value constraints allow java expressions to be embedded.
That leaves the conditional elements, not, exists, and, or etc. They work like their DRL counterparts. Elements that are nested under and an "and" element are logically "anded" together. Likewise with "or" (and you can nest things further). "Exists" and "Not" work around Columns, to check for the existence or non existance of a fact meeting its constraints.
The Eval element allows the execution of a valid snippet of java code - as long as it evaluates to a boolean (do not end it with a semi-colon, as it is just a fragment) - this can include calling a function. The Eval is less efficient then then columns, as the rule engine has to evaluate it each time, but it is a "catch all" feature for when you can express what you need to do with Column constraints.
The Drools 2.x legacy XML format is somewhat different to the Drools 3 format that you can see above.
However, there are some similarities. Basically the imports, globals (which replace application-data in drools 2.x) work the same. Functions are also similar, except that they are specified one at a time (in Drools 2 they where specified in a single block).
Example 3.34. Drools 2.x xml
<rule name="Goodbye Cruel World">
<parameter identifier="goodbye">
<class>String</class>
</parameter>
<java:condition>goodbye.equals("Goodbye")</java:condition>
<java:consequence>
goodbyeWorld( goodbye );
</java:consequence>
</rule>It is possible generally to migrate from drools 2 to drools 3 XML format, if you have existing rulebases you want to port to drools 3. This may be done with a stylesheet, possibly. Note that Drools 2.x DSLs will not be trivial to port to drools 3 XML. You would be best to look at the new DSL features in Drools 3 DRL.
Note that while "eval" allows you to more or less directly migrate your conditions from Drools 2.x to 3, it is far more powerful, and performant, to use constraints on columns where possible, as it utilises the full power of the engine.
Example 3.35. Drools 3 XML equivalent
<rule name="Goodbye Cruel World">
<lhs>
<column identifier='goodbye' object-type='String' >
<eval>goodbye.equals("Goodbye")</eval>
</lhs>
<rhs>
goodbyeWorld( goodbye );
</rhs>
</rule>Drools comes with some utility classes to transform between formats. This works by parsing the rules from the source format into the AST, and then "dumping" out to the appropriate target format. This allows you, for example, to write rules in DRL, and when needed, export to XML if necessary at some point in the future.
The classes to look at if you need to do this are:
XmlDumper - for exporting XML. DrlDumper - for exporting DRL. DrlParser - reading DRL. XmlPackageReader - reading XML.
Using combinations of the above, you can convert between any format (including round trip). Note that DSLs will not be preserved (from DRLs that are using a DSL) - but they will be able to be converted.
Feel free to make use of XSLT to provide all sorts of possibilities for XML, XSLT and its ilk are what make XML powerful.
Decision tables are a "precise yet compact" (ref. Wikipedia) way of representing conditional logic, and are well suited to "business" level rules.
Drools supports managing rules in a Spreadsheet format. Formats supported are Excel, and CSV. Thus you can use a variety of spreadsheet programs (such as Microsoft Excel, OpenOffice.org Calc amongst others). It is expected that web based decision table editors will be included in a near future release.
Decision tables are an old concept (in software terms) but have proven useful over the years. Very briefly speaking, in Drools decision tables are a way to generate rules driven from the data entered into a spreadsheet. All the usual features of a spreadsheet for data capture and manipulation can be taken advantage of.
You may want to consider decision tables if you have rules that can be expressed as rule templates + data. In each row of a decision table, data is collected that is combined with the tempaltes to generate a rule.
Many businesses already use spreadsheets for managing data, calculations etc. If you are happy to continue this way, you can also manage your business rules this way. This also assumes you are happy to manage packages of rules in .xls or .csv files. Decision tables are not recommented for rules that do not follow a set of templates, or where there are a small number of rules (or if you don't want to use software like excel or open office). They are ideal in the sense that you can control what "parameters" of rules can be edited, without exposing the rules directly.
Decision tables also provide a degree of insulation from the underlying object model.
Here are some examples of real world decision tables (slightly edited to protect the innocent).



In the above examples, the technical aspects of the decision table have been collapsed away (standard spreadsheet feature).
The rules start from row 17 (each row results in a rule). The conditions are in column C, D, E etc.. (off screen are the actions). You can see that the value in the cells are quite simple, and have meaning when you look at the headers in Row 16. Column B is just a description. It is conventional to use colour to make it obvious what the different areas of the table mean.
Note that although the decision tables look like they process top down, this is not necessarily the case. It is ideal if you can author rules in such a way as order does not matter (simply as it makes maintenance easier, you are not trying to shift rows around all the time). As each row is a rule, the same principles apply. As the rule engine processes the facts, any rules that match may fire (some people are confused by this - it is possible to clear the agenda when a rule fires and simulate a very simple decision table where the first match exists). Also note that you can have multiple tables on the one spreadsheet (so rules can be grouped where they share common templates - yet at the end of the day they are all combined into a one rule package). Decision tables are essentually a tool to generate DRL rules automatically.

The key point to keep in mind is that in a decision table, each row is a rule, and each column in that row is either a condition or action for that rule.

The spreadsheet looks for the "RuleTable" keyword to indicate the start of a rule table (both the starting row and column). Other keywords are also used to define other package level attributes (covered later). It is important to keep the keywords in the one column. By convention the second column ("B") is used for this, but it can be any column (convention is to leave a margin on the left for notes). In the following diagram, C is actually the column where it starts. Everything to the left of this is ignored.
If we expand the hidden sections, it starts to make more sense how it works. Note the keywords in column C.

Now you can see the hidden magic that makes it work. The RuleSet keyword indicates the name to be used in the "rule package" that all the rules will come under (the name is optional, it will have a default but you MUST have the "RuleSet" keyword) in the cell immediately to the right. The other keywords visible in Column C are: Import, Sequential which will be covered later - just note that in general the keywords make up name/value pairs. The RuleTable keyword is important as it indicates that a chunk of rules will follow, based on some rule templates. After the RuleTable keyword there is a name - this name is used to prefix the generated rules names (the row numbers are appended to create 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 (if you are not using it, you must leave a blank row - but you *will* want to use it !). When you use this row, the values in the cells below (row 16) become constraints on that object type. In the above case, it will generate: Person(age=="42") etc (where 42 comes from row 18). In the above example, the "==" is implicit (if you just put a field name, it will assume that you are looking for exact matches). Also note that you can have a ObjectType declaration span columns (via merged cells) - and that means that all columns below the merged range will be combined into the one set of contraints.
Row 16 contains the rule templates themselves: note that they can use the "$para" place holder to indicate where data from the cells below will be populated (you can use $param, or $1, $2 etc to indicate parameters from a comma seperated list in a cell below). Row 17 is ignored - it is textual descriptions of the rule template.
Row 18 to 19 shows 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 (eg it means that condition, or action, does not apply for that rule-row). Rule rows are read until there is a BLANK row. You can have mutliple RuleTables 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 this case column C has been chosen to be significant, but you can use column A if you like).
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 that the [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".
The syntax of what goes in the templates is dependent on if it is a CONDITION column or ACTION column. In mosts cases, it is identical to "vanilla" DRL for the LHS or RHS respectively. This means in the LHS, the constraint language must be used, and in the RHS it is a snippet of code to be executed.
The "$param" place holder is used in templates to indicate where data form the cell will be interpolated. You can also use "$1" to the same effect. If the cell contains a comma seperated list of values, $1 and $2 etc. may be used to indicate which positional parameter from the list of values in the cell will be used.
For example: If the templates is [Foo(bar == $param)] and the cell is [ 42 ] then the result will be [Foo(bar == 42)] If the template is [Foo(bar < $1, baz == $2)] and the cell is [42,42] then the result will be [Foo(bar > 42, baz ==42)]
For conditions: How snippets are rendered depends on if there is anything in the row above (where ObjectType declarations may appear). If there is, then the snippets are rendered as individual constraints on that ObjectType. If there isn't, then they are just rendered as is (with values substituted). If you put just a plain field (as in the example above) then it will assume you mean equality. If you put another operator at the end of the snippet, then the values will put interpolated at the end of the contraint, otherwise it will look for "$param" as outlined previously.
For consequences: How snippets are rendered also depends on if there is anything in the row immediately above it. If there is nothing there, the output is simple the interpolated snippets. If there is something there (which would typically be a bound variable or a global like in the example above) then it will append it as a method call on that object (refer to the above example).
This may be easiest to understand with some examples below.

The above shows how the Person ObjectType declaration spans 2 columns in the spreadsheet, thus both constraints will appear as Person(age == ... , type == ...). As before, as only the field names are present in the snippet, they imply an equality test.

The above condition example shows how you use interpolation to place the values in the snippet (in this case it would result in Person(age == "42")).

The above condition example show that if you put an operator on the end by itself, the values will be placed after the operator automatically.

You can of course put a binding in before the column (the constraints will be added from the cells below). You can put anything in the ObjectType row (eg it could be a pre condition for the columns in the spreadsheet columns that follow).

This shows how the consequence could be done the by simple interpolation (just leave the cell above blank, the same applies to condition columns) - with this style you can put whatever you want in the consequence (not just one method call).
The following table describes the keywords that are pertinent to the rule table structure.
Table 4.1. Keywords
| Keyword | Description | Is required? |
|---|---|---|
| RuleSet | The cell to the right of this contains the ruleset name | One only (if left out, it will default) |
| Sequential | The cell to the right of this can be true or false. If true, then salience is used to ensure that rules fire from the top down | optional |
| Import | The cell to the right contains a comma seperated list of java classes to import | optional |
| RuleTable | A cell starting with RuleTable indicates the start of a definition of a rule table. The actual rule table starts the next row down. The rule table is read left-to-right, and top-down, until there is one BLANK ROW. | at least one. if there are more, then they are all added to the one ruleset |
| CONDITION | Indicates that this column will be for rule conditions | At least one per rule table |
| ACTION | Indicates that this column will be for rule consequences | At least one per rule table |
| PRIORITY | Indicates that this columns values will set the 'salience' values for the rule row. Over-rides the 'Sequential' flag. | optional |
| DURATION | Indicates that this columns values will set the duration values for the rule row. | optional |
| NAME | Indicates that this columns values will set the name for the rule generated from that row | optional |
| Functions | The cell immediately to the right can contain functions which can be used in the rule snippets. Drools supports functions defined in the DRL, allowing logic to be embedded in the rule, and changed without hard coding, use with care. Same syntax as regular DRL. | optional |
| Variables | The cell immediately to the right can contain global declartions which drools supports. This is a type, followed by a variable name. (if multiple variables are needed, comma seperate them). | optional |
| UNLOOP | Indicates that if there cell values in this column, the no-loop attribute should be set | optional |
| XOR-GROUP | Cell values in this column mean that the rule-row belongs to the given XOR/activation group . An Activation group means that only one rule in the named group will fire (ie the first one to fire cancels the other rules activations). | optional |
| Worksheet | By default, the first worksheet is only looked at for decision tables. | N/A |

The API to use spreadsheet based decision tables is in the drools-decisiontables module. There is really only one class to look at: SpreadsheetCompiler. This class will take spreadsheets in various formats, and generate rules in DRL (which you can then use in the normal way). Also note that if you like you can just use the SpreadsheetComiler to generate partial rule files, and assemble it into a complete rule package after the fact (this allows to you seperate technical and non technical aspects of the rules if needed).
To get started, you can find a sample spreadsheet and base it on that. Alternatively, if you are using the plug in (Rule Workbench IDE) the wizard can generate a spreadsheet for you from a template (to edit it you will need to use an xls compatable spreadsheet editor).

Spreadsheets are well established business tools (in use for over 25 years). Decision tables lend themselves to close collaboration between IT and domain experts, while making the business rules clear to business analysts, it is an ideal seperation of concerns.
Typically, the whole process of authoring rules (coming up with a new decision table) would be something like:
Business analyst takes a template decision table (from a repository, or from IT)
Decision table business language descriptions are entered in the table(s)
Decision table rules (rows) are entered (roughly)
Decision table is handed to a technical resource, who maps the business language (descriptions) to scripts (this may involve software development of course, if it is a new application or data model !)
Technical person hands back and reviews the modifications with the business analyst.
The business analyst can continue editing the rule rows as needed (moving columns around is also fine etc).
In parallel, the technical person can develop test cases for the rules (liasing with business analysts) as these test cases can be used to verify rules and rule changes once the system is running.
You can use the features of applications like Excel to provide assistance in entering data into spreadsheets, such as validating fields. You can use lists that are stored in other worksheets to provide valid lists of values for cells, like in the following diagram.

Some applications provide a limited ability to keep a history of changes, but it is recommended that an alternative means of revision control is also used. So when you are making changes to rules over time, older versions are archived (many solutions exist for this which are also open source, such as Subversion). http://www.drools.org/Business+rules+in+decision+tables+explained
The JBoss Rules workbench is delivered as an eclipse plugin, which allows you to author and manage rules from within Eclipse, as well as integrate rules with your application. This is an optional tool, and not all components are required to be used, you can use what components are relevant to you. Other flavours of the workbench will follow, which will be aimed at less technical rule management (such as allowing business analysts to review and manage rules) - all this is based on the Eclipse platform (hence the term "Workbench").
This guide will cover some of the features of JBoss rules, in as far as the workbench touches on them (it is assumed that the reader has some familiarity with rule engines, and Drools in particular. It is important to note that none of the underlying features of the rule engine are dependent on Eclipse, and integrators are free to use their tools of choice, as always !
Note you can get the plug in either as a zip to download, or from an update site (refer to the chapter on installation).
The rules workbench has the following features
Textual/graphical rule editor
An editor that is aware of DRL syntax, and provides content assistance (including an outline view)
Wizards to accellerate and ...
Help you quickly create a new "rules" project
Create a new rule resource
Create a new Domain Specific language
A domain specific lanaguage editor
Create and manage mappings from your users language to the rule language
Rule validation
As rules are entered, the rule is "built" in the background and errors reported via the problem "view" where possible
You can see the above features make use of Eclipse infrastructure and features. All of the power of eclipse is available.
The aim of the new project wizard is to setup an executable scaffold project to start using rules immediately. This will setup a basic structure, classpath and sample rules and test case to get you started.
The newly created project contains an example rule file (Sample.drl) in the src/rules dir and an example java file (DroolsTest.java) that can be used to execute the rules in a Drools engine in the folder src/java, in the com.sample package. All the others jars that are necessary during execution are also added to the classpath in a custom classpath container called Drools Library [3.0]. Rules do not have to be kept in "java" projects at all, this is just a convenience for people who are already using eclipse as their Java IDE.
Important note: The Drools plug in adds a "Drools Builder" capability to your eclipse instance. This means you can enable a builder on any project that will build and validate your rules when resources change. This happens automatically with the Rule Project Wizard, but you can also enable it manually on any project. One downside of this is if you have rule files that have a large number of rules (>500 rules per file) - as it means that the background builder may be doing a lot of work to build the rules on each change. An option here is to turn off the builder, or put the large rules into .rule files, where you can still use the rule editor, but it won't build them in the background - to fully validate the rules you will need to run them in a unit test of course.
You can create a rule simple as an empty text ".drl" file, or use the wizard to do so. The wizard menu can be invoked by Control+N, or choosing it from the toolbar (there will be a menu with the JBoss Rules icon).
The wizard will ask for some basic options for generating a rule resource. These are just hints, you can change your mind later !. In terms of location, typically you would create a top level /rules directory to store your rules if you are creating a rule project, and store it in a sutably named subdirectory. The package name is mandatory, and is similar to a package name in java (ie. its a namespace that groups like rules together).
This result of this wizard is to generate a rule skeleton to work from. As with all wizards, they are candy: you don't have to use them if you don't want !
The rule editor is where rule managers and developers will be spending most of their time. The rule editor follows the pattern of a normal text editor in eclipse, with all the normal features of a text editor. On top of this, the rule editor provides pop up content assistance. You invoke popup content assistance the "normal" way by pressing Control + Space at the same time.
The rule editor works on files that have a .drl (or .rule) extension. Rules are generally grouped together as a "package" of rules (like the old ruleset construct). It will also be possible to have rules in individual files (grouped by being in the same package "namespace" if you like). These DRL files are plain text files.
You can see from the example above that the package is using a domain specific language (note the expander keyword, which tells the rule compiler to look for a dsl file of that name, to resolve the rule language). Even with the domain specific language (DSL) the rules are still stored as plain text as you see on screen, which allows simpler management of rules and versions (comparing versions of rules for instance).
The editor has an outline view that is kept in sync with the structure of the rules (updated on save). This provides a quick way of navigating around rules by name, in a file which may have hundreds of rules. The items are sorted alphabetically by default.
When debugging an application using a Drools engine, three new views can be used to check the state of the Drools engine itself: the Working Memory View, the Agenda View and the Global Data View. To be able to use these views, create breakpoints in your code invoking the working memory. For example, the line where you call workingMemory.fireAllRules() is a good candidate. If the debugger halts at that joinpoint, you should select the working memory variable in the debug variables view. The following rules can then be used to show the details of the selected working memory:
The Working Memory shows all elements in the working memory of the Drools working memory.
The Agenda View shows all elements on the agenda. For each rule on the agenda, the rule name and bound variables are shown.
The Global Data View shows all global data currently defined in the Drools working memory.
The Audit view can be used to show audit logs that contain events that were logged during the execution of a rules engine in a tree view.

The Working Memory shows all elements in the working memory of the Drools engine.
An action is added to the right of the view, to customize what is shown:
The Show Logical Structure toggles showing the logical structure of the elements in the working memory, or all their details. Logical structures allow for example visualizing sets of elements in a more obvious way.

The Agenda View shows all elements on the agenda. For each rule on the agenda, the rule name and bound variables are shown.
An action is added to the right of the view, to customize what is shown:
The Show Logical Structure toggles showing the logical structure of the agenda item, or all their details. Logical structures allow for example visualizing sets of elements in a more obvious way. The logical structure of AgendaItems shows the rule that is represented by the AgendaItem, and the values of all the parameters used in the rule.

The Global Data View shows all global data currently defined in the Drools engine.
An action is added to the right of the view, to customize what is shown:
The Show Logical Structure toggles showing the logical structure of the elements in the working memory, or all their details. Logical structures allow for example visualizing sets of elements in a more obvious way.
![]() |
The audit view can be used to visualize an audit log that can be created when executing the rules engine. To create an audit log, use the following code:
WorkingMemory workingMemory = ruleBase.newWorkingMemory();
// create a new Working Memory Logger, that logs to file.
WorkingMemoryFileLogger logger = new WorkingMemoryFileLogger(workingMemory);
// an event.log file is created in the log dir (which must exist)
// in the working directory
logger.setFileName("log/event");
workingMemory.assertObject( ... );
workingMemory.fireAllRules();
// stop logging
logger.writeToDisk();Open the log by clicking the Open Log action (first action in the Audit View) and select the file. The Audit view now shows all events that where logged during the executing of the rules. There are five types of events (each with a different icon):
Object asserted (green square)
Object modified (yellow square)
Object retracted (red square)
Activation created (arrow to the right)
Activation cancelled (arrow to the left)
Activation executed (blue diamond)
All these events show extra information concerning the event, like the id and toString representation of the object in case of working memory events (assert, modify and retract), the name of the rule and all the variables bound in the activation in case of an activation event (created, cancelled or executed). If an event occurs when executing an activation, it is shown as a child of the activation executed event. For some events, you can retrieve the "cause":
The cause of an object modified or retracted event is the last object event for that object. This is either the object asserted event, or the last object modified event for that object.
The cause of an activation cancelled or executed event is the corresponding activation created event.
When selecting an event, the cause of that event is shown in green in the audit view (if visible of course). You can also right click the action and select the "Show Cause" menu item. This will scroll you to the cause of the selected event.
Domain Specific Languages (dsl) allow you to create a language that allows your rules to look like, rules ! Most often the domain specific language reads like natural language. Typically you would look at how a business analyst would describe the rule, in their own words, and then map this to your object model via rule constructs. A side benefit of this is that it can provide an insulation layer between your domain objects, and the rules themselves (as we know you like to refactor !). A domain specific language will grow as the rules grow, and works best when there are common terms used over an over, with different parameters.
To aid with this, the rule workbench provides an editor for domain specific lanaguages (they are stored in a plain text format, so you can use any editor of your choice - it uses a slightly enhanced version of the "Properties" file format, simply). The editor will be invoked on any files with a .dsl extension (there is also a wizard to create a sample DSL).
The DSL editor provides a table view of Language Expression to Rule Expression mapping. The Language expression is what is used in the rules. This also feeds the content assistance for the rule editor, so that it can suggest Language Expressions from the DSL configuration (the rule editor loads up the DSL configuration when the rule resource is loaded for editing). The Rule language mapping is the "code" for the rules - which the language expression will be compiled to by the rule engine compiler. For form of this Rule language depends if it is for a condition or action part of a rule (it may be a snippet of java, for instance). The "scope" item indicates where the expression is targeted: is it for the "when" part of the rule (LHS)? the "then" part (RHS)? Or anywhere?
By selecting a mapping item (a row in the table) you can see the expression and mapping in the greyed out fields below. Double clicking or pressing the edit button will open the edit dialog. You can remove items, and add new ones (you should generally only remove when you know that expression is no longer in use).
How it works: the "Language expression" is used to parse the rule language, depending on what the "scope" is set to. When it is found in a rule, the values that are market by the curly braces {value} are extracted from the rule source. These values are then interpolated with the "Rule mapping" expression, based on the names between the curly braces. So in the example above, the natural language expression mapps to 2 contraints on a fact of type Person (ie the person object has the age field as less than {age}, and the location value is the string of {value}, where {age} and {value} are pulled out of the original rule source. The Rule mapping may be a java expression (such as if the scope was "then"). If you did not wish to use a language mapping for a particular rule in a drl, prefix the expression with > and the compiler will not try to translate it according to the language definition. Also note that domain specific languages are optional. When the rule is compiled, the .dsl file will also need to be avilable.
The Rete Tree View shows you the current Rete Network for your drl file. Just click on the tab "Rete Tree" below on the DRL Editor. Afterwards you can generate the current Rete Network visualisation. You can push and pull the nodes to arrange your optimal network overview. If you got hundreds of nodes, select some of them with a frame. Then you can pull groups of them. You can zoom in and out, in case not all nodes are shown in the current view. For this press the button "+" oder "-".
There is no export function, which creates a gif or jpeg picture, in the current release. Please use ctrl + alt + print to create a copy of your current eclipse window and cut it off.

The graph is created with the Java Universal Network/Graph Framework (JUNG). The Rete View is an advanced feature which is still in experimental state. It uses Swing inside eclipse. In future it will maybe improved using SWT or GEF.
The Rete view works only in Drools Rule Projects, where the Drools Builder is set in the project´s properties.
If you are using Drools in an other type of project, where you are not having a Drools Rule Project with the appropiate Drools Builder, you can create a little workaround:
Set up a little Drools Rule Project next to it, putting needed libraries into it and the drls you want to inspect with the Rete View. Just click on the right tab below in the DRL Editor, followed by a click on "Generate Rete View".
Depending on the JDK you use, it may be necessary to increase the permanent generation max size. Both SUN and IBM jdk have a permanent generation, whereas BEA JRockit does not.
To increase the permanent generation, start eclipse with -XX:MaxPermSize=###m
Example: c:\eclipse\eclipse.exe -XX:MaxPermSize=128m
Rulesets of 4,000 rules or greater should set the permanent generation to atleast 128Mb.
(note that this may also apply to compiling large numbers of rules in general - as there is generally one or more classes per rule).
As an alternative to the above, you may put rules in a file with the ".rule" extension, and the background builder will not try to compile them with each change, which may provide performance improvements if your IDE becomes sluggish with very large numbers of rules.
Drools provides an implementation of the Java Rule Engine API (known as JSR94), which allows for support of multiple rule engines from a single API. JSR94 does not deal in anyway with the rule language itself. W3C is working on the Rule Interchange Format (RIF) and the OMG has started to work on a standard based on RuleML, recently Haley Systems has also proposed a rule alnguage standard called RML.
It should be remembered that the JSR94 standard represents the "least common denominator" in features across rule engines - this means there is less functionality in the JSR94 api than in the standard Drools api. So by using JSR94 you are restricting yourself in taking advantage of using the full capabilities of the Drools Rule Engine. It is necessary to expose further functionality, like globals and support for drl, dsl and xml via properties maps due to the very basic feature set of JSR94 - this introduces non portable functionality. Further to this, as JSR94 does not provide a rule language, you are only solving a small fraction of the complexity of switching rule engines with very little gain. So while we support JSR94, for those that insist on using it, we strongly recommend you program against the Drools API.
There are two parts to working with JSR94. The first part is the administrative api that deals with building and register RuleExecutionSets, the second part is runtime session execution of those RuleExecutionSets.
The RuleServiceProviderManager manages the registration and retrieval of RuleServiceProviders. The Drools RuleServiceProvider implementation is automatically registered via a static block when the class is loaded using Class.forName; in much the same way as JDBC drivers.
Example 6.1. Automatic RuleServiceProvider Registration
// RuleServiceProviderImpl is registered to "http://drools.org/" via a static initialization block
Class.forName("org.drools.jsr94.rules.RuleServiceProviderImpl");
// Get the rule service provider from the provider manager.
RuleServiceProvider ruleServiceProvider = RuleServiceProviderManager.getRuleServiceProvider("http://drools.org/");
The RuleServiceProvider provides access to the RuleRuntime and
RuleAdministration APIs. The RuleAdministration provides an administration
API for the management of RuleExecutionSets, making it possible to
register a RuleExecutionSet that can then be retrieved via the
RuleRuntime.
First you need to create a RuleExecutionSet before it can be registered; RuleAdministrator provides factory methods to return an empty LocalRuleExecutionSetProvider or RuleExecutionSetProvider. The LocalRuleExecutionSetProvider should be used to load a RuleExecutionSets from local sources that are not serializable, like Streams. The RuleExecutionSetProvider can be used to load RuleExecutionSets from serializable sources, like DOM Elements or Packages. Both the "ruleAdministrator.getLocalRuleExecutionSetProvider( null );" and the "ruleAdministrator.getRuleExecutionSetProvider( null );" take null as a parameter, as the properties map for these methods is not currently used.
Example 6.2. Registering a LocalRuleExecutionSet with the RuleAdministration API
// Get the RuleAdministration
RuleAdministration ruleAdministrator = ruleServiceProvider.getRuleAdministrator();
LocalRuleExecutionSetProvider ruleExecutionSetProvider = ruleAdministrator.getLocalRuleExecutionSetProvider( null );
// Create a Reader for the drl
URL drlUrl = new URL("http://mydomain.org/sources/myrules.drl");
Reader drlReader = new InputStreamReader( drlUrl.openStream() );
// Create the RuleExecutionSet for the drl
RuleExecutionSet ruleExecutionSet = ruleExecutionSetProvider.createRuleExecutionSet( drlReader, null );
"ruleExecutionSetProvider.createRuleExecutionSet( reader, null )" in the above example takes a null parameter for the propties map; however it can actually be used to provide configuration for the incoming source. When null is passed the default is used to load the input as a drl. Allowed keys for a map are "source" and "dsl". "source" takes "drl" or "xml" as its value; set "source" to "drl" to load a drl or to "xml" to load an xml source; xml will ignore any "dsl" key/value settings. The "dsl" key can take a Reader or a String (the contents of the dsl) as a value.
Example 6.3. Specifying a DSL when registering a LocalRuleExecutionSet
// Get the RuleAdministration
RuleAdministration ruleAdministrator = ruleServiceProvider.getRuleAdministrator();
LocalRuleExecutionSetProvider ruleExecutionSetProvider = ruleAdministrator.getLocalRuleExecutionSetProvider( null );
// Create a Reader for the drl
URL drlUrl = new URL("http://mydomain.org/sources/myrules.drl");
Reader drlReader = new InputStreamReader( drlUrl.openStream() );
// Create a Reader for the dsl and a put in the properties map
URL dslUrl = new URL("http://mydomain.org/sources/myrules.dsl");
Reader dslReader = new InputStreamReader( dslUrl.openStream() );
Map properties = new HashMap();
properties.put( "source", "drl" );
properties.put( "dsl", dslReader );
// Create the RuleExecutionSet for the drl and dsl
RuleExecutionSet ruleExecutionSet = ruleExecutionSetProvider.createRuleExecutionSet( reader, properties );
When registering a RuleExecutionSet you must specify the name, to be used for its retrieval. There is also a field to pass properties, this is currently unused so just pass null.
Example 6.4. Register the RuleExecutionSet
// Register the RuleExecutionSet with the RuleAdministrator String uri = ruleExectionSet.getName(); ruleAdministrator.registerRuleExecutionSet(uri, ruleExecutionSet, null);
The Runtime, obtained from the RuleServiceProvider, is used to create stateful and stateless rule engine sessions.
To create a rule session you must use one of the two RuleRuntime public constants - "RuleRuntime.STATEFUL_SESSION_TYPE" and "RuleRuntime.STATELESS_SESSION_TYPE" along with the uri to the RuleExecutionSet you wish to instantiate a RuleSession for. The properties map can be null, or it can be used to specify globals, as shown in the next section. The createRuleSession(....) method returns a RuleSession instance which must then be cast to StatefulRuleSession or StatelessRuleSession.
Example 6.6. Stateful Rule
(StatefulRuleSession) session = ruleRuntime.createRuleSession( uri,
null,
RuleRuntime.STATEFUL_SESSION_TYPE );
session.addObject( new PurchaseOrder( "lots of cheese" ) );
session.executeRules();The StatelessRuleSession has a very simple API; you can only call executeRules(List list) passing a list of objects, and an optional filter, the resulting objects are then returned.
Example 6.7. Stateless
(StatelessRuleSession) session = ruleRuntime.createRuleSession( uri,
null,
RuleRuntime.STATELESS_SESSION_TYPE );
List list = new ArrayList();
list.add( new PurchaseOrder( "even more cheese" ) );
List results = new ArrayList();
results = session.executeRules( list );It is possible to support globals with JSR94, in a none portable manner, by using the propperties map passed to the RuleSession factory method. Globals must be defined in the drl or xml file first, otherwise an Exception will be thrown. the key represents the identifier declared in the drl or xml and the value is the instance you wish to be used in the execution. In the following example the results are collected in an java.util.List which is used as global:
java.util.List globalList = new java.util.ArrayList( );
java.util.Map map = new java.util.HashMap( );
map.put( "list", globalList );
//Open a stateless Session StatelessRuleSession srs = (StatelessRuleSession) runtime.createRuleSession( "SistersRules", map, RuleRuntime.STATELESS_SESSION_TYPE );
...
// Persons added to List
// call executeRules( ) giving a List of Objects as parameter
// There are rules which will put Objects in the List
// fetch the list from the map
List list = (java.util.List) map.get("list");Do not forget to declare the global "list" in your DRL:
package SistersRules; import org.drools.jsr94.rules.Person; global java.util.List list rule FindSisters when $person1 : Person ( $name1:name ) $person2 : Person ( $name2:name ) eval( $person1.hasSister($person2) ) then list.add($person1.getName() + " and " + $person2.getName() +" are sisters"); assert( $person1.getName() + " and " + $person2.getName() +" are sisters"); end
If you need more information on JSR 94, please refer to the following references
Official JCP Specification for Java Rule Engine API (JSR 94)
The Java Rule Engine API documentation
The Logic From The Bottom Line: An Introduction to The Drools Project. By N. Alex Rupp, published on TheServiceSide.com in 2004
Getting Started With the Java Rule Engine API (JSR 94): Toward Rule-Based Applications. By Dr. Qusay H. Mahmoud, published on Sun Developer Network in 2005
Jess and the javax.rules API. By Ernest Friedman-Hill, published on TheServerSide.com in 2003
In any reasonably complex application, there are many things that may effect performance. The usual advice applies of course (ie don't speculate, measure, profile and plan). In terms of the rule engine, it does its best to be as efficient as possibly, without too much thought needed, most people should not need to read this chapter in detail.
Note that for someone who is using a rule engine of the first time, the most noticable "cost" will be the startup of the rule engine (which is actually compiling the rules) - this problem is easily solved - simply cache the RuleBase instances (or the rule packages) and only update rules as needed (there are many ways to achieve this in your application which will not be covered here).
The remainder of this chapter is considerations on tuning the runtime performance of rules (not compiling), which is where performance often really counts.
As explained in the chapter on the Rete Algorithm, BetaNodes are nodes that have two inputs: the left input (for tuples) and the right input (for single objects). Each beta node has two memories, one for each input: the left memory and the right memory.
So, when a single object arrives at the right input of the node, it tries to match every tuple in the left memory according to the constraints defined for the given BetaNode. Those elements that match are propagated down through the network. The symmetrical behavior happens for when a tuple arrives at the left input of the node. See diagram bellow:
When the number of elements in each of the Beta Node Memories starts to grow, the matching process starts to slow down, as each new element that arrives needs to try to match all the elements in the opposite memory for the given constraints. This process becomes a serious limitation for real systems where thousands of facts are asserted into working memory and where the Rete Network has several Beta Nodes.
One way of minimizing the problem is to index each of the BetaNode memories in a way that when a new element arrives, it does not need to iterate over all elements of the opposite memory in order to find its matches.
So, for example, if we have a Rule like the following:
rule "find brothers"
when
p1: Person( $mother : mother )
p2: Person( mother == $mother )
then
// do something
end
If no indexing is used, each new Person object asserted into working memory will try to match each other previously asserted Person object to find those that have the same mother. So, if we have 1000 Person objects already asserted into working memory, and we assert a new one, the new one will try to match each of the 1000 previously asserted objects.
If we index BetaNode memories by the “mother” attribute, though, when a new Person is asserted into memory, it will try to match only the previously asserted objects that have the same mother attribute, in a very efficient way using the previously built index. So, if the new object has only one brother previously asserted into memory, it will match only one object, avoiding the 999 tries that would fail.
Drools implements BetaNode indexing exactly as described above in order to boost performance. The BetaNode indexing is enabled by default and users usually don’t need to worry about it. Although, for specific situations where a user has a limited amount of memory or for some reason does not want to incur in the indexing overhead, indexing can be disabled for each of the memories, by setting the following system properties to false:
org.drools.reteoo.beta.index-left org.drools.reteoo.beta.index-right For example: ..when you launch the application (or in the container as appropriate). -Dorg.drools.reteoo.beta.index-right=false -Dorg.drools.reteoo.beta.index-left=false
A good way to understand what happens when indexing is used is to make an analogy to databases systems. As we all know, indexing is a great mechanism for performance improvements on database queries, but also adds an overhead to other operations like insert, updates and deletes. Also, there is a memory consumption cost involved. A well planned set of indexes is essential for most enterprise applications and the responsible for defining them is usually the DBA. Once indexes are defined, when a query is executed against that database, a query planner component is used by database systems to estimate the best plan to run the query with the best performance, sometimes using the index, sometimes not.
Working memory has the same issues and same thoughts are valid here. Drools implements an automatic indexing strategy to index beta node memories. Just to have some data to understand the consequences of it, lets use Manners 64 benchmark test results on a Pentium IV 3 Ghz HT machine with 1.0 Gb memory. This is not really a detailed benchmark test, but simply some rough numbers in order to make the scenario easier to understand:
Manners 64 without indexes: 135000 millisec to run Manners 64 with BetaNode indexes: 10078 millisec to run on average
It is obvious by the previous run times that indexes overall benefits pays off the overhead to keep them, at least in terms of performance. We are not analyzing limited memory environments here.
Although, every system has its own peculiarities and sometimes it is possible to do some fine tuning on performance. For example, in our Manners 64 example, if we disable the right memory indexing we would have the following result:
Manners 64 with BetaNode indexing only for left memory: 142000 millisec to run on average
The above is even worse than no using any indexing. This happens clearly because for Manners 64, the left indexing overhead is bigger than its benefit. So, if we do the contrary, leaving right indexing enabled and disabling the left indexing, we get the following result:
Manners 64 with BetaNode indexing only for right memory: 8765 millisec to run on average
So, we have the best scenario now. For Manners 64, the best would be to disable left indexing, leaving only right indexing enabled.
Another tip to tune performance when using indexing is always to write your rules in a way that the most restrictive constraints are declared before the less restrictive ones in your rule. For example, if you have a rule with a column like this:
Employee (department == $aDepartment, name == $aName)
Rewriting it as shown bellow will probably give you a better performance, as “name” is probably a more restrictive constraint than “department”:
Employee (name == $aName, department == $aDepartment)
(Unless you work in an organisation where there are more departments then employees, which could well be the case in a Government organisation ;)
Some other improvements are being developed for Drools in this area and will be documented as they become available in future versions.
Some examples are included as "integration tests" in the Drools source code - specifically the drools-compiler module. On top of these, you can download the drools-examples module; which is a self contained Eclipse project. This module is always having more samples added to it by users. The example project in eclipse requires that you have the plugin instilled: load up the drools-examples project (it has an eclipse project already setup). The rules all have example classes that execute the rules. If you want to try the examples in another project (or another IDE) then you will need to setup the dependencies by hand of course.
The examples can be found at http://anonsvn.labs.jboss.com/labs/jbossrules/trunk/drools-examples/ (you can browse online, or use a subversion client and download the project into Eclipse
Future documentation will include walk throughs for each of the examples.
Once you have rules integrated in your application (or ideally before) you will need to plan how to deploy rules along with your application. Typically rules are used to allow changes to application business logic without re-deploying the whole application. This means that the rules must be provided to the application as data, not as part of the application (eg embedded in the classpath).
Drools 3.0 is primarily a Rule Engine, which leaves open many options for rule deployment. The focus of this chapter is on deployment options, and the API. A future version of Drools will have a Rule Server component for managing multiple versions of rules (including locking, and automatic deployment).
As every organisation is subtly different, and different deployment patterns will be needed. Many organisations have (or should have) configuration management processes for changes to production systems. It is best to think of rules as "data" rather then software in that regard. However, as rules can contain a considerable amount of powerful logic, proper procedures should be used in testing and verifying rule changes, and approving changes before exposing them to the world.
In the simplest possible scenario, you would compile and construct a rulebase, and then cache that rulebase. That rulebase can be shared across threads, spawning new working memories to process transactions (working memories are then discarded). This is essentually the stateless mode. To update the rulebase, a new rulebase is loaded, and then swapped out with the cached rulebase (any existing threads that happen to be using the old rulebase will continue to use it until they are finished, in which case it will eventually be garbage collected).
There are many more sophisticated approaches to the above - Drools rule engine is very dynamic, meaning pretty much all the components can be swapped out on the fly (rules, packages) even when there are *existing* working memories in use. For instance rules can be retracted from a rulebase which has many in-use working memories - the RETE network will then be adjusted to remove that rule without having to assert all the facts again. Long running working memories are useful for complex applications where the rule engine builds up knowledge over time to assist with decision making for instance - it is in these cases that the dynamic-ness of the engine can really shine.
One option is to deploy the rules in source form. This leaves the runtime engine (which must include the compiler components) to compile the rules, and build the rule base. A similar approach is to deploy the "PackageDescr" object, which means that the rules are pre-parsed (for syntactic errors) but not compiled into the binary form. Use the PackageBuilder class to achieve this. You can of course use the XML form for the rules if needed.
PackageDescr, PackageBuilder, RuleBaseLoader
This option is the most flexible. In this case, Packages are built from DRL source using PackageBuilder - but it is the binary Package objects that are actually deployed. Packages can be merged together. That means a package containing perhaps a single new rule, or a change to an existing rule, can be built on its own, and then merged in with an existing package in an existing RuleBase. The rulebase can then notify existing working memories that a new rule exists (as the RuleBase keeps "weak" links back to the Working Memory instances that it spawned). The rulebase keeps a list of Packages, and to merge into a package, you will need to know which package you need to merge into (as obviously, only rules from the same package name can be merged together).
Package objects themselves are serializable, hence they can be sent over a network, or bound to JNDI, Session etc.
PackageBuilder, RuleBase, org.drools.rule.Package
Compiled Packages are added to rulebases. RuleBases are serializable, so they can be a binary deployment unit themselves. This can be a useful option for when rulebases are updated as a whole - for short lived working memories. If existing working memories need to have rules changed on the fly, then it is best to deploy Package objects.
RuleBase, RuleBaseLoader
Practically all of the rulebase related objects in Drools are serializable. For a working memory to be serializable, all of your objects must of course be serializable. So it is always possible to deploy remotely, and "bind" rule assets to JNDI as a means of using them in a container environment.
Please note that when using package builder, you may want to check the hasError() flag before continuing deploying your rules (if there are errors, you can get them from the package builder - rather then letting it fail later on when you try to deploy).
In this case, rules are provided to the runtime system in source form. The runtime system contains the drools-compiler component to build the rules. This is the simplest approach.
In this case, rules are build into their binary process outside of the runtime system (for example in a deployment server). The chief advantage of deploying from an outside process is that the runtime system can have minimal dependencies (just one jar). It also means that any errors to do with compiling are well contained and and known before deployment to the running system is attempted.
Use the PackageBuilder class out of process, and then use getPackage() to get the Package object. You can then (for example) serialize the Package object to a file (using standard java serialization). The runtime system, which only needs drools-core, can then load the file using RuleBaseFactory.newRuleBase().addPackage(deserialized package object).
This section contains some suggested deployment scenarios, of course you can use a variety of techologies as alternatives to the ones in the diagram.
In this scenario, rules are pulled from the rule repository into the runtime system. The repository can be as simple as a file system, or a database. The trigger to pull the rules could be a timed task (to check for changes) or a request to the runtime system (perhaps via a JMX interface). This is possibly the more common scenario.

In this scenario, the rule deployment process/repository "pushes" rules into the runtime system (either in source or binary form, as described above). This gives more control as to when the new rules take effect.

A possible deployment pattern for rules are to expose the rules as a web service. There a many ways to achieve this, but possibly the simplest way at present do achieve it is to use an interface-first process: Define the "facts" classes/templates that the rules will use in terms of XML Schema - and then use binding technologies to generate binding objects for the rules to actually operate against. A reverse possibility is to use a XSD/WSDL generator to generate XML bindings for classes that are hand built (which the rules work against). It is expected in a future version there will be an automated tool to expose rules as web services (and possibly use XSDs as facts for the rules to operate on).
In recent years, practices such as Test Driven Development have become increasingly mainstream, as the value and quality that these techniques bring to software development has been realised. In a sense, rules are code (although at a highlevel), and a lot of the same principles apply.
You can provide tests as a means to specify rule behaviour before rules are even written. Further to this, tests are even more important in environments where rules change frequently. Tests can provide a baseline of confidence that the rule changes are consistent with what is specified in the tests. Of course, the rules may change in such a way as the tests are now wrong (or perhaps new tests need to be written to cover the new rule behaviour). As in TDD practices, tests should be run often, and in a rule driven environment, this means that they should be run everytime the rules change (even though the software may be static).
For developers, clearly JUnit (or TestNG) are popular tools for testing code, and these can also apply to rules. Keep in mind that rule changes may happen out of sync with code changes, so you should be prepared to keep these unit tests up to date with rules (may not be possible in all environments). Also, the best idea is to target testing some core features of the rule sets that are not as likely to change over time.
Obviously, for rule tests, other non source code driven frameworks would be preferable to test rules in some environments. The following section outlines a rule testing component add on.
As a seperate add-on, there is a testing framework available that is built on FIT (Framework for Integrated Testing). This allows rule test suites (functional) to be capture in Word documents, or Excel spreadsheets (in fact any tool that can save as HTML). It utilises a tabular layout to capture input data, and make assertions over the rules of a rulesets execution for the given facts. As the tests are stored in documents, the scenarious and requirements can be (optionally) kept in the same documents, providing a single point of truth for rule behaviour.
Also, as the test documents are not code, they can be updated frequently, and kept with the rules, used to validate rule changes etc. As the input format is fairly simple to people familiar with the domain of the rules, it also facilitates "scenario testing" where different scenarious can be tried out with the rules - all external to the application that the rules are used in. These scenarios can then be kept as tests to increase confidence that a rule change is consistent with the users understanding.
This testing framework is built on FIT and JSR-94, and is kept as a seperate project to JBoss Rules. Due to it being built on FIT, it requires a different licence (but is still open source). You can download and read more about this tool from this web page: Fit for rules http://fit-for-rules.sourceforge.net/
The following screen captures show the fit for rules framework in action.

Using Fit for rules, you capture test data, pass it to the rule engine and then verify the results (with documentation woven in with the test). It is expected that in future, the Drools Server tools will provide a similar integrated framework for testing (green means good ! red means a failure - with the expected values placed in the cell). Refer to http://fit.c2.com for more information on the FIT framework itself.

More information and downloads from Here
Miss Manners is throwing a party and being the good host she wants to arrange good seating. Her initial design arranges everyone in male female pairs, but then she worries about people have things to talk about; what is a good host to do? So she decides to note the hobby of each guest so she can then arrange guests in not only male and female pairs but also ensure that a guest has someone to talk about a common hobby, from either their left or right side.
5 benchmarks were established in the 1991 paper "Effects of Database Size on Rule System Performance: Five Case Studies" by Brant, Timothy Grose, Bernie Lofaso, & Daniel P. Miranker.
Manners
Uses a depth-first search approach to determine the seating arrangements of boy/girl and one common hobby for dinner guests
Waltz
line labelling for simple scenes by constraint propagation
WaltzDB
More general version of Walts to be able to adapt to a database of facts
ARP
Route planner for a robotic air vehicle using the A* search algorithm
Weavera
VLSI router for channels and boxes using a black-board technique
Manners has become the de facto rule engine benchmark; however it's behaviour is now well known and many engines optimise for this thus negating its usefulness as a benchmark which is why Waltz is becoming more favourable. These 5 benchmarks are also published at the University of Texas http://www.cs.utexas.edu/ftp/pub/ops5-benchmark-suite/.
After the first Seating arrangement has been assigned a depth-first recursion occurs which repeatedly assigns correct Seating arrangements until the last seat is assigned. Manners uses a Context instance to control execution flow; the activity diagram is partitioned to show the relation of the rule execution to the current Context state.
Before going deeper into the rules lets first take a look at the asserted data and the resulting Seating arrangement. The data is a simple set of 5 guests who should be arranged in male/female pairs with common hobbies.
The Data
Each line of the results list is printed per execution of the “Assign Seat” rule. They key bit to notice is that each line has pid one greater than the last, the significance of this will be explained in t he “Assign Seating” rule description. The 'l' and the 'r' refer to the left and right, 's' is sean and 'n' is the guest name. In my actual implementation I used longer notation, 'leftGuestName', but this is not practicle in a printed article. I found the notation of left and right preferable to the original OPS5 '1' and '2
(guest (name n1) (sex m) (hobby h1) )
(guest (name n2) (sex f) (hobby h1) )
(guest (name n2) (sex f) (hobby h3) )
(guest (name n3) (sex m) (hobby h3) )
(guest (name n4) (sex m) (hobby h1) )
(guest (name n4) (sex f) (hobby h2) )
(guest (name n4) (sex f) (hobby h3) )
(guest (name n5) (sex f) (hobby h2) )
(guest (name n5) (sex f) (hobby h1) )
(last_seat (seat 5) )
The Results
[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5]
[Seating id=2, pid=1, done=false, ls=1, ln=n5, rs=2, rn=n4]
[Seating id=3, pid=2, done=false, ls=2, ln=n4, rs=3, rn=n3]
[Seating id=4, pid=3, done=false, ls=3, rn=n3, rs=4, rn=n2]
[Seating id=5, pid=4, done=false, ls=4, ln=n2, rs=5, rn=n1]
As the introduction states, manners is a depth-first or LIFO example. Depth-first refers to the Conflict Resolution strategy used to solve the problem. OPS5 had two strategies, LEX and MEA, LEX is a chain of several stratagies including Salience, Recency, Complexity:
Every fact and instance is marked internally with a “time tag” toindicate its relative recency with respect to every other fact and instance in the system. The pattern entities associated with each rule activation are sorted in descending order for determining placement. An activation with a more recent pattern entities is placed before activations with less recent pattern entities. To determine the placement order of two activations, compare the sorted time tags of the two activations one by one starting with the largest time tags. The comparison should continue until one activation’s time tag is greater than the other activation’s corresponding time tag. The activation with the greater time tag is placed before the other activation on the agenda. If one activation has more pattern entities than the other activation and the compared time tags are all identical, then the activation with more time tags is placed before the other activation on the agenda. | ||
| --Clips Reference Manual | ||
Clips is able to specify the LEX and MEA strategies as well as others, however the default is a Depth strategy which is implemented from the LEX Recency strategy:
Newly activated rules are placed above all rules of the same salience. For example, given that fact-a activates rule-1 and rule-2 and fact-b activates rule-3 and rule-4, then if fact-a is asserted before fact-b, rule-3 and rule-4 will be above rule-1 and rule-2 on the agenda. However, the position of rule-1 relative to rule-2 and rule-3 relative to rule-4 will be arbitrary. | ||
| --Clips Reference Manual | ||
Jess only has two strategies, Depth and Breadth, which are documented the same as the Clips ones. When Miss Manners was first implemented in Drools 3.0 using the Depth strategy it was not producing the correct result, which meant it ran forever. This paper discusses the behaviour of Miss Manners with two Rule engines, Clips and Jess, and how they make it work and how it was made to work in Drools.
Once the context is changed to START_UP Activations are created for all asserted Guests; because all Activations are created as the result of a single Working Memory action, they all have the same Activation time tag. Using the Depth, or Breadth, Conflict Resolution strategy the Activations would be fired arbitrary. With LEX's Recency the last asserted Guest would have a higher time tag and its Activation would fire. The execution order in this rule has little importance, but has a big impact in the rule "Assign Seat". The Activation fires and asserts the first Seating arrangement, a Path and then sets the Context's state to create Activatiosn for "Assign Seat".
rule assignFirstSeat() {
when {
context : Context( state == Context.START_UP )
guest : Guest()
count : Count()
} then {
String guestName = guest.getName();
drools.assert( new Seating( count.getValue(), 1, true, 1, guestName, 1, guestName) );
drools.assert( new Path( count.getValue(), 1, guestName ) );
count.setCount( count.getValue() + 1 );
context.setPath( Context.ASSIGN_SEATS );
}
}
This rule determines each of the Seating arrangements. The Rule creates cross product solutions for ALL asserted Seating arrangements against ALL the asserted guests; accept against itself or any already assigned Chosen solutions.
rule assignSeat() {
when {
context : Context( state == Context.ASSIGN_SEATS )
Seating( seatingId:id, seatingPid:pid, pathDone == true
seatingRightSeat:rightSeat seatingRightGuestName:rightGuestName )
Guest( name == seatingRightGuestName, rightGuestSex:sex, rightGuestHobby:hobby )
Guest( leftGuestName:name , sex != rightGuestSex, hobby == rightGuestHobby )
count : Count()
not ( Path( id == seatingId, guestName == leftGuestName) )
not ( Chosen( id == seatingId, guestName == leftGuestName, hobby == rightGuestHobby) )
} then {
int newSeat = rightSeat + 1;
drools.assert( new Seating( countValue, id, false, coung.getValue(), rightSeat, rightSeatName, newSeat, leftGuestName);
drools.assert( new Path( countValue, leftGuestName, newSeat );
drools.assert( new Chosen( id, leftGuestName, rightGuestHobby ) );
count.setCount( countValue + 1 );
context.setPath( Context.MAKE_PATH );
}
}
However, as can be seen from the printed results shown earlier, it is essential that only the Seating with the highest pid cross product be chosen – yet how can this be possible if we have Activations for nearly all existing Seating and Guests. For example on the third iteration of "Assing Seat" these are the produced Activations, remember this is from a very small data set and with larger data sets there would be many more possible Activated Seating solutions, with multiple solutions per pid:
=>[ActivationCreated(35): rule=findSeating
[fid:19:33]:[Seating id=3, pid=2, done=true, ls=2, ln=n4, rs=3, rn=n3]
[fid:4:4]:[Guest name=n3, sex=m, hobbies=h3]
[fid:3:3]:[Guest name=n2, sex=f, hobbies=h3]
=>[ActivationCreated(35): rule=findSeating
[fid:15:23]:[Seating id=2, pid=1, done=true, ls=1, ln=n5, rs=2, rn=n4]
[fid:5:5]:[Guest name=n4, sex=m, hobbies=h1]
[fid:2:2]:[Guest name=n2, sex=f, hobbies=h1]
=>[ActivationCreated(35): rule=findSeating
[fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5]
[fid:9:9]:[Guest name=n5, sex=f, hobbies=h1]
[fid:1:1]:[Guest name=n1, sex=m, hobbies=h1]
The creation of all these redundant Activations might seem pointless, but it must be remembered that Manners is not about good rule design; its purposefully designed as a bad rule to full stress test the cross product matching process, which this clearly does. Notice that each Activation has the same time tag of 35, as they were all activated by the change in Context to ASSIGN_SEATS. With OPS5 and LEX it would correctly fire the Activation with the last asserted Seating. With Depth strategy the execution is arbitrary yet Manners does correctly execute for Jess and Clips using the Depth strategy. Clips support, via the public fourms, have said that Manners was
The default conflict resolution strategy for CLIPS, depth, is different than the default conflict resolution strategy used by OPS5. Therefore if you directly translate an OPS5 program to CLIPS, but use the default depth conflict resolution strategy, you're only likely to get the correct behavior by coincidence. The lex and mea conflict resolution strategies are provided in CLIPS to allow you to quickly convert and correctly run an OPS5 program in CLIPS | ||
| --Clips Support Forum | ||
Early Drools 3.0 implementations, implementing Depth, would not work as all the Activations had the same priority and the reliance on arbitrary execution meant that Seatings with lower pids where firing. A LEX style recency strategy was implemented and the benchmark worked correctly. This leaves the question how does Jess and Clips work, even if it is by coincidence. While I haven’t looked at Jess or Clips code Peter Lin has confirmed that Clips uses LinkedLists for the memory, as shown by the next* property from the PartialMatch Clips struct:
struct partialMatch
{
unsigned int betaMemory : 1;
unsigned int busy : 1;
unsigned int activationf : 1;
unsigned int dependentsf : 1;
unsigned int notOriginf : 1;
unsigned int counterf : 1;
unsigned int bcount : 9;
struct partialMatch *next;
struct genericMatch binds[1];
};This suggests the joins remember and attempt joins based on the fact assertion order into the node; thus Activation firing would follow fact assertion order. When Manners is executed in Clips with the Breadth strategy other than executing forever, explained in the next section, it also does not fire the Activation with the highest pid for Seating instances and thus solutions are never found. This indicates that the order the Activations with the same time tag are placed on the Agenda are still executed in LIFO/FIFO order. Later versions of Drools have moved away from HashMaps for BetaNode memory and reliance on LEX style recency and instead use LinkedLists relying on assertion order to control LIFO - in much the same manner as Clips. So in Drools Manners works, but it works by coincidence, this level of behaviour should not be expected by the user. It is still unknown how Jess deals with this situation, Peter Lin believes that Jess may have some simple and undocumented fact recency solution.
Make Path must always fires before Path Done. A Path is asserted for each Seating arrangement up to the last asserted Seating. Notice that Path Done is a subset of Make Path, so how do we ensure that Make Path fires first?
rule makePath() {
when {
context : Context( state == Context.MAKE_PATH )
Seating( seatingId:id, seatingPid:pid, pathDone == false )
Path( id == seatingPid, pathGuestName:guest, pathSeat:seat )
(not Path( id == seatingId, guestName == pathGuestName )
} else {
drools.assert( new Path( seatingId, pathSeat, pathGuestName ) );
}
}
rule pathDone() {
when {
context : Context( state == Context.MAKE_PATH )
seating : Seating( pathDone == false )
} then {
seating.setPathDone( true );
context.setName( Context.CHECK_DONE );
}
}
With OPS5 LEX the recency strategy says that if all compared facts have the same time tag then the activation with more facts wins; which would make sure that "Make Path" always fires before "Path Done". Clips succesfully completes manners, no matter what the defined order of the rules are in the rule file. If a system is not relying on recency and the rule definition order does not impact execution then the Rete build process must attach joins in such a way that propagation ensures that most complex rules activate last - a kind of inbuilt compexity strategy, however this is not documented; thus with LIFO the first created Activation will fire last. When manners is executed in CLIPS with breadth strategy, it may result in an infinite loop if path_done is fired before make_path activations. This is because path_done modifies the context and removes the activations for make_path rule. For proper execution of manners, the activations for make_path have to fire before path_done. This again illustrates that the arbitrary execution for activations placed on the Agenda obey the set LIFO/FIFO strategy.
"Are We Done" only activates when the last seat is assigned, at which point both rules will be activated. For the same reason that "Make Path" always wins over "Path Done" "Are We Done" will take priority over "Continue".
rule areWeDone() {
when {
context : Context( state == Context.CHECK_DONE )
LastSeat( lastSeat: seat )
Seating( rightSeat == lastSeat )
} then {
context.setState(Context.PRINT_RESULTS );
}
}
rule continue() {
Context context;
when {
context : Context( state == Context.CHECK_DONE )
} then {
context.setState( Context.ASSIGN_SEATS );
}
}
What have we learned from this? That Manners was designed to run with OPS5 Recency and that if works with Jess or Clips under Depth then from a user point of view it should be considered purely coincidence - even if the design was by developers intent. As long as manners does complete with the correct output it will always provide a good stress test for the join nodes; however the Agenda Conflict Resolution will only be tested if a fact based Recency type strategy is used. there are known cheats to Miss:
Using arrays for a guests hobbies, instead of asserting each one as a single fact. This massively reduces the cross products.
The altering of the sequence of data can also reducing the amount of matching increase execution speed
Changing NOT CE (conditional element) such that the test algorithm only uses the "first-best-match". Basically, changing the test algorithm to backward chaining. the results are only comparable to other backward chaining rule engines or ports of Manners.
Removing the context so the rule engine matches the guests and seats pre-maturely. A proper port will prevent facts from matching using the context start.
Any change which prevents the rule engine from performing combinatorial pattern matching
If no facts are retracted in the reasoning cycle, as a result of NOT CE, the port is incorrect.
All the above are external optimisations, so easily detected and the results made void. However its also possible to optimise internally; this can be sean from the Leaps algorithm; which Drools has an expiremental implementation of. Manners is brute force stress test of the cross product speed of various joins. Leaps avoid full cross product testing by only attempting the leading join, so where as Rete would create hundreds of Activations leaps only creates one - this gives orders of magnitudes speed increases. Modern Rete based Rule Engines are starting to analyse networks and apply this type of optimisation, whether its a leaps style lazy evaluation or a pre-compilation optimisation to avoid erroneous joins. Drools ReteOO does not currently make these types of optimisations although pre-compilation optimisations will be researched in the next release. These optimisations have made the Miss Manners benchmark redundant and the results form a system that optimises in this manner should be considered void. This view is also put forward by James Own who publishes the 2000-2005 Benchmarks, http://www.kbsc.com/Performance2000-2005.xls:
Due to significant advances in BRMS engines, the 2000 - 2005 Benchmarks will change significantly for 2006 and beyond. For example, Miss Manners and Waltz 50 will be dropped and we will examine all engines (at this time) using only Waltz DB and a yet to be determined sequential benchmark that probably will not use the Rete Algorithm. | ||
| --James Own, 2000-2005 Benchmarks, Knowledge-Based Systems Corporation | ||
Assign First seat
=>[fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5]
=>[fid:14:14]:[Path id=1, seat=1, guest=n5]
==>[ActivationCreated(16): rule=findSeating
[fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5]
[fid:9:9]:[Guest name=n5, sex=f, hobbies=h1]
[fid:1:1]:[Guest name=n1, sex=m, hobbies=h1]
==>[ActivationCreated(16): rule=findSeating
[fid:13:13]:[Seating id=1 , pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5]
[fid:9:9]:[Guest name=n5, sex=f, hobbies=h1]
[fid:5:5]:[Guest name=n4, sex=m, hobbies=h1]*
Assign Seating
=>[fid:15:17] :[Seating id=2 , pid=1 , done=false, ls=1, lg=n5, rs=2, rn=n4]
=>[fid:16:18]:[Path id=2, seat=2, guest=n4]
=>[fid:17:19]:[Chosen id=1, name=n4, hobbies=h1]
=>[ActivationCreated(21): rule=makePath
[fid:15:17] : [Seating id=2, pid=1, done=false, ls=1, ln=n5, rs=2, rn=n4]
[fid:14:14] : [Path id=1, seat=1, guest=n5]*
==>[ActivationCreated(21): rule=pathDone
[Seating id=2, pid=1, done=false, ls=1, ln=n5, rs=2, rn=n4]*
Make Path
=>[fid:18:22:[Path id=2, seat=1, guest=n5]]
Path Done
Continue Process
=>[ActivationCreated(25): rule=findSeating
[fid:15:23]:[Seating id=2, pid=1, done=true, ls=1, ln=n5, rs=2, rn=n4]
[fid:7:7]:[Guest name=n4, sex=f, hobbies=h3]
[fid:4:4] : [Guest name=n3, sex=m, hobbies=h3]*
=>[ActivationCreated(25): rule=findSeating
[fid:15:23]:[Seating id=2, pid=1, done=true, ls=1, ln=n5, rs=2, rn=n4]
[fid:5:5]:[Guest name=n4, sex=m, hobbies=h1]
[fid:2:2]:[Guest name=n2, sex=f, hobbies=h1], [fid:12:20] : [Count value=3]
=>[ActivationCreated(25): rule=findSeating
[fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5]
[fid:9:9]:[Guest name=n5, sex=f, hobbies=h1]
[fid:1:1]:[Guest name=n1, sex=m, hobbies=h1]
Assign Seating
=>[fid:19:26]:[Seating id=3, pid=2, done=false, ls=2, lnn4, rs=3, rn=n3]]
=>[fid:20:27]:[Path id=3, seat=3, guest=n3]]
=>[fid:21:28]:[Chosen id=2, name=n3, hobbies=h3}]
=>[ActivationCreated(30): rule=makePath
[fid:19:26]:[Seating id=3, pid=2, done=false, ls=2, ln=n4, rs=3, rn=n3]
[fid:18:22]:[Path id=2, seat=1, guest=n5]*
=>[ActivationCreated(30): rule=makePath
[fid:19:26]:[Seating id=3, pid=2, done=false, ls=2, ln=n4, rs=3, rn=n3]
[fid:16:18]:[Path id=2, seat=2, guest=n4]*
=>[ActivationCreated(30): rule=done
[fid:19:26]:[Seating id=3, pid=2, done=false, ls=2, ln=n4, rs=3, rn=n3]*
Make Path
=>[fid:22:31]:[Path id=3, seat=1, guest=n5]
Make Path
=>[fid:23:32] [Path id=3, seat=2, guest=n4]
Path Done
Continue Processing
=>[ActivationCreated(35): rule=findSeating
[fid:19:33]:[Seating id=3, pid=2, done=true, ls=2, ln=n4, rs=3, rn=n3]
[fid:4:4]:[Guest name=n3, sex=m, hobbies=h3]
[fid:3:3]:[Guest name=n2, sex=f, hobbies=h3], [fid:12:29]*
=>[ActivationCreated(35): rule=findSeating
[fid:15:23]:[Seating id=2, pid=1, done=true, ls=1, ln=n5, rs=2, rn=n4]
[fid:5:5]:[Guest name=n4, sex=m, hobbies=h1]
[fid:2:2]:[Guest name=n2, sex=f, hobbies=h1]
=>[ActivationCreated(35): rule=findSeating
[fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5]
[fid:9:9]:[Guest name=n5, sex=f, hobbies=h1], [fid:1:1] : [Guest name=n1, sex=m, hobbies=h1]
Assign Seating
=>[fid:24:36]:[Seating id=4, pid=3, done=false, ls=3, ln=n3, rs=4, rn=n2]]
=>[fid:25:37]:[Path id=4, seat=4, guest=n2]]
=>[fid:26:38]:[Chosen id=3, name=n2, hobbies=h3]
==>[ActivationCreated(40): rule=makePath
[fid:24:36]:[Seating id=4, pid=3, done=false, ls=3, ln=n3, rs=4, rn=n2]
[fid:23:32]:[Path id=3, seat=2, guest=n4]*
==>[ActivationCreated(40): rule=makePath
[fid:24:36]:[Seating id=4, pid=3, done=false, ls=3, ln=n3, rs=4, rn=n2]
[fid:20:27]:[Path id=3, seat=3, guest=n3]*
=>[ActivationCreated(40): rule=makePath
[fid:24:36]:[Seating id=4, pid=3, done=false, ls=3, ln=n3, rs=4, rn=n2]
[fid:22:31]:[Path id=3, seat=1, guest=n5]*
=>[ActivationCreated(40): rule=done
[fid:24:36]:[Seating id=4, pid=3, done=false, ls=3, ln=n3, rs=4, rn=n2]*
Make Path
=>fid:27:41:[Path id=4, seat=2, guest=n4]
Make Path
=>fid:28:42]:[Path id=4, seat=1, guest=n5]]
Make Path
=>fid:29:43]:[Path id=4, seat=3, guest=n3]]
Path Done
Continue Processing
=>[ActivationCreated(46): rule=findSeating
[fid:15:23]:[Seating id=2, pid=1, done=true, ls=1, ln=n5, rs=2, rn=n4]
[fid:5:5]:[Guest name=n4, sex=m, hobbies=h1], [fid:2:2]
[Guest name=n2, sex=f, hobbies=h1]
=>[ActivationCreated(46): rule=findSeating
[fid:24:44]:[Seating id=4, pid=3, done=true, ls=3, ln=n3, rs=4, rn=n2]
[fid:2:2]:[Guest name=n2, sex=f, hobbies=h1]
[fid:1:1]:[Guest name=n1, sex=m, hobbies=h1]*
=>[ActivationCreated(46): rule=findSeating
[fid:13:13]:[Seating id=1, pid=0, done=true, ls=1, ln=n5, rs=1, rn=n5]
[fid:9:9]:[Guest name=n5, sex=f, hobbies=h1]
[fid:1:1]:[Guest name=n1, sex=m, hobbies=h1]
Assign Seating
=>[fid:30:47]:[Seating id=5, pid=4, done=false, ls=4, ln=n2, rs=5, rn=n1]
=>[fid:31:48]:[Path id=5, seat=5, guest=n1]
=>[fid:32:49]:[Chosen id=4, name=n1, hobbies=h1]