Drools has a "native" rule language. This format is very light in terms of punctuation, and supports natural and domain specific languages via "expanders" that allow the language to morph to your problem domain. This chapter is mostly concerted with this native rule format. The diagrams used to present the syntax are known as "railroad" diagrams, and they are basically flow charts for the language terms. The technically very keen may also refer to Show
A rule file is typically a file with a .drl extension. In a DRL file you can have multiple rules, queries and functions, as well as some resource declarations like imports, globals and attributes that are assigned and used by your rules and queries. However, you are also able to spread your rules across multiple rule files (in that case, the extension .rule is suggested, but not required) - spreading rules across files can help with managing large numbers of rules. A DRL file is simply a text file. The overall structure of a rule file is: The order in which the elements are declared is not important, except for the package name that, if declared, must be the first element in the rules file. All elements are optional, so you will use only those you need. We will discuss each of them in the following sections. For the inpatients, just as an early view, a rule has the following rough structure: rule It's 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 dialect specific semantic code to be executed. It is important to note that white space is not important, except in the case of domain specific languages, where lines are processed one by one and spaces may be significant to the domain language. Drools 5 introduces the concept of hard and soft keywords. Hard keywords are reserved, you cannot use any hard keyword when naming your domain objects, properties, methods, functions and other elements that are used in the rule text. Here is the list of hard keywords that must be avoided as identifiers when writing rules:
Soft keywords are just recognized in their context, enabling you to use these words in any other place if you wish, although, it is still recommended to avoid them, to avoid confusions, if possible. Here is a list of the soft keywords:
Of course, you can have these (hard and soft) words as part of a method name in camel case, like notSomething() or accumulateSomething() - there are no issues with that scenario. Although the 3 hard keywords above are unlikely to be used in your existing domain models, if you absolutely need to use them as identifiers instead of keywords, the DRL language provides the ability to escape hard keywords on rule text. To escape a word, simply enclose it in grave accents, like this: Holiday( `true` == "yes" ) // please note that Drools will resolve that reference to the method Holiday.isTrue() Comments are sections of text that are ignored by the rule engine. They are stripped out when they are encountered, except inside semantic code blocks, like the RHS of a rule. To create single line comments, you can use either '#' or '//'. The parser will ignore anything in the line after the comment symbol. Example: rule "Testing Comments" when # this is a single line comment // this is also a single line comment eval( true ) # this is a comment in the same line of a pattern then // this is a comment inside a semantic code block # this is another comment in a semantic code block end Multi-line comments are used to comment blocks of text, both in and outside semantic code blocks. Example: rule "Test Multi-line Comments" when /* this is a multi-line comment in the left hand side of a rule */ eval( true ) then /* and this is a multi-line comment in the right hand side of a rule */ end Drools 5 introduces standardized error messages. This standardization aims to help users to find and resolve problems in a easier and faster way. In this section you will learn how to identify and interpret those error messages, and you will also receive some tips on how to solve the problems associated with them. The standardization includes the error message format and to better explain this format, let's use the following example: 1st Block: This area identifies the error code. 2nd Block: Line and column information. 3rd Block: Some text describing the problem. 4th Block: This is the first context. Usually indicates the rule, function, template or query where the error occurred. This block is not mandatory. 5th Block: Identifies the pattern where the error occurred. This block is not mandatory. Indicates the most common errors, where the parser came to a decision point but couldn't identify an alternative. Here are some examples: The above example generates this message:
At first glance this seems to be valid syntax, but it is not (exits != exists). Let's take a look at next example: Now the above code generates this message:
This message means that the parser encountered the token WHEN, actually a hard keyword, but it's in the wrong place since the the rule name is missing. The error "no viable alternative" also occurs when you make a simple lexical mistake. Here is a sample of a lexical problem: The above code misses to close the quotes and because of this the parser generates this error message:
NoteUsually the Line and Column information are accurate, but in some cases (like unclosed quotes), the parser generates a 0:-1 position. In this case you should check whether you didn't forget to close quotes, apostrophes or parentheses. This error indicates that the parser was looking for a particular symbol that it didn’t find at the current input position. Here are some samples: The above example generates this message:
To fix this problem, it is necessary to complete the rule statement. NoteUsually when you get a 0:-1 position, it means that parser reached the end of source. The following code generates more than one error message: These are the errors associated with this source:
Note that the second problem is related to the first. To fix it, just replace the commas (',') by AND operator ('&&'). NoteIn some situations you can get more than one error message. Try to fix one by one, starting at the first one. Some error messages are generated merely as consequences of other errors. A validating semantic predicate evaluated to false. Usually these semantic predicates are used to identify soft keywords. This sample shows exactly this situation: With this sample, we get this error message:
The fdsfdsfds text is invalid and the parser couldn’t identify it as the soft keyword NoteThis error is very similar to 102: Mismatched input, but usually involves soft keywords. This error is associated with the Due to the trailing semicolon within eval, we get this error message:
This problem is simple to fix: just remove the semi-colon. The recognizer came to a subrule in the grammar that must match an alternative at least once, but the subrule did not match anything. Simply put: the parser has entered a branch from where there is no way out. This example illustrates it: This is the message associated to the above sample:
To fix this problem it is necessary to remove the numeric value as it is neither a valid data type which might begin a new template slot nor a possible start for any other rule file construct. Any other message means that something bad has happened, so please contact the development team. 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). Although, it is not possible to merge into the same package resources declared under different names. A single Rulebase may, however, contain multiple packages built on it. A common structure is to have all the rules for a package in the same file as the package declaration (so that is it entirely self-contained). The following railroad 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
Notice that any rule atttribute (as described the section Rule Attributes) may also be written at package level, superseding the attribute's default value. The modified default may still be replaced by an attribute setting within a rule. Import statements work like import statements in Java. You need to specify the fully qualified paths and type names for
any objects you want to use in the rules. Drools automatically imports classes from the Java package of the same name, and also from the package With If multiple packages declare globals with the same identifier they must be of the same type and all of them will reference the same global value. In order to use globals you must:
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. With the new One example may be an instance of a Email service. In your integration code that is calling the rule engine, you obtain 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 the name "email". Then in your rule consequences, you can use things like email.sendSMS(number, message). Globals are not designed to share data between rules and they should never be used for that purpose. Rules always reason and react to the working memory state, so if you want to pass data from rule to rule, assert the data as facts into the working memory. It is strongly discouraged to set or change a global value from inside your rules. We recommend to you always set the value from your application using the working memory interface. Functions are a way to put semantic code in your rule source file, as opposed to in normal Java classes. They can't do anything more than what you can do with helper classes. (In fact, the compiler generates the
helper class for you behind the scenes.) 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 (which can be a good or a bad thing). Functions are most useful for invoking actions on the consequence ( A typical function declaration looks like: function String hello(String name) { return "Hello "+name+"!"; } Note
that the Alternatively, you could use a static method in a helper class, e.g., import function my.package.Foo.hello Irrespective of the way the function is defined or imported, you use a function by calling it by its name, in the consequence or inside a semantic code block. Example: rule "using a static function" when eval( true ) then System.out.println( hello( "Bob" ) ); end Type declarations have two main goals in the rules engine: to allow the declaration of new types, and to allow the declaration of metadata for types.
To declare a new type, all you need to do is use the keyword The previous example declares a new fact type called For instance, we may want to declare another fact type As we can see on the previous example, You may avoid having to write the fully qualified name of a class every time you write it by using the When you declare a new fact type, Drools will, at compile time, generate bytecode that implements a Java class representing the fact type. The generated Java class will be a one-to-one Java Bean mapping of the type definition. So, for the previous example, the generated Java class would be: Since the generated class is a simple Java class, it can be used transparently in the rules, like any other fact. Metadata may be assigned to several different constructions in Drools: fact types, fact attributes and rules. Drools uses the at sign ('@') to introduce metadata, and it always uses the form: @metadata_key( metadata_value ) The parenthesized metadata_value is optional. For instance, if you want to declare a metadata attribute like Drools allows the declaration of any arbitrary metadata attribute, but some will have special meaning to the engine, while others are simply available for querying at runtime. Drools allows the declaration of metadata both for fact types and for fact attributes. Any metadata that is declared before the attributes of a fact type are assigned to the fact type, while metadata declared after an attribute are assigned to that particular attribute. In the previous example, there are two metadata items declared for the fact type ( @position can be used to declare the position of a field, overriding the default declared order. This is used for positional constraints in patterns. declare Cheese name : String @position(1) shop : String @position(2) price : int @position(0) end Drools allows the declaration of metadata attributes for existing types in the same way as when declaring metadata attributes for new fact types. The only difference is that there are no fields in that declaration. For instance, if there is a class org.drools.examples.Person, and one wants to declare metadata for it, it's possible to write the following code: Instead of using the import, it is also possible to reference the class by its fully qualified name, but since the class will also be referenced in the rules, it is usually shorter to add the import and use the short class name everywhere. Generate constructors with parameters for declared types. Example: for a declared type like the following: declare Person firstName : String @key lastName : String @key age : int end The compiler will implicitly generate 3 constructors: one without parameters, one with the @key fields, and one with all fields. Person() // parameterless constructor Person( String firstName, String lastName ) Person( String firstName, String lastName, int age ) @typesafe( <boolean>) has been added to type declarations. By default all type declarations are compiled with type safety enabled; @typesafe( false ) provides a means to override this behaviour by permitting a fall-back, to type unsafe evaluation where all constraints are generated as MVEL constraints and executed dynamically. This can be important when dealing with collections that do not have any generics or mixed type collections. Declared types are usually used inside rules files, while Java models are used when sharing the model between rules and applications. Although, sometimes, the application may need to access and handle facts from the declared types, especially when the application is wrapping the rules engine and providing higher level, domain specific user interfaces for rules management. In such cases, the generated classes can be handled as usual with the Java Reflection API, but, as we know, that usually requires a lot of work for small results. Therefore, Drools provides a simplified API for the most common fact handling the application may want to do. The first important thing to realize is that a declared fact will belong to the
package where it was declared. So, for instance, in the example below, Declared types, as discussed previously, are generated at knowledge base compilation time, i.e., the application will only have access to them at application run time. Therefore, these classes are not available for direct reference from the application. Drools then provides an interface through
which users can handle declared types from the application code: The API also includes other helpful methods, like setting all the attributes at once, reading values from a Map, or reading all attributes at once, into a Map. Although the API is similar to Java reflection (yet much simpler to use), it does not use reflection underneath, relying on much more performant accessors implemented with generated bytecode. Type declarations now support 'extends' keyword for inheritance In order to extend a type declared in Java by a DRL declared subtype, repeat the supertype in a declare statement without any fields. import org.people.Person declare Person end declare Student extends Person school : String end declare LongTermStudent extends Student years : int course : String end A rule specifies that when a particular set of conditions occur, specified in the Left Hand Side (LHS), then do what queryis specified as a list of actions in the Right Hand Side (RHS). A common question from users is "Why use when instead of if?" "When" was chosen over "if" because "if" is normally part of a procedural execution flow, where, at a specific point in time, a condition is to be checked. In contrast, "when" indicates that the condition evaluation is not tied to a specific evaluation sequence or point in time, but that it happens continually, at any time during the life time of the engine; whenever the condition is met, the actions are executed. A rule must have a name, unique within its rule package. If you define a rule twice in the same DRL it produces an error while loading. If you add a DRL that includes a rule name already in the package, it replaces the previous rule. If a rule name is to have spaces, then it will need to be enclosed in double quotes (it is best to always use double quotes). Attributes - described below - are optional. They are best written one per line. The LHS of the rule follows the Rule attributes provide a declarative way to influence the behavior of the rule. Some are quite simple, while others are part of complex subsystems such as ruleflow. To get the most from Drools you should make sure you have a proper understanding of each attribute.
default value: type: Boolean When a rule's consequence modifies a fact it may cause the rule to activate again, causing an infinite loop. Setting no-loop to true will skip the creation of another Activation for the rule with the current set of facts. ruleflow-group default value: N/A type: String Ruleflow is a Drools feature that lets you exercise control over the firing of rules. Rules that are assembled by the same ruleflow-group identifier fire only when their group is active. lock-on-active default value: type: Boolean Whenever a ruleflow-group becomes active or an agenda-group receives the focus, any rule within that group that has lock-on-active set to true will not be activated any more; irrespective of the origin of the update, the activation of a matching rule is discarded. This is a stronger version of no-loop, because the change could now be caused not only by the rule itself. It's ideal for calculation rules where you have a number of rules that modify a fact and you don't want any rule re-matching and firing again. Only when the ruleflow-group is no longer active or the agenda-group loses the focus those rules with lock-on-active set to true become eligible again for their activations to be placed onto the agenda. salience default
value: type: integer Each rule has an integer salience attribute which defaults to zero 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. Drools also supports dynamic salience where you can use an expression involving bound variables. agenda-group default value: MAIN type: String Agenda groups allow the user to partition the Agenda providing more execution control. Only rules in the agenda group that has acquired the focus are allowed to fire. auto-focus default value: type: Boolean When a rule is activated where the activation-group default value: N/A type: String Rules that belong to the same activation-group, identified by this attribute's string value, will only fire exclusively. In other words, the first rule in an activation-group to fire will cancel the other rules' activations, i.e., stop them from firing. Note: This used to be called Xor group, but technically it's not quite an Xor. You may still hear people mention Xor group; just swap that term in your mind with activation-group. dialect default value: as specified by the package type: String possible values: "java" or "mvel" The dialect species the language to be used for any code expressions in the LHS or the RHS code block. Currently two dialects are available, Java and MVEL. While the dialect can be specified at the package level, this attribute allows the package definition to be overridden for a rule. date-effective default value: N/A type: String, containing a date and time definition A rule can only activate if the current date and time is after date-effective attribute. date-expires default value: N/A type: String, containing a date and time definition A rule cannot activate if the current date and time is after the date-expires attribute. duration default value: no default value type: long The duration dictates that the rule will fire after a specified duration, if it is still true. Rules now suport both interval and cron based timers, which replace the now deprecated duration attribute. Interval (indicated by "int:") timers follow the semantics of java.util.Timer objects, with an initial delay and an optional repeat interval. Cron (indicated by "cron:") timers follow standard Unix cron expressions: Calendars are used to control when rules can fire. The Calendar API is modelled on Quartz: Calendars are registered with the StatefulKnowledgeSession: They can be used in conjunction with normal rules and rules including timers. The rule attribute "calendars" may contain one or more comma-separated calendar names written as string literals. The Left Hand Side (LHS) is a common name for the conditional part of the rule. It consists of zero or more Conditional Elements. If the LHS is empty, it will be considered as a condition element that is always true and it will be activated once, when a new WorkingMemory session is created. Conditional elements work on one or more patterns (which are described below). The most common conditional element is " NoteAn " // Compile error $person : (Person( name == "Romeo" ) and Person( name == "Juliet")) A pattern element is the most important Conditional Element. It can potentially match on each fact that is inserted in the working memory. A pattern contains of zero or more constraints and has an optional pattern binding. The railroad diagram below shows the syntax for this. In its simplest form, with no constraints, a pattern matches against a fact of the given type. In the following case the type is Person() The type need not be the actual class of some fact object. Patterns may refer to superclasses or even interfaces, thereby potentially matching facts from many different classes. Object() // matches all objects in the working memory Inside of the pattern parenthesis is where all the action happens: it defines the constraints for that pattern. For example, with a age related constraint: Person( age == 100 ) NoteFor backwards compatibility reasons it's
allowed to suffix patterns with the For referring to the matched object, use a pattern binding variable such as The prefixed dollar symbol ( A constraint is an expression that
returns Person( 5 < 6 ) // just an example, as constraints like this would be useless in a real pattern In essence, it's a Java expression with some enhancements (such as property access) and a few differences (such as Any bean property can be used directly. A bean property is exposed using a standard Java bean getter: a method Person( age == 50 ) // this is the same as: Person( getAge() == 50 ) Drools uses the standard JDK NoteWe recommend using property access ( WarningProperty accessors must not 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 in between invocations to make it faster. public int getAge() { age++; // Do NOT do this return age; } public int getAge() { Date now = DateUtil.now(); // Do NOT do this return DateUtil.differenceInYears(now, birthday); } To solve this latter case, insert a fact that wraps the current date into working memory and update
that fact between NoteThe following fallback applies: if the getter of a property cannot be found, the compiler will resort to using the property name as a method name and without arguments: Person( age == 50 ) // If Person.getAge() does not exists, this falls back to: Person( age() == 50 ) Nested property access is also supported: Person( address.houseNumber == 50 ) // this is the same as: Person( getAddress().getHouseNumber() == 50 ) Nested properties are also indexed. WarningIn a stateful session, care should be taken when using nested accessors as the Working Memory is not aware of any of the nested values, and does not know when they change. Either consider them immutable while any of their parent references are inserted into the Working Memory. Or, instead, if you wish to modify a nested value you should mark all of the outer facts as updated. In the above example, when the You can use any Java expression that returns a Person( age == 50 ) It is possible to change the evaluation priority by using parentheses, as in any logic or mathematical expression: Person( age > 100 && ( age % 10 == 0 ) ) It is possible to reuse Java methods: Person( Math.round( weight / ( height * height ) ) < 25.0 ) WarningAs for property accessors, methods must not change the state of the object in a way that may affect the rules. Any method executed on a fact in the LHS should be a read only method. Person( incrementAndGetAge() == 10 ) // Do NOT do this WarningThe state of a fact should not change between rule invocations (unless those facts are marked as updated to the working memory on every change): Person( System.currentTimeMillis() % 1000 == 0 ) // Do NOT do this Normal Java operator precedence applies, see the operator precedence list below. ImportantAll operators have normal Java semantics except for The // Similar to: java.util.Objects.equals(person.getFirstName(), "John") // so (because "John" is not null) similar to: // "John".equals(person.getFirstName()) Person( firstName == "John" ) The // Similar to: !java.util.Objects.equals(person.getFirstName(), "John") Person( firstName != "John" ) Type coercion is always attempted if the field and the value are of different types; exceptions will be thrown if a bad coercion is attempted. For instance, if "ten" is provided as a string in a numeric evaluator, an exception is thrown, whereas "10" would coerce to a numeric 10. Coercion is always in favor of the field type and not the value type: Person( age == "10" ) // "10" is coerced to 10 The comma character (' // Person is at least 50 and weighs at least 80 kg Person( age > 50, weight > 80 ) // Person is at least 50, weighs at least 80 kg and is taller than 2 meter. Person( age > 50, weight > 80, height > 2 ) NoteAlthough the The comma operator should be preferred at the top level constraint, as it makes constraints easier to read and the engine will often be able to optimize them better. The comma ( Person( ( age > 50, weight > 80 ) || height > 2 ) // Do NOT do this: compile error // Use this instead Person( ( age > 50 && weight > 80 ) || height > 2 ) A property can be bound to a variable: // 2 persons of the same age Person( $firstAge : age ) // binding Person( age == $firstAge ) // constraint expression The prefixed dollar symbol ( NoteFor backwards compatibility reasons, It's allowed (but not recommended) to mix a constraint binding and constraint expressions as such: // Not recommended Person( $age : age * 2 < 100 ) // Recommended (seperates bindings and constraint expressions) Person( age * 2 < 100, $age : age ) Bound variable restrictions using the operator Drools does not allow bindings to the same declaration. However this is an important aspect to derivation query unification. While positional arguments are always processed with unification a special unification symbol, ':=', was introduced for named arguments named arguments. The following "unifies" the age argument across two people. Person( $age := age ) Person( $age := age) In essence unification will declare a binding for the first occurance, and constrain to the same value of the bound field for sequence occurances. Besides normal Java literals (including Java 5 enums), this literal is also supported: The date format It's possible to directly access a // Same as childList(0).getAge() == 18 Person( childList[0].age == 18 ) It's also possible to directly access a // Same as credentialMap.get("jsmith").isValid() Person( credentialMap["jsmith"].valid ) This allows you to place more than one restriction on a field using the restriction connectives // Simple abbreviated combined relation condition using a single && Person( age > 30 && < 40 ) // Complex abbreviated combined relation using groupings Person( age ( (> 30 && < 40) || (> 20 && < 25) ) ) // Mixing abbreviated combined relation with constraint connectives Person( age > 30 && < 40 || location == "london" ) Coercion to the correct value for the evaluator and the field will be attempted. These operators can be used on properties with natural ordering. For example, for Date
fields, Person( firstName < $otherFirstName ) Person( birthDate < $otherBirthDate ) Only applies on The operator returns true if the String does not match the regular expression. The same rules apply as for the Only applies on The operator Person( father memberOf parents ) The operator Person( father not memberOf childern ) This operator is similar to This operator Message( routingValue str[startsWith] "R1" ) Message( routingValue str[endsWith] "R2" ) Message( routingValue str[length] 17 ) The compound value restriction is used where there is more than one possible value to match. Currently only the The operators are evaluated in this precedence: Patterns now support positional arguments on type declarations. Positional arguments are ones where you don't need to specify the field name, as the position maps to a known named field. i.e. Person( name == "mark" ) can be rewritten as Person( "mark"; ). The semicolon ';' is important so that the engine knows that everything before it is a positional argument. Otherwise we might assume it was a boolean expression, which is how it could be interpretted after the semicolon. You can mix positional and named arguments on a pattern by using the semicolon ';' to separate them. Any variables used in a positional that have not yet been bound will be bound to the field that maps to that position. declare Cheese name : String shop : String price : int end Example patterns, with two constraints and a binding. Remember semicolon ';' is used to differentiate the positional section from the named argument section. Variables and literals and expressions using just literals are supported in posional arguments, but not variables. Positional arguments are always resolved using unification. Cheese( "stilton", "Cheese Shop", p; ) Cheese( "stilton", "Cheese Shop"; p : price ) Cheese( "stilton"; shop == "Cheese Shop", p : price ) Cheese( name == "stilton"; shop == "Cheese Shop", p : price ) Positional arguments that are given a previously declared binding will consrain against that using unification; these are referred to as input arguments. If the binding does not yet exist, it will create the declaration binding it to the field represented by the position argument; these are referred to as output arguments. The Conditional Element Traditional infix //infixAnd Cheese( cheeseType : type ) and Person( favouriteCheese == cheeseType ) Explicit grouping with parentheses is also supported: //infixAnd with grouping ( Cheese( cheeseType : type ) and ( Person( favouriteCheese == cheeseType ) or Person( favouriteCheese == cheeseType ) ) NoteThe symbol Prefix (and Cheese( cheeseType : type ) Person( favouriteCheese == cheeseType ) ) The root element of the LHS is an implicit prefix The Conditional Element Traditional infix //infixOr Cheese( cheeseType : type ) or Person( favouriteCheese == cheeseType ) Explicit grouping with parentheses is also supported: //infixOr with grouping ( Cheese( cheeseType : type ) or ( Person( favouriteCheese == cheeseType ) and Person( favouriteCheese == cheeseType ) ) NoteThe symbol Prefix
(or Person( sex == "f", age > 60 ) Person( sex == "m", age > 65 ) NoteThe behavior of the Conditional Element The Conditional Element pensioner : ( Person( sex == "f", age > 60 ) or Person( sex == "m", age > 65 ) ) (or pensioner : Person( sex == "f", age > 60 ) pensioner : Person( sex == "m", age > 65 ) ) Since the conditional element The best way to think of the conditional element The CE The keyword The CE The keyword The Conditional Element rule "All English buses are red" when forall( $bus : Bus( type == 'english') Bus( this == $bus, color = 'red' ) ) then # all english buses are red end In the above rule, we "select" all Bus objects whose type is "english". Then, for each fact that matches this pattern we evaluate the following patterns and if they match, the forall CE will evaluate to true. To state that all facts of a given type in the working memory must match a set of constraints, Another example shows multiple patterns inside the Forall can be nested inside other CEs. For instance, As a side note, not(p1 and not(and p2 p3...)) Also, it is important to note that The Conditional Element The expression used to define the object source is any expression that follows regular MVEL syntax. Therefore, it allows you to easily use object property navigation, execute method calls and access maps and collections elements. Here is a simple example of reasoning and binding on another pattern sub-field: rule "validate zipcode" when Person( $personAddress : address ) Address( zipcode == "23920W") from $personAddress then # zip code is ok end With all the flexibility from the new expressiveness in the Drools engine you can slice and dice this problem many ways. This is the same but shows how you can use a graph notation with the 'from': rule "validate zipcode" when $p : Person( ) $a : Address( zipcode == "23920W") from $p.address then # zip code is ok end Previous examples were evaluations using a single pattern. The CE rule "apply 10% discount to all items over US$ 100,00 in an order" when $order : Order() $item : OrderItem( value > 100 ) from $order.items then # apply discount to $item end The above example will cause the rule to fire once for each item whose value is greater than 100 for each given order. You must take caution, however, when using rule "Assign people in North Carolina (NC) to sales region 1" ruleflow-group "test" lock-on-active true when $p : Person( ) $a : Address( state == "NC") from $p.address then modify ($p) {} #Assign person to sales region 1 in a modify block end rule "Apply a discount to people in the city of Raleigh" ruleflow-group "test" lock-on-active true when $p : Person( ) $a : Address( city == "Raleigh") from $p.address then modify ($p) {} #Apply discount to person in a modify block end In the above example, persons in Raleigh, NC should be assigned to sales region 1 and receive a discount; i.e., you would expect both rules to activate and fire. Instead you will find that only the second rule fires. If you were to turn on the audit log, you would also see that when the second rule fires, it deactivates the first rule. Since the rule attribute First, it's important to review why you would use the above pattern. You may have many rules across different rule-flow groups. When rules modify working memory and other rules downstream of your RuleFlow (in different rule-flow groups) need to be reevaluated, the use of There are several ways to address this issue:
The preferred solution is to minimize use of rule "Assign people in North Carolina (NC) to sales region 1" ruleflow-group "test" lock-on-active true when $p : Person(address.state == "NC" ) then modify ($p) {} #Assign person to sales region 1 in a modify block end rule "Apply a discount to people in the city of Raleigh" ruleflow-group "test" lock-on-active true when $p : Person(address.city == "Raleigh" ) then modify ($p) {} #Apply discount to person in a modify block end Now, you will find that
both rules fire as expected. However, it is not always possible to access nested facts as above. Consider an example where a Person holds one or more Addresses and you wish to use an existential quantifier to match people with at least one address that meets certain conditions. In this case, you would have to resort to the use of There are several ways to use rule "Assign people in North Carolina (NC) to sales region 1" ruleflow-group "test" lock-on-active true when $p : Person($addresses : addresses) exists (Address(state == "NC") from $addresses) then modify ($p) {} #Assign person to sales region 1 in a modify block end rule "Apply a discount to people in the city of Raleigh" ruleflow-group "test" lock-on-active true when $p : Person($addresses : addresses) exists (Address(city == "Raleigh") from $addresses) then modify ($p) {} #Apply discount to person in a modify block end However, the following slightly different approach does exhibit the problem: rule "Assign people in North Carolina (NC) to sales region 1" ruleflow-group "test" lock-on-active true when $assessment : Assessment() $p : Person() $addresses : List() from $p.addresses exists (Address( state == "NC") from $addresses) then modify ($assessment) {} #Modify assessment in a modify block end rule "Apply a discount to people in the city of Raleigh" ruleflow-group "test" lock-on-active true when $assessment : Assessment() $p : Person() $addresses : List() from $p.addresses exists (Address( city == "Raleigh") from $addresses) then modify ($assessment) {} #Modify assessment in a modify block end In the above example, the $addresses variable is returned from the use of Though the above examples demonstrate how to combine the use of A better alternative is to assert more facts into working memory. In this case, a person's addresses may be asserted into
working memory and the use of There are cases, however, where asserting all data into working memory is not practical and we need to find other solutions. Another option is to reevaluate the need for The Conditional Element import java.util.ArrayList rule "Raise priority if system has more than 3 pending alarms" when $system : System() $alarms : ArrayList( size >= 3 ) from collect( Alarm( system == $system, status == 'pending' ) ) then # Raise priority, because system $system has # 3 or more alarms pending. The pending alarms # are $alarms. end In the above example, the rule will look for all pending alarms in the working memory for each given system and group them in ArrayLists. If 3 or more alarms are found for a given system, the rule will fire. The result pattern of Both source and result patterns can be constrained as any other pattern. Variables bound before the Collect accepts nested import java.util.LinkedList; rule "Send a message to all mothers" when $town : Town( name == 'Paris' ) $mothers : LinkedList() from collect( Person( gender == 'F', children > 0 ) from $town.getPeople() ) then # send a message to all mothers end The Conditional Element Accumulate supports both the use of pre-defined accumulate functions, or the use of inline custom code. Inline custom code should be avoided though, as it is extremely hard to maintain, and frequently leads to code duplication. Accumulate functions are easier to test and reuse. The accumulate CE is a very powerful CE, but it gets real declarative and easy to use when using predefined functions that are known as Accumulate Functions. They work exactly like accumulate, but instead of explicitly writing custom code in every accumulate CE, the user can use predefined code for common operations. For instance, a rule to apply a 10% discount on orders over $100 could be written in the following way, using Accumulate Functions: rule "Apply 10% discount to orders over US$ 100,00" when $order : Order() $total : Number( doubleValue > 100 ) from accumulate( OrderItem( order == $order, $value : value ), sum( $value ) ) then # apply discount to $order end In the above example, sum is an Accumulate Function and will sum the $value of all OrderItems and return the result. Drools ships with the following built-in accumulate functions:
These common functions accept any expression as input. For instance, if someone wants to calculate the average profit on all items of an order, a rule could be written using the average function: rule "Average profit" when $order : Order() $profit : Number() from accumulate( OrderItem( order == $order, $cost : cost, $price : price ) average( 1 - $cost / $price ) ) then # average profit for $order is $profit end Accumulate Functions are all pluggable. That means that if needed, custom, domain specific functions can easily be added to the engine and rules can start to use them without
any restrictions. To implement a new Accumulate Functions all one needs to do is to create a Java class that implements the
The code for the function is very simple, as we could expect, as all the "dirty" integration work is done by the engine. Finally, to plug the function into the engine, we added it to the configuration file: drools.accumulate.function.average = org.drools.base.accumulators.AverageAccumulateFunction Here, "drools.accumulate.function." is a prefix that must always be used, "average" is how the function will be used in the rule file, and "org.drools.base.accumulators.AverageAccumulateFunction" is the fully qualified name of the class that implements the function behavior. WarningThe use of accumulate with inline custom code is not a good practice for several reasons, including dificulties on maintaining and testing rules that use them, as well as the inability of reusing that code. Implementing your own accumulate functions is very simple and straightforward, they are easy to unit test and to use. This form of accumulate is supported for backward compatibility only. The general
syntax of the
The meaning of each of the elements is the following:
It is easier to understand if we look at an example: rule "Apply 10% discount to orders over US$ 100,00" when $order : Order() $total : Number( doubleValue > 100 ) from accumulate( OrderItem( order == $order, $value : value ), init( double total = 0; ), action( total += $value; ), reverse( total -= $value; ), result( total ) ) then # apply discount to $order end In the above example, for each The example used Java as the semantic dialect, and as such, note that the usage of the semicolon as statement delimiter is mandatory in the init, action and reverse code blocks. The result is an expression and, as such, it does not admit ';'. If the user uses any other dialect, he must comply to that dialect's specific syntax. As mentioned before, the reverse code is optional, but it is strongly recommended that the user writes it in order to benefit from the improved performance on update and retract. The rule "Accumulate using custom objects" when $person : Person( $likes : likes ) $cheesery : Cheesery( totalAmount > 100 ) from accumulate( $cheese : Cheese( type == $likes ), init( Cheesery cheesery = new Cheesery(); ), action( cheesery.addCheese( $cheese ); ), reverse( cheesery.removeCheese( $cheese ); ), result( cheesery ) ); then // do something end The accumulate CE now supports multiple functions. For instance, if one needs to find the minimum, maximum and average value for the same set of data, instead of having to repeat the accumulate statement 3 times, a single accumulate can be used. rule "Max, min and average" when accumulate( Cheese( $price : price ), $max : max( $price ), $min : min( $price ), $avg : average( $price ) ) then // do something end The conditional element Evals cannot be indexed and thus are not as efficient as 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. For folks who are familiar with Drools 2.x lineage, the old Drools parameter and condition tags are equivalent to binding a variable to an appropriate type, and then using it in an eval node. p1 : Parameter() p2 : Parameter() eval( p1.getList().containsKey( p2.getItem() ) ) p1 : Parameter() p2 : Parameter() // call function isValid in the LHS eval( isValid( p1, p2 ) ) The Right Hand Side (RHS) is a common name for the consequence or action part of the rule; this part should contain a list of actions to be executed. It is bad practice to use imperative or conditional code in the RHS of a rule; as a rule should be atomic in nature - "when this, then do this", not "when this, maybe do this". The RHS part of a rule should also be kept small, thus keeping it declarative and readable. If you find you need imperative and/or conditional code in the RHS, then maybe you should be breaking that rule down into multiple rules. The main purpose of the RHS is to insert, retractor modify working memory data. To assist with that there are a few convenience methods you can use to modify working memory; without having to first reference a working memory instance.
These convenience methods are basically macros that provide short cuts to the
The full Knowledge Runtime API is exposed through another predefined variable,
This language extension
provides a structured approach to fact updates. It combines the update operation with a number of setter calls to change the object's fields. This is the syntax schema for the
The parenthesized <fact-expression> must yield a fact object reference. The expression list in the block should consist of setter calls for the given object, to be written without the usual object reference, which is automatically prepended by the compiler. The example illustrates a simple fact modification. Drools attempts to preserve numbers in their primitive or object wrapper form, so a variable bound to an int primitive when used in a code block or expression will no longer need manual unboxing; unlike Drools 3.0 where all primitives were autoboxed, requiring manual unboxing. A variable bound to an object wrapper will remain as an object; the existing JDK 1.5 and JDK 5 rules to handle auto-boxing and unboxing apply in this case. When evaluating field constraints, the system attempts to coerce one of the values into a comparable format; so a primitive is comparable to an object wrapper. A query is a simple way to search the working memory for facts that match the stated conditions. Therefore, it contains only the structure of the LHS of a rule, so that you specify neither "when" nor "then". A query has an optional set of parameters, each of which can be optionally typed. If the type is not given, the type Object is assumed. The engine will attempt to coerce the values as needed. Query names are global to the KnowledgeBase; so do not add queries of the same name to different packages for the same RuleBase. To return the results use The first example presents a simple query for all the people over the age of 30. The second one, using parameters, combines the age limit with a location. We iterate over the returned QueryResults using a standard "for" loop. Each element is a QueryResultsRow which we can use to access each of the columns in the tuple. These columns can be accessed by bound declaration name or index position. Example 5.54. Query People over the age of 30 QueryResults results = ksession.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 ( QueryResultsRow row : results ) { Person person = ( Person ) row.get( "person" ); System.out.println( person.getName() + "\n" ); } Support for positional syntax has been added for more compact code. By default the the declared type order in the type declaration matches the argument position. But it possible to override these using the @position annotation. declare Cheese name : String @position(1) shop : String @position(2) price : int @position(0) end The @Position annotation, in the org.drools.definition.type package, can be used to annotate original pojos on the classpath. Currently only fields on classes can be annotated. Inheritence of classes is supported, but not interfaces of methods yet. Queries can now call other queries, this combined with optional query arguments provides deriviation query style backward chaining. Positional and mixed positional/named type is supported. Literal expressions can passed as query arguments, but at this stage you cannot mix expressions with variables. Here s an example of a query that calls another query. Note that 'z' here will always be an 'out' variable. The '?' symbol means the query is pull only, once the results are returned you will not received further results as the underlying data changes. declare Location thing : String location : String end query isContainedIn( String x, String y ) Location(x, y;) or ( Location(z, y;) and ?isContainedIn(x, z;) ) end As previously mentioned you can use live "open" queries to reactively receive changes over time from the query results, as the underlying data it queries against changes. Notice the "look" rule calls the query without using '?'. query isContainedIn( String x, String y ) Location(x, y;) or ( Location(z, y;) and isContainedIn(x, z;) ) end rule look when Person( $l : likes ) isContainedIn( $l, 'office'; ) then insertLogical( $l + 'is in the office' ); end Drools supports unification for derivation queries, in short this means that arguments are optional. It is possible to call queries from java leaving arguments unspecified using the static field org.drools.runtime.rule.Variable.v - note you must use 'v' and not an alternative instanceof Variable. These are referred to as 'out' arguments. Note that the query itself does not declare at compile time whether an argument is in or an out, this can be defined purely at runtime on each use. The following example will return all objects contained in the office. results = ksession.getQueryResults( "isContainedIn", new Object[] { Variable.v, "office" } ); l = new ArrayList<List<String>>(); for ( QueryResultsRow r : results ) { l.add( Arrays.asList( new String[] { (String) r.get( "x" ), (String) r.get( "y" ) } ) ); } The algorithm uses stacks to handle recursion, so the method stack will not blow up. The following is not yet suported:
Domain Specific Languages (or DSLs) are a way of creating a rule language that is dedicated to your problem domain. A set of DSL definitions consists of transformations from DSL "sentences" to DRL constructs, which lets you use of all the underlying rule language and engine features. Given a DSL, you write rules in DSL rule (or DSLR) files, which will be translated into DRL files. DSL and DSLR files are plain text files, and you can use any text editor to create and modify them. But there are also DSL and DSLR editors, both in the IDE as well as in the web based BRMS, and you can use those as well, although they may not provide you with the full DSL functionality. DSLs can serve as a layer of separation between rule authoring (and rule authors) and the technical intricacies resulting from the modelling of domain object and the rule engine's native language and methods. If your rules need to be read and validated by domain experts (such as business analysts, for instance) who are not programmers, you should consider using a DSL; it hides implementation details and focuses on the rule logic proper. DSL sentences can also act as "templates" for conditional elements and consequence actions that are used repeatedly in your rules, possibly with minor variations. You may define DSL sentences as being mapped to these repeated phrases, with parameters providing a means for accomodating those variations. DSLs have no impact on the rule engine at runtime, they are just a compile time feature, requiring a special parser and transformer. The Drools DSL mechanism allows you to customise conditional expressions and consequence actions. A global substitution mechanism ("keyword") is also available. In the preceding example, Whenever the DSL parser matches a line from the rule file written in the DSL with an expression in the DSL definition, it performs three steps of string manipulation. First, it extracts the string values appearing where the expression contains variable names in braces (here: Note that the expressions (i.e., the strings on the left hand side of the equal sign) are used as regular expressions in a pattern matching operation against a line of the DSL rule file, matching all or part of a line. This means you can use (for instance) a '?' to indicate that the preceding character is optional. One good reason to use this is to overcome variations in natural language phrases of your DSL. But, given that these expressions are regular expression patterns, this also means that all "magic" characters of Java's pattern syntax have to be escaped with a preceding backslash ('\'). It is important to note that the compiler transforms DSL rule files line by line. In the above example, all the text after "Something is " to the end of the line is captured as the replacement value for "{colour}", and this is used for interpolating the target string. This may not be exactly what you want. For instance, when you intend to merge different DSL expressions to generate a composite DRL pattern, you need to transform a DSLR line in several independent operations. The best way to achieve this is to ensure that the captures are surrounded by characteristic text - words or even single characters. As a result, the matching operation done by the parser plucks out a substring from somewhere within the line. In the example below, quotes are used as distinctive characters. Note that the characters that surround the capture are not included during interpolation, just the contents between them. As a rule of thumb, use quotes for textual data that a rule editor may want to enter. You can also enclose the capture with words to ensure that the text is correctly matched. Both is illustrated by the following example. Note that a single line such as It is a good idea to avoid punctuation (other than quotes or apostrophes) in your DSL expressions as much as possible. The main reason is that punctuation is easy to forget for rule authors using your DSL. Another reason is that parentheses, the period and the question mark are magic characters, requiring escaping in the DSL definition. In a DSL mapping, the braces "{" and "}" should only be used to enclose a variable definition or reference, resulting in a capture. If they should occur literally, either in the expression or within the replacement text on the right hand side, they must be escaped with a preceding backslash ("\"): [then]do something= if (foo) \{ doSomething(); \} NoteIf braces "{" and "}" should appear in the replacement string of a DSL definition, escape them with a backslash ('\'). Given the above DSL examples, the following examples show the expansion of various DSLR snippets: NoteDon't forget that if you are capturing plain text from a DSL rule line and want to use it as a string literal in the expansion, you must provide the quotes on the right hand side of the mapping. You can chain DSL expressions together on one line, as long as it is clear to the parser where one ends and the next one begins and where the text representing a parameter ends. (Otherwise you risk getting all the text until the end of the line as a parameter value.) The DSL expressions are tried, one after the other, according to their order in the DSL definition file. After any match, all remaining DSL expressions are investigated, too. The resulting DRL text may consist of more than one line. Line ends are in the replacement text are written as A common requirement when writing rule conditions is to be able to add an arbitrary combination of constraints to a pattern. Given that a fact type may have many fields, having to provide an individual DSL statement for each combination would be plain folly. The DSL facility allows you to add constraints to a pattern by a simple convention: if your DSL expression starts with a hyphen (minus character, "-") it is assumed to be a field constraint and, consequently, is is added to the last pattern line preceding it. For an
example, lets take look at class Cheese(age < 5, price == 20, type=="stilton", country=="ch") The DSL definitions given below result in three DSL phrases which may be used to create any combination of constraint involving these fields. [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 a line beginning with "-" and add it as a constraint to the preceding pattern, inserting a comma when it is required. For the preceding example, the resulting DRL is: Cheese(age<42, type=='stilton') Combining all all numeric fields with all relational operators (according to the DSL expression "age is less than..." in the preceding example) produces an unwieldy amount of DSL entries. But you can define DSL phrases for the various operators and even a generic expression that handles any field constraint, as shown below. (Notice that the expression definition contains a regular expression in addition to the variable name.) [when][]is less than or equal to=<= [when][]is less than=< [when][]is greater than or equal to=>= [when][]is greater than=> [when][]is equal to=== [when][]equals=== [when][]There is a Cheese with=Cheese() [when][]- {field:\w*} {operator} {value:\d*}={field} {operator} {value} Given these DSL definitions, you can write rules with conditions such as: There is a Cheese with - age is less than 42 - rating is greater than 50 - type equals 'stilton' In this specific case, a phrase such as "is less than" is replaced by Cheese(age<42, rating > 50, type=='stilton') NoteThe order of the entries in the DSL is important if separate DSL expressions are intended to match the same line, one after the other. A good way to get started is to write representative samples of the rules your application requires, and to test them as you develop. This will provide you with a stable framework of conditional elements and their constraints. Rules, both in DRL and in DSLR, refer to entities according to the data model representing the application data that should be subject to the reasoning process defined in rules. Notice that writing rules is generally easier if most of the data model's types are facts. Given an initial set of rules, it should be possible to identify recurring or similar code snippets and to mark variable parts as parameters. This provides reliable leads as to what might be a handy DSL entry. Also, make sure you have a full grasp of the jargon the domain experts are using, and base your DSL phrases on this vocabulary. You may postpone implementation decisions concerning conditions and actions during this first design phase by leaving certain conditional elements and actions in their DRL form by prefixing a line with a greater sign (">"). (This is also handy for inserting debugging statements.) During the next development phase, you should find that the DSL configuration stabilizes pretty quickly. New rules can be written by reusing the existing DSL definitions, or by adding a parameter to an existing condition or consequence entry. Try to keep the number of DSL entries small. Using parameters lets you apply the same DSL sentence for similar rule patterns or constraints. But do not exaggerate: authors using the DSL should still be able to identify DSL phrases by some fixed text.
A DSL file is a text file in a line-oriented format. Its entries are used for transforming a DSLR file into a file according to DRL syntax.
A DSL entry consists of the following four parts:
Debugging of DSL expansion can be turned on, selectively, by using a comment line starting with "#/" which may contain one or more words from the table presented below. The resulting output is written to standard output. Below are some sample DSL definitions, with comments describing the language features they illustrate. # Comment: DSL examples #/ debug: display result and usage # keyword definition: replaces "regula" by "rule" [keyword][]regula=rule # conditional element: "T" or "t", "a" or "an", convert matched word [when][][Tt]here is an? {entity:\w+}= ${entity!lc}: {entity!ucfirst} () # consequence statement: convert matched word, literal braces [then][]update {entity:\w+}=modify( ${entity!lc} )\{ \} The transformation of a DSLR file proceeds as follows:
NoteIt is currently not possible to use a line with a leading hyphen to insert text into other conditional element forms (e.g., "accumulate") or it may only work for the first insertion (e.g., "eval"). All string transformation functions are described in the following table. Table 5.3. String transformation functions
The following DSL examples show how to use string transformation functions. # definitions for conditions [when][]There is an? {entity}=${entity!lc}: {entity!ucfirst}() [when][]- with an? {attr} greater than {amount}={attr} <= {amount!num} [when][]- with a {what} {attr}={attr} {what!positive?>0/negative?%lt;0/zero?==0/ERROR} A file containing a DSL definition is customarily given the extension The DSL must be passed to the Knowledge Builder ahead of any rules file using the DSL. KnowledgeBuilder kBuilder = new KnowledgeBuilder(); Resource dsl = ResourceFactory.newClassPathResource( dslPath, getClass() ); kBuilder.add( dsl, ResourceType.DSL ); Resource dslr = ResourceFactory.newClassPathResource( dslrPath, getClass() ); kBuilder.add( dslr, ResourceType.DSLR ); For parsing and expanding a DSLR file the DSL configuration is read and supplied to the parser. Thus, the parser can "recognize" the DSL expressions and transform them into native rule language expressions. 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 hierarchical views of the XML, ideally visually (commercial tools like XMLSpy, Oxygen etc are good, but cost money, but then so do headache tablets). Other scenarios 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. In the preceding XML text you will see the typical XML element, the package declaration, imports, globals, functions, and the rule itself. Most of the elements are self explanatory if you have some understanding of the Drools features. The The The The rule is discussed below. In the above detail of the rule we see that the rule has 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 as it contains nested elements for conditional elements, constraints and restrictions. A key element of the LHS is the Pattern element. This allows you to specify a type (class) and perhaps bind a variable to an instance of that class. Nested under the pattern object are constraints and restrictions 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 patterns, to check for the existence or nonexistence of a fact meeting the pattern's 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 than the 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 no longer supported by Drools XML parser 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. What is the process of defining two or more methods within same class that have same name but different parameters declaration in Java?If a class has multiple methods having same name but parameters of the method should be different is known as Method Overloading.
What is the process of defining more than one method in a class having the same name but differentiated by method signature?Explanation: Function overloading is a process of defining more than one method in a class with same name differentiated by function signature i:e return type or parameters type and number.
When 2 or more methods in the same class have the same name it is called method?Having two or more methods named the same in the same class is called overloading. It's not overloading if you have the same method name in two different classes.
|