From dd196035cfce2c573092e28df4af2fdb0be22b2e Mon Sep 17 00:00:00 2001 From: Toshiya Kobayashi Date: Mon, 22 Jan 2024 19:18:31 +0900 Subject: [PATCH 1/2] [incubator-kie-drools#5659] Update drools documentation : Bring back traditional DRL syntax explanation --- drools-docs/src/modules/ROOT/nav.adoc | 1 + .../_DSL-section.adoc | 398 +++++++++ .../_drl-declarations-access-con.adoc | 47 ++ .../_drl-declarations-con.adoc | 15 + .../_drl-declarations-enumerative-con.adoc | 23 + .../_drl-declarations-extended-con.adoc | 23 + .../_drl-declarations-metadata-tags-ref.adoc | 297 +++++++ .../_drl-declarations-with-metadata-con.adoc | 43 + ...drl-declarations-without-metadata-con.adoc | 83 ++ .../_drl-functions-con.adoc | 56 ++ .../_drl-globals-con.adoc | 43 + .../_drl-imports-con.adoc | 17 + .../_drl-operator-precedence-ref.adoc | 73 ++ .../_drl-operators-ref.adoc | 244 ++++++ .../_drl-packages-con.adoc | 26 + .../_drl-queries-con.adoc | 162 ++++ .../_drl-rule-units-con.adoc | 603 ++++++++++++++ .../_drl-rules-THEN-advanced-con.adoc | 111 +++ .../_drl-rules-THEN-con.adoc | 19 + .../_drl-rules-THEN-methods-ref.adoc | 128 +++ ..._drl-rules-THEN-methods-variables-ref.adoc | 34 + .../_drl-rules-WHEN-con.adoc | 377 +++++++++ ..._drl-rules-WHEN-elements-diagrams-ref.adoc | 188 +++++ .../_drl-rules-WHEN-elements-ref.adoc | 785 ++++++++++++++++++ .../_drl-rules-comments-con.adoc | 28 + .../_drl-rules-errors-ref.adoc | 207 +++++ .../_drl-rules-oopath-con.adoc | 158 ++++ .../_drl-rules-traditional.adoc | 5 + .../_drl-timers-calendars-con.adoc | 173 ++++ .../language-reference-traditional/index.adoc | 62 ++ .../pages/language-reference/_drl-rules.adoc | 2 + 31 files changed, 4431 insertions(+) create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_DSL-section.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-access-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-enumerative-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-extended-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-metadata-tags-ref.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-with-metadata-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-without-metadata-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-functions-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-globals-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-imports-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-operator-precedence-ref.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-operators-ref.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-packages-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-queries-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rule-units-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-THEN-advanced-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-THEN-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-THEN-methods-ref.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-THEN-methods-variables-ref.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-WHEN-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-WHEN-elements-diagrams-ref.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-WHEN-elements-ref.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-comments-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-errors-ref.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-oopath-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-traditional.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-timers-calendars-con.adoc create mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/index.adoc diff --git a/drools-docs/src/modules/ROOT/nav.adoc b/drools-docs/src/modules/ROOT/nav.adoc index f210e651503..769eff9ce1a 100644 --- a/drools-docs/src/modules/ROOT/nav.adoc +++ b/drools-docs/src/modules/ROOT/nav.adoc @@ -3,6 +3,7 @@ * xref:KIE/index.adoc[leveloffset=+1] * xref:rule-engine/index.adoc[leveloffset=+1] * xref:language-reference/index.adoc[leveloffset=+1] +* xref:language-reference-traditional/index.adoc[leveloffset=+1] * xref:DMN/index.adoc[leveloffset=+1] * xref:pragmatic-ai/index.adoc[leveloffset=+1] * xref:Commands/index.adoc[leveloffset=+1] diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_DSL-section.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_DSL-section.adoc new file mode 100644 index 00000000000..f23dc18a02f --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_DSL-section.adoc @@ -0,0 +1,398 @@ += Domain Specific Languages + + +(((Domain Specific Languages))) +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. + +== When to Use a DSL + + +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 accommodating 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. + +== DSL Basics + + +The Drools DSL mechanism allows you to customise conditional expressions and consequence actions. +A global substitution mechanism ("keyword") is also available. + +.Example (((DSL))) DSL mapping +==== +[source] +---- +[when]Something is {colour}=Something(colour=="{colour}") +---- +==== + + +In the preceding example, `[when]` indicates the scope of the expression, i.e., whether it is valid for the LHS or the RHS of a rule. +The part after the bracketed keyword is the expression that you use in the rule; typically a natural language expression, but it doesn't have to be. +The part to the right of the equal sign ("=") is the mapping of the expression into the rule language. +The form of this string depends on its destination, RHS or LHS. +If it is for the LHS, then it ought to be a term according to the regular LHS syntax; if it is for the RHS then it might be a Java statement. + +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: ``\{colour}``). Then, the values obtained from these captures are then interpolated wherever that name, again enclosed in braces, occurs on the right hand side of the mapping. +Finally, the interpolated string replaces whatever was matched by the entire expression in the line of the DSL rule file. + +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 previous 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 `Something is "green" and + another solid thing` is now correctly expanded. + +.Example with quotes +==== +[source] +---- +[when]something is "{colour}"=Something(colour=="{colour}") +[when]another {state} thing=OtherThing(state=="{state})" +---- +==== + + +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 ("\"): + +[source] +---- +[then]do something= if (foo) \{ doSomething(); \} +---- + +[NOTE] +==== +If braces "{" and "}" should appear in the replacement string of a DSL definition, escape them with a backslash ('\'). +==== + +.Examples of DSL mapping entries +==== +[source] +---- +# 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 = and +---- +==== + + +Given the above DSL examples, the following examples show the expansion of various DSLR snippets: + +.Examples of DSL expansions +==== +[source] +---- +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 "Utah" + ==> Person(name="Bob") and Person(age >= 30, location="Utah") +---- +==== + +[NOTE] +==== +Don'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 ``\n``. + +== Adding Constraints to Facts + + +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``, with the following fields: type, price, age and country. +We can express some LHS condition in normal DRL like the following + +[source] +---- +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. + +[source] +---- +[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: +[source] +---- +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: +[source] +---- +Cheese(age<42, type=='stilton') +---- + +Combining 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.) + +[source] +---- +[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: + +[source] +---- +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 ``<``, and then the line matches the last DSL entry. +This removes the hyphen, but the final result is still added as a constraint to the preceding pattern. +After processing all of the lines, the resulting DRL text is: + +[source] +---- +Cheese(age<42, rating > 50, type=='stilton') +---- + +[NOTE] +==== +The order of the entries in the DSL is important if separate DSL expressions are intended to match the same line, one after the other. +==== + +== Developing a DSL + + +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. + +== DSL and DSLR Reference + + +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 line starting with "#" or "//" (with or without preceding white space) is treated as a comment. A comment line starting with "#/" is scanned for words requesting a debug option, see below. +* Any line starting with an opening bracket ("[") is assumed to be the first line of a DSL entry definition. +* Any other line is appended to the preceding DSL entry definition, with the line end replaced by a space. + + +A DSL entry consists of the following four parts: + +* A scope definition, written as one of the keywords "when" or "condition", "then" or "consequence", "*" and "keyword", enclosed in brackets ("[" and "]"). This indicates whether the DSL entry is valid for the condition or the consequence of a rule, or both. A scope indication of "keyword" means that the entry has global significance, i.e., it is recognized anywhere in a DSLR file. +* A type definition, written as a Java class name, enclosed in brackets. This part is optional unless the next part begins with an opening bracket. An empty pair of brackets is valid, too. +* A DSL expression consists of a (Java) regular expression, with any number of embedded _variable definitions,_ terminated by an equal sign ("="). A variable definition is enclosed in braces ("{" and "}"). It consists of a variable name and two optional attachments, separated by colons (":"). If there is one attachment, it is a regular expression for matching text that is to be assigned to the variable; if there are two attachments, the first one is a hint for the GUI editor and the second one the regular expression. ++ +Note that all characters that are "magic" in regular expressions must be escaped with a preceding backslash ("\") if they should occur literally within the expression. +* The remaining part of the line after the delimiting equal sign is the replacement text for any DSLR text matching the regular expression. It may contain variable references, i.e., a variable name enclosed in braces. Optionally, the variable name may be followed by an exclamation mark ("!") and a transformation function, see below. ++ +Note that braces ("{" and "}") must be escaped with a preceding backslash ("\") if they should occur literally within the replacement string. + + +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. + +.Debug options for DSL expansion +[cols="1,1", options="header"] +|=== +| Word +| Description + +|result +|Prints the resulting DRL text, with line numbers. + +|steps +|Prints each expansion step of condition and consequence + lines. + +|keyword +|Dumps the internal representation of all DSL entries with + scope "keyword". + +|when +|Dumps the internal representation of all DSL entries with + scope "when" or "*". + +|then +|Dumps the internal representation of all DSL entries with + scope "then" or "*". + +|usage +|Displays a usage statistic of all DSL entries. +|=== + + +Below are some sample DSL definitions, with comments describing the language features they illustrate. + +[source] +---- +# 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: + +. The text is read into memory. +. Each of the "keyword" entries is applied to the entire text. First, the regular expression from the keyword definition is modified by replacing white space sequences with a pattern matching any number of white space characters, and by replacing variable definitions with a capture made from the regular expression provided with the definition, or with the default (".*?"). Then, the DSLR text is searched exhaustively for occurrences of strings matching the modified regular expression. Substrings of a matching string corresponding to variable captures are extracted and replace variable references in the corresponding replacement text, and this text replaces the matching string in the DSLR text. +. Sections of the DSLR text between "when" and "then", and "then" and "end", respectively, are located and processed in a uniform manner, line by line, as described below. ++ +For a line, each DSL entry pertaining to the line's section is taken in turn, in the order it appears in the DSL file. +Its regular expression part is modified: white space is replaced by a pattern matching any number of white space characters; variable definitions with a regular expression are replaced by a capture with this regular expression, its default being ".*?". If the resulting regular expression matches all or part of the line, the matched part is replaced by the suitably modified replacement text. ++ +Modification of the replacement text is done by replacing variable references with the text corresponding to the regular expression capture. +This text may be modified according to the string transformation function given in the variable reference; see below for details. ++ +If there is a variable reference naming a variable that is not defined in the same entry, the expander substitutes a value bound to a variable of that name, provided it was defined in one of the preceding lines of the current rule. +. If a DSLR line in a condition is written with a leading hyphen, the expanded result is inserted into the last line, which should contain a pattern CE, i.e., a type name followed by a pair of parentheses. if this pair is empty, the expanded line (which should contain a valid constraint) is simply inserted, otherwise a comma (",") is inserted beforehand. ++ +If a DSLR line in a consequence is written with a leading hyphen, the expanded result is inserted into the last line, which should contain a "modify" statement, ending in a pair of braces ("{" and "}"). If this pair is empty, the expanded line (which should contain a valid method call) is simply inserted, otherwise a comma (",") is inserted beforehand. + + +[NOTE] +==== +It 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. + +.String transformation functions +[cols="1,1", options="header"] +|=== +| Name +| Description + +|uc +|Converts all letters to upper case. + +|lc +|Converts all letters to lower case. + +|ucfirst +|Converts the first letter to upper case, and + all other letters to lower case. + +|num +|Extracts all digits and "-" from the string. If the + last two digits in the original string are preceded by "." or + ",", a decimal period is inserted in the corresponding position. + +|__a__?__b__/__c__ +|Compares the string with string __a__, and if they + are equal, replaces it with __b__, otherwise with + __c__. But _c_ can be another triplet + __a__, __b__, __c__, so + that the entire structure is, in fact, a translation table. +|=== + + +The following DSL examples show how to use string transformation functions. + +[source] +---- +# 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 has to be put under the resources folder or any of its subfolders like any other drools artifact. +It must have the extension ``$$.$$dsl``, or alternatively be marked with type ``ResourceType.DSL``. +when programmatically added to a ``KieFileSystem``. +For a file using DSL definition, the extension `$$.$$dslr` should be used, while it can be added to a `KieFileSystem` with type ``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. + +ifdef::backend-docbook[] +[index] +== Index +// Generated automatically by the DocBook toolchain. +endif::backend-docbook[] diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-access-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-access-con.adoc new file mode 100644 index 00000000000..60316ee6216 --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-access-con.adoc @@ -0,0 +1,47 @@ +[id='drl-declarations-access-con_{context}'] += Access to DRL declared types in application code + +Declared types in DRL are typically used within the DRL files while Java models are typically used when the model is shared between rules and applications. Because declared types are generated at KIE base compile time, an application cannot access them until application run time. In some cases, an application needs to access and handle facts directly from the declared types, especially when the application wraps the {RULE_ENGINE} and provides higher-level, domain-specific user interfaces for rules management. + +To handle declared types directly from the application code, you can use the `org.drools.definition.type.FactType` API in {PRODUCT}. Through this API, you can instantiate, read, and write fields in the declared fact types. + +The following example code modifies a `Person` fact type directly from an application: + +.Example application code to handle a declared fact type through the FactType API +[source,java] +---- +import java.util.Date; + +import org.kie.api.definition.type.FactType; +import org.kie.api.KieBase; +import org.kie.api.runtime.KieSession; + +... + +// Get a reference to a KIE base with the declared type: +KieBase kbase = ... + +// Get the declared fact type: +FactType personType = kbase.getFactType("org.drools.examples", "Person"); + +// Create instances: +Object bob = personType.newInstance(); + +// Set attribute values: +personType.set(bob, "name", "Bob" ); +personType.set(bob, "dateOfBirth", new Date()); +personType.set(bob, "address", new Address("King's Road","London","404")); + +// Insert the fact into a KIE session: +KieSession ksession = ... +ksession.insert(bob); +ksession.fireAllRules(); + +// Read attributes: +String name = (String) personType.get(bob, "name"); +Date date = (Date) personType.get(bob, "dateOfBirth"); +---- + +The API also includes other helpful methods, such as setting all the attributes at once, reading values from a `Map` collection, or reading all attributes at once into a `Map` collection. + +Although the API behavior is similar to Java reflection, the API does not use reflection and relies on more performant accessors that are implemented with generated bytecode. diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-con.adoc new file mode 100644 index 00000000000..2894d191fff --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-con.adoc @@ -0,0 +1,15 @@ +[id='drl-declarations-con_{context}'] += Type declarations and metadata in DRL + +ifdef::DROOLS,JBPM,OP[] +.Type declaration +image::language-reference/type_declaration.png[align="center"] + +.Metadata +image::language-reference/meta_data.png[align="center"] +endif::[] + +Declarations in DRL files define new fact types or metadata for fact types to be used by rules in the DRL file: + +* *New fact types:* The default fact type in the `java.lang` package of {PRODUCT} is `Object`, but you can declare other types in DRL files as needed. Declaring fact types in DRL files enables you to define a new fact model directly in the {RULE_ENGINE}, without creating models in a lower-level language like Java. You can also declare a new type when a domain model is already built and you want to complement this model with additional entities that are used mainly during the reasoning process. +* *Metadata for fact types:* You can associate metadata in the format `@key(value)` with new or existing facts. Metadata can be any kind of data that is not represented by the fact attributes and is consistent among all instances of that fact type. The metadata can be queried at run time by the {RULE_ENGINE} and used in the reasoning process. diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-enumerative-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-enumerative-con.adoc new file mode 100644 index 00000000000..d1dc314537f --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-enumerative-con.adoc @@ -0,0 +1,23 @@ +[id='drl-declarations-enumerative-con_{context}'] += Enumerative type declarations in DRL + +DRL supports the declaration of enumerative types in the format `declare enum `, followed by a comma-separated list of values ending with a semicolon. You can then use the enumerative list in the rules in the DRL file. + +For example, the following enumerative type declaration defines days of the week for an employee scheduling rule: + +.Example enumerative type declaration with a scheduling rule +[source] +---- +declare enum DaysOfWeek + SUN("Sunday"),MON("Monday"),TUE("Tuesday"),WED("Wednesday"),THU("Thursday"),FRI("Friday"),SAT("Saturday"); + + fullName : String +end + +rule "Using a declared Enum" +when + $emp : Employee( dayOff == DaysOfWeek.MONDAY ) +then + ... +end +---- diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-extended-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-extended-con.adoc new file mode 100644 index 00000000000..351d72825e2 --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-extended-con.adoc @@ -0,0 +1,23 @@ +[id='drl-declarations-extended-con_{context}'] += Extended type declarations in DRL + +DRL supports type declaration inheritance in the format `declare extends `. To extend a type declared in Java by a subtype declared in DRL, you repeat the parent type in a declaration statement without any fields. + +For example, the following type declarations extend a `Student` type from a top-level `Person` type, and a `LongTermStudent` type from the `Student` subtype: + +.Example extended type declarations +[source] +---- +import org.people.Person + +declare Person end + +declare Student extends Person + school : String +end + +declare LongTermStudent extends Student + years : int + course : String +end +---- diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-metadata-tags-ref.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-metadata-tags-ref.adoc new file mode 100644 index 00000000000..8327e8e114a --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-metadata-tags-ref.adoc @@ -0,0 +1,297 @@ +[id='drl-declarations-metadata-tags-ref_{context}'] + +ifeval::["{context}" == "decision-engine"] += Metadata tags for events + +The {RULE_ENGINE} uses the following metadata tags for events that are inserted into the working memory of the {RULE_ENGINE}. You can change the default metadata tag values in your Java class or DRL rule file as needed. +endif::[] +ifeval::["{context}" == "drl-rules"] += Metadata tags for fact type and attribute declarations in DRL + +Although you can define custom metadata attributes in DRL declarations, the {RULE_ENGINE} also supports the following predefined metadata tags for declarations of fact types or fact type attributes. +endif::[] + +[NOTE] +==== +The examples in this section that refer to the `VoiceCall` class assume that the sample application domain model includes the following class details: + +.VoiceCall fact class in an example Telecom domain model +[source,java] +---- +public class VoiceCall { + private String originNumber; + private String destinationNumber; + private Date callDateTime; + private long callDuration; // in milliseconds + + // Constructors, getters, and setters +} +---- +==== + +@role:: +This tag determines whether a given fact type is handled as a regular fact or an event in the {RULE_ENGINE} during complex event processing. ++ +-- +Default parameter: `fact` + +Supported parameters: `fact`, `event` + +[source] +---- +@role( fact | event ) +---- + +.Example: Declare VoiceCall as event type +[source] +---- +declare VoiceCall + @role( event ) +end +---- +-- + +@timestamp:: +This tag is automatically assigned to every event in the {RULE_ENGINE}. By default, the time is provided by the session clock and assigned to the event when it is inserted into the working memory of the {RULE_ENGINE}. You can specify a custom time stamp attribute instead of the default time stamp added by the session clock. ++ +-- +Default parameter: The time added by the {RULE_ENGINE} session clock + +Supported parameters: Session clock time or custom time stamp attribute + +[source] +---- +@timestamp( ) +---- + +.Example: Declare VoiceCall timestamp attribute +[source] +---- +declare VoiceCall + @role( event ) + @timestamp( callDateTime ) +end +---- +-- + +@duration:: +This tag determines the duration time for events in the {RULE_ENGINE}. Events can be interval-based events or point-in-time events. Interval-based events have a duration time and persist in the working memory of the {RULE_ENGINE} until their duration time has lapsed. Point-in-time events have no duration and are essentially interval-based events with a duration of zero. By default, every event in the {RULE_ENGINE} has a duration of zero. You can specify a custom duration attribute instead of the default. ++ +-- +Default parameter: Null (zero) + +Supported parameters: Custom duration attribute + +[source] +---- +@duration( ) +---- + +.Example: Declare VoiceCall duration attribute +[source] +---- +declare VoiceCall + @role( event ) + @timestamp( callDateTime ) + @duration( callDuration ) +end +---- +-- + +@expires:: +This tag determines the time duration before an event expires in the working memory of the {RULE_ENGINE}. By default, an event expires when the event can no longer match and activate any of the current rules. You can define an amount of time after which an event should expire. This tag definition also overrides the implicit expiration offset calculated from temporal constraints and sliding windows in the KIE base. This tag is available only when the {RULE_ENGINE} is running in stream mode. ++ +-- +Default parameter: Null (event expires after event can no longer match and activate rules) + +Supported parameters: Custom `timeOffset` attribute in the format `[##d][#h][#m][#s][#[ms]]` +// I had to put two #'s above for it to render only one. Otherwise removed the # from [d]. (Stetson, 28 May 2019) + +[source] +---- +@expires( ) +---- + +.Example: Declare expiration offset for VoiceCall events +[source] +---- +declare VoiceCall + @role( event ) + @timestamp( callDateTime ) + @duration( callDuration ) + @expires( 1h35m ) +end +---- +-- + +ifeval::["{context}" == "drl-rules"] +@typesafe:: +This tab determines whether a given fact type is compiled with or without type safety. By default, all type declarations are compiled with type safety enabled. You can override this behavior to type-unsafe evaluation, where all constraints are generated as MVEL constraints and executed dynamically. This is useful when dealing with collections that do not have any generics or mixed type collections. ++ +-- +Default parameter: `true` + +Supported parameters: `true`, `false` + +[source] +---- +@typesafe( ) +---- + +.Example: Declare VoiceCall for type-unsafe evaluation +[source] +---- +declare VoiceCall + @role( fact ) + @typesafe( false ) +end +---- +-- + +@serialVersionUID:: +This tag defines an identifying `serialVersionUID` value for a serializable class in a fact declaration. If a serializable class does not explicitly declare a `serialVersionUID`, the serialization run time calculates a default `serialVersionUID` value for that class based on various aspects of the class, as described in the https://docs.oracle.com/javase/10/docs/specs/serialization/index.html[Java Object Serialization Specification]. However, for optimal deserialization results and for greater compatibility with serialized KIE sessions, set the `serialVersionUID` as needed in the relevant class or in your DRL declarations. ++ +-- +Default parameter: Null + +Supported parameters: Custom `serialVersionUID` integer + +[source] +---- +@serialVersionUID( ) +---- + +.Example: Declare serialVersionUID for a VoiceCall class +[source] +---- +declare VoiceCall + @serialVersionUID( 42 ) +end +---- +-- + +@key:: +This tag enables a fact type attribute to be used as a key identifier for the fact type. The generated class can then implement the `equals()` and `hashCode()` methods to determine if two instances of the type are equal to each other. The {RULE_ENGINE} can also generate a constructor using all the key attributes as parameters. ++ +-- +Default parameter: None + +Supported parameters: None + +[source] +---- + @key +---- + +.Example: Declare Person type attributes as keys +[source] +---- +declare Person + firstName : String @key + lastName : String @key + age : int +end +---- + +For this example, the {RULE_ENGINE} checks the `firstName` and `lastName` attributes to determine if two instances of `Person` are equal to each other, but it does not check the `age` attribute. The {RULE_ENGINE} also implicitly generates three constructors: one without parameters, one with the `@key` fields, and one with all fields: + +.Example constructors from the key declarations +[source] +---- +Person() // Empty constructor + +Person( String firstName, String lastName ) + +Person( String firstName, String lastName, int age ) +---- + +You can then create instances of the type based on the key constructors, as shown in the following example: + +.Example instance using the key constructor +[source,java] +---- +Person person = new Person( "John", "Doe" ); +---- +-- + +@position:: +This tag determines the position of a declared fact type attribute or field in a positional argument, overriding the default declared order of attributes. You can use this tag to modify positional constraints in patterns while maintaining a consistent format in your type declarations and positional arguments. You can use this tag only for fields in classes on the classpath. If some fields in a single class use this tag and some do not, the attributes without this tag are positioned last, in the declared order. Inheritance of classes is supported, but not interfaces of methods. ++ +-- +Default parameter: None + +Supported parameters: Any integer + +[source] +---- + @position ( ) +---- + +.Example: Declare a fact type and override declared order +[source] +---- +declare Person + firstName : String @position( 1 ) + lastName : String @position( 0 ) + age : int @position( 2 ) + occupation: String +end +---- + +In this example, the attributes are prioritized in positional arguments in the following order: + +. `lastName` +. `firstName` +. `age` +. `occupation` + +In positional arguments, you do not need to specify the field name because the position maps to a known named field. For example, the argument `Person( lastName == "Doe" )` is the same as `Person( "Doe"; )`, where the `lastName` field has the highest position annotation in the DRL declaration. The semicolon `;` indicates that everything before it is a positional argument. You can mix positional and named arguments on a pattern by using the semicolon to separate them. Any variables in a positional argument that have not yet been bound are bound to the field that maps to that position. + +The following example patterns illustrate different ways of constructing positional and named arguments. The patterns have two constraints and a binding, and the semicolon differentiates the positional section from the named argument section. Variables and literals and expressions using only literals are supported in positional arguments, but not variables alone. + +.Example patterns with positional and named arguments +[source] +---- +Person( "Doe", "John", $a; ) + +Person( "Doe", "John"; $a : age ) + +Person( "Doe"; firstName == "John", $a : age ) + +Person( lastName == "Doe"; firstName == "John", $a : age ) +---- + +Positional arguments can be classified as _input arguments_ or _output arguments_. Input arguments contain a previously declared binding and constrain against that binding using unification. Output arguments generate the declaration and bind it to the field represented by the positional argument when the binding does not yet exist. + +In extended type declarations, use caution when defining `@position` annotations because the attribute positions are inherited in subtypes. This inheritance can result in a mixed attribute order that can be confusing in some cases. Two fields can have the same `@position` value and consecutive values do not need to be declared. If a position is repeated, the conflict is solved using inheritance, where position values in the parent type have precedence, and then using the declaration order from the first to last declaration. + +For example, the following extended type declarations result in mixed positional priorities: + +.Example extended fact type with mixed position annotations +[source] +---- +declare Person + firstName : String @position( 1 ) + lastName : String @position( 0 ) + age : int @position( 2 ) + occupation: String +end + +declare Student extends Person + degree : String @position( 1 ) + school : String @position( 0 ) + graduationDate : Date +end +---- + +In this example, the attributes are prioritized in positional arguments in the following order: + +. `lastName` (position 0 in the parent type) +. `school` (position 0 in the subtype) +. `firstName` (position 1 in the parent type) +. `degree` (position 1 in the subtype) +. `age` (position 2 in the parent type) +. `occupation` (first field with no position annotation) +. `graduationDate` (second field with no position annotation) +-- +endif::[] diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-with-metadata-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-with-metadata-con.adoc new file mode 100644 index 00000000000..0a2abb2d26e --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-with-metadata-con.adoc @@ -0,0 +1,43 @@ +[id='drl-declarations-with-metadata-con_{context}'] += Type declarations with metadata in DRL + +You can associate metadata in the format `@key(value)` (the value is optional) with fact types or fact attributes. Metadata can be any kind of data that is not represented by the fact attributes and is consistent among all instances of that fact type. The metadata can be queried at run time by the {RULE_ENGINE} and used in the reasoning process. Any metadata that you declare before the attributes of a fact type are assigned to the fact type, while metadata that you declare after an attribute are assigned to that particular attribute. + +In the following example, the two metadata attributes `@author` and `@dateOfCreation` are declared for the `Person` fact type, and the two metadata items `@key` and `@maxLength` are declared for the `name` attribute. The `@key` metadata attribute has no required value, so the parentheses and the value are omitted. + +.Example metadata declaration for fact types and attributes +[source] +---- +import java.util.Date + +declare Person + @author( Bob ) + @dateOfCreation( 01-Feb-2009 ) + + name : String @key @maxLength( 30 ) + dateOfBirth : Date + address : Address +end +---- + +For declarations of metadata attributes for existing types, you can identify the fully qualified class name as part of the `import` clause for all declarations or as part of the individual `declare` clause: + +.Example metadata declaration for an imported type +[source] +---- +import org.drools.examples.Person + +declare Person + @author( Bob ) + @dateOfCreation( 01-Feb-2009 ) +end +---- + +.Example metadata declaration for a declared type +[source] +---- +declare org.drools.examples.Person + @author( Bob ) + @dateOfCreation( 01-Feb-2009 ) +end +---- diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-without-metadata-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-without-metadata-con.adoc new file mode 100644 index 00000000000..5bf64f3fa1c --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-declarations-without-metadata-con.adoc @@ -0,0 +1,83 @@ +[id='drl-declarations-without-metadata-con_{context}'] += Type declarations without metadata in DRL + +A declaration of a new fact does not require any metadata, but must include a list of attributes or fields. If a type declaration does not include identifying attributes, the {RULE_ENGINE} searches for an existing fact class in the classpath and raises an error if the class is missing. + +The following example is a declaration of a new fact type `Person` with no metadata in a DRL file: + +.Example declaration of a new fact type with a rule +[source] +---- +declare Person + name : String + dateOfBirth : java.util.Date + address : Address +end + +rule "Using a declared type" + when + $p : Person( name == "James" ) + then // Insert Mark, who is a customer of James. + Person mark = new Person(); + mark.setName( "Mark" ); + insert( mark ); +end +---- + +In this example, the new fact type `Person` has the three attributes `name`, `dateOfBirth`, and `address`. Each attribute has a type that can be any valid Java type, including another class that you create or a fact type that you previously declared. The `dateOfBirth` attribute has the type `java.util.Date`, from the Java API, and the `address` attribute has the previously defined fact type `Address`. + +To avoid writing the fully qualified name of a class every time you declare it, you can define the full class name as part of the `import` clause: + +.Example type declaration with the fully qualified class name in the import +[source] +---- +import java.util.Date + +declare Person + name : String + dateOfBirth : Date + address : Address +end +---- + +When you declare a new fact type, the {RULE_ENGINE} generates at compile time a Java class representing the fact type. The generated Java class is a one-to-one JavaBeans mapping of the type definition. + +For example, the following Java class is generated from the example `Person` type declaration: + +.Generated Java class for the Person fact type declaration +[source,java] +---- +public class Person implements Serializable { + private String name; + private java.util.Date dateOfBirth; + private Address address; + + // Empty constructor + public Person() {...} + + // Constructor with all fields + public Person( String name, Date dateOfBirth, Address address ) {...} + + // If keys are defined, constructor with keys + public Person( ...keys... ) {...} + + // Getters and setters + // `equals` and `hashCode` + // `toString` +} +---- + +You can then use the generated class in your rules like any other fact, as illustrated in the previous rule example with the `Person` type declaration: + +.Example rule that uses the declared Person fact type +[source] +---- +rule "Using a declared type" + when + $p : Person( name == "James" ) + then // Insert Mark, who is a customer of James. + Person mark = new Person(); + mark.setName( "Mark" ); + insert( mark ); +end +---- diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-functions-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-functions-con.adoc new file mode 100644 index 00000000000..46995f85b37 --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-functions-con.adoc @@ -0,0 +1,56 @@ +[id='drl-functions-con_{context}'] += Functions in DRL + +ifdef::DROOLS,JBPM,OP[] +.Function +image::language-reference/function.png[align="center"] +endif::[] + +Functions in DRL files put semantic code in your rule source file instead of in Java classes. Functions are especially useful if an action (`then`) part of a rule is used repeatedly and only the parameters differ for each rule. Above the rules in the DRL file, you can declare the function or import a static method from a helper class as a function, and then use the function by name in an action (`then`) part of the rule. + +The following examples illustrate a function that is either declared or an imported static method in a DRL file: + +.Example function declaration with a rule (option 1) +[source] +---- +function String hello(String applicantName) { + return "Hello " + applicantName + "!"; +} + +rule "Using a function" + when + // Empty + then + System.out.println( hello( "James" ) ); +end +---- + +.Example import a static method of a Java class (option 2) +[source,java] +---- +package org.example.applicant; + +public class MyFunctions { + + public static String hello(String applicantName) { + return "Hello " + applicantName + "!"; + } +} +---- + +[source] +---- +import static org.example.applicant.MyFunctions.hello; + +rule "Using a function" + when + // Empty + then + System.out.println( hello( "James" ) ); +end +---- + +[NOTE] +==== +A function declared in a DRL file cannot be imported to a rule in a different package while a Java static method in a different package can be imported. +==== diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-globals-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-globals-con.adoc new file mode 100644 index 00000000000..d189ff21dbc --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-globals-con.adoc @@ -0,0 +1,43 @@ +[id='drl-globals-con_{context}'] += Global variables in DRL + +ifdef::DROOLS,JBPM,OP[] +.Global +image::language-reference/global.png[align="center"] +endif::[] + +Global variables in DRL files typically provide data or services for the rules, such as application services used in rule consequences, and return data from rules, such as logs or values added in rule consequences. You set the global value in the working memory of the {RULE_ENGINE} through a KIE session configuration or REST operation, declare the global variable above the rules in the DRL file, and then use it in an action (`then`) part of the rule. For multiple global variables, use separate lines in the DRL file. + +The following example illustrates a global variable list configuration for the {RULE_ENGINE} and the corresponding global variable definition in the DRL file: + +.Example global list configuration for the {RULE_ENGINE} +[source] +---- +List list = new ArrayList<>(); +KieSession kieSession = kiebase.newKieSession(); +kieSession.setGlobal( "myGlobalList", list ); +---- + +.Example global variable definition with a rule +[source] +---- +global java.util.List myGlobalList; + +rule "Using a global" + when + // Empty + then + myGlobalList.add( "My global list" ); +end +---- + +[WARNING] +==== +Do not use global variables to establish conditions in rules unless a global variable has a constant immutable value. Global variables are not inserted into the working memory of the {RULE_ENGINE}, so the {RULE_ENGINE} cannot track value changes of variables. + +Do not use global variables to share data between rules. 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 of the {RULE_ENGINE}. +==== + +A use case for a global variable might be an instance of an 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 of the {RULE_ENGINE}. In the DRL file, you declare that you have a global of type `emailService` and give it the name `"email"`, and then in your rule consequences, you can use actions such as `email.sendSMS(number, message)`. + +If you declare global variables with the same identifier in multiple packages, then you must set all the packages with the same type so that they all reference the same global value. diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-imports-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-imports-con.adoc new file mode 100644 index 00000000000..ec8277cf632 --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-imports-con.adoc @@ -0,0 +1,17 @@ +[id='drl-imports-con_{context}'] += Import statements in DRL + +ifdef::DROOLS,JBPM,OP[] +.Import +image::language-reference/import.png[align="center"] +endif::[] + +Similar to import statements in Java, imports in DRL files identify the fully qualified paths and type names for any objects that you want to use in the rules. You specify the package and data object in the format `packageName.objectName`, with multiple imports on separate lines. The {RULE_ENGINE} automatically imports classes from the Java package with the same name as the DRL package and from the package `java.lang`. + +The following example is an import statement for a loan application object in a mortgage application decision service: + +.Example import statement in a DRL file +[source] +---- +import org.mortgages.LoanApplication; +---- diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-operator-precedence-ref.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-operator-precedence-ref.adoc new file mode 100644 index 00000000000..25e0fa007a1 --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-operator-precedence-ref.adoc @@ -0,0 +1,73 @@ +[id='drl-operator-precedence-ref_{context}'] += Operator precedence in DRL pattern constraints + +DRL supports standard Java operator precedence for applicable constraint operators, with some exceptions and with some additional operators that are unique in DRL. The following table lists DRL operator precedence where applicable, from highest to lowest precedence: + +.Operator precedence in DRL pattern constraints +[cols="2,2,3", options="header"] +|=== +|Operator type +|Operators +|Notes + +|Nested or null-safe property access +|`.`, `.()`, `!.` +|Not standard Java semantics + +|`List` or `Map` access +|`[]` +|Not standard Java semantics + +|Constraint binding +|`:` +|Not standard Java semantics + +|Multiplicative +|`*`, `/%` +| + +|Additive +|`+`, `-` +| + +|Shift +|`>>`, `>>>`, `<<` +| + +|Relational +|`<`, `\<=`, `>`, `>=`, `instanceof` +| + +|Equality +|`== !=` +|Uses `equals()` and `!equals()` semantics, not standard Java `same` and `not same` semantics + + +|Non-short-circuiting `AND` +|`&` +| + +| Non-short-circuiting exclusive `OR` +|`^` +| + +|Non-short-circuiting inclusive `OR` +|`\|` +| + +|Logical `AND` +|`&&` +| + +|Logical `OR` +|`\|\|` +| + +|Ternary +|`? :` +| + +|Comma-separated `AND` +|`,` +|Not standard Java semantics +|=== diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-operators-ref.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-operators-ref.adoc new file mode 100644 index 00000000000..2defce96958 --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-operators-ref.adoc @@ -0,0 +1,244 @@ +[id='drl-operators-ref_{context}'] + += Supported operators in DRL pattern constraints + +DRL supports standard Java semantics for operators in pattern constraints, with some exceptions and with some additional operators that are unique in DRL. The following list summarizes the operators that are handled differently in DRL constraints than in standard Java semantics or that are unique in DRL constraints. + +`.()`, `#`:: +Use the `.()` operator to group property accessors to nested objects, and use the `#` operator to cast to a subtype in nested objects. Casting to a subtype makes the getters from the parent type available to the subtype. You can use either the object name or fully qualified class name, and you can cast to one or multiple subtypes. ++ +-- +.Example patterns with nested objects +[source] +---- +// Ungrouped property accessors: +Person( name == "mark", address.city == "london", address.country == "uk" ) + +// Grouped property accessors: +Person( name == "mark", address.( city == "london", country == "uk") ) +---- + +NOTE: The period prefix `.` differentiates the nested object constraints from a method call. + +.Example patterns with inline casting to a subtype +[source] +---- +// Inline casting with subtype name: +Person( name == "mark", address#LongAddress.country == "uk" ) + +// Inline casting with fully qualified class name: +Person( name == "mark", address#org.domain.LongAddress.country == "uk" ) + +// Multiple inline casts: +Person( name == "mark", address#LongAddress.country#DetailedCountry.population > 10000000 ) +---- +-- + +`!.`:: +Use this operator to dereference a property in a null-safe way. The value to the left of the `!.` operator must be not null (interpreted as `!= null`) in order to give a positive result for pattern matching. ++ +-- +.Example constraint with null-safe dereferencing +[source] +---- +Person( $streetName : address!.street ) + +// This is internally rewritten in the following way: + +Person( address != null, $streetName : address.street ) +---- +-- + +`[]`:: +Use this operator to access a `List` value by index or a `Map` value by key. ++ +-- +.Example constraints with `List` and `Map` access +[source] +---- +// The following format is the same as `childList(0).getAge() == 18`: +Person(childList[0].age == 18) + +// The following format is the same as `credentialMap.get("jdoe").isValid()`: +Person(credentialMap["jdoe"].valid) +---- +-- + +`<`, `\<=`, `>`, `>=`:: +Use these operators on properties with natural ordering. For example, for `Date` fields, the `<` operator means _before_, and for `String` fields, the operator means _alphabetically before_. These properties apply only to comparable properties. ++ +-- +.Example constraints with `before` operator +[source] +---- +Person( birthDate < $otherBirthDate ) + +Person( firstName < $otherFirstName ) +---- +-- + +`==`, `!=`:: +Use these operators as `equals()` and `!equals()` methods in constraints, instead of the usual `same` and `not same` semantics. ++ +-- +.Example constraint with null-safe equality +[source] +---- +Person( firstName == "John" ) + +// This is similar to the following formats: + +java.util.Objects.equals(person.getFirstName(), "John") +"John".equals(person.getFirstName()) +---- + +.Example constraint with null-safe not equality +[source] +---- +Person( firstName != "John" ) + +// This is similar to the following format: + +!java.util.Objects.equals(person.getFirstName(), "John") +---- +-- + +`&&`, `||`:: +Use these operators to create an abbreviated combined relation condition that adds more than one restriction on a field. You can group constraints with parentheses `()` to create a recursive syntax pattern. ++ +-- +.Example constraints with abbreviated combined relation +[source] +---- +// 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") +---- + +ifdef::DROOLS,JBPM,OP[] +.Abbreviated combined relation condition +image::language-reference/abbreviatedCombinedRelationCondition.png[align="center"] + +.Abbreviated combined relation condition withparentheses +image::language-reference/abbreviatedCombinedRelationConditionGroup.png[align="center"] +endif::[] +-- + +`matches`, `not matches`:: +Use these operators to indicate that a field matches or does not match a specified Java regular expression. Typically, the regular expression is a `String` literal, but variables that resolve to a valid regular expression are also supported. These operators apply only to `String` properties. If you use `matches` against a `null` value, the resulting evaluation is always `false`. If you use `not matches` against a `null` value, the resulting evaluation is always `true`. As in Java, regular expressions that you write as `String` literals must use a double backslash `\\` to escape. ++ +-- +.Example constraint to match or not match a regular expression +[source] +---- +Person( country matches "(USA)?\\S*UK" ) + +Person( country not matches "(USA)?\\S*UK" ) +---- +-- + +`contains`, `not contains`:: +Use these operators to verify whether a field that is an `Array` or a `Collection` contains or does not contain a specified value. These operators apply to `Array` or `Collection` properties, but you can also use these operators in place of `String.contains()` and `!String.contains()` constraints checks. ++ +-- +.Example constraints with `contains` and `not contains` for a Collection +[source] +---- +// Collection with a specified field: +FamilyTree( countries contains "UK" ) + +FamilyTree( countries not contains "UK" ) + + +// Collection with a variable: +FamilyTree( countries contains $var ) + +FamilyTree( countries not contains $var ) +---- + +.Example constraints with `contains` and `not contains` for a String literal +[source] +---- +// Sting literal with a specified field: +Person( fullName contains "Jr" ) + +Person( fullName not contains "Jr" ) + + +// String literal with a variable: +Person( fullName contains $var ) + +Person( fullName not contains $var ) +---- + +NOTE: For backward compatibility, the `excludes` operator is a supported synonym for `not contains`. + +-- + +`memberOf`, `not memberOf`:: +Use these operators to verify whether a field is a member of or is not a member of an `Array` or a `Collection` that is defined as a variable. The `Array` or `Collection` must be a variable. ++ +-- +.Example constraints with `memberOf` and `not memberOf` with a Collection +[source] +---- +FamilyTree( person memberOf $europeanDescendants ) + +FamilyTree( person not memberOf $europeanDescendants ) +---- +-- + +`soundslike`:: +Use this operator to verify whether a word has almost the same sound, using English pronunciation, as the given value (similar to the `matches` operator). This operator uses the Soundex algorithm. ++ +-- +.Example constraint with `soundslike` +[source] +---- +// Match firstName "Jon" or "John": +Person( firstName soundslike "John" ) +---- +-- + +`str`:: +Use this operator to verify whether a field that is a `String` starts with or ends with a specified value. You can also use this operator to verify the length of the `String`. ++ +-- +.Example constraints with `str` +[source] +---- +// Verify what the String starts with: +Message( routingValue str[startsWith] "R1" ) + +// Verify what the String ends with: +Message( routingValue str[endsWith] "R2" ) + +// Verify the length of the String: +Message( routingValue str[length] 17 ) +---- +-- + +`in`, `notin`:: +Use these operators to specify more than one possible value to match in a constraint (compound value restriction). This functionality of compound value restriction is supported only in the `in` and `not in` operators. The second operand of these operators must be a comma-separated list of values enclosed in parentheses. You can provide values as variables, literals, return values, or qualified identifiers. These operators are internally rewritten as a list of multiple restrictions using the operators `==` or `!=`. ++ +-- +ifdef::DROOLS,JBPM,OP[] +.compoundValueRestriction +image::language-reference/compoundValueRestriction.png[align="center"] +endif::[] + +.Example constraints with `in` and `notin` +[source] +---- +Person( $color : favoriteColor ) +Color( type in ( "red", "blue", $color ) ) + +Person( $color : favoriteColor ) +Color( type notin ( "red", "blue", $color ) ) +---- +-- diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-packages-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-packages-con.adoc new file mode 100644 index 00000000000..11afed8471d --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-packages-con.adoc @@ -0,0 +1,26 @@ +[id='drl-packages-con_{context}'] += Packages in DRL + +A package is a folder of related assets in {PRODUCT}, such as data objects, DRL files, decision tables, and other asset types. A package also serves as a unique namespace for each group of rules. A single rule base can contain multiple packages. You typically store all the rules for a package in the same file as the package declaration so that the package is self-contained. However, you can import objects from other packages that you want to use in the rules. + +The following example is a package name and namespace for a DRL file in a mortgage application decision service: + +.Example package definition in a DRL file +[source] +---- +package org.mortgages; +---- + +ifdef::DROOLS,JBPM,OP[] +The following railroad diagram shows all the components that may make up a package: + +.Package +image::language-reference/package.png[align="center"] + +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` statement, which must be at the top of the file. +In all cases, the semicolons are optional. + +Notice that any rule attribute (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. +endif::[] diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-queries-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-queries-con.adoc new file mode 100644 index 00000000000..24c9afc4845 --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-queries-con.adoc @@ -0,0 +1,162 @@ +[id='drl-queries-con_{context}'] += Queries in DRL + +ifdef::DROOLS,JBPM,OP[] +.Query +image::language-reference/query.png[align="center"] +endif::[] + +Queries in DRL files search the working memory of the {RULE_ENGINE} for facts related to the rules in the DRL file. You add the query definitions in DRL files and then obtain the matching results in your application code. Queries search for a set of defined conditions and do not require `when` or `then` specifications. Query names are global to the KIE base and therefore must be unique among all other rule queries in the project. To return the results of a query, you construct a `QueryResults` definition using `ksession.getQueryResults("name")`, where `"name"` is the query name. This returns a list of query results, which enable you to retrieve the objects that matched the query. You define the query and query results parameters above the rules in the DRL file. + +The following example is a query definition in a DRL file for underage applicants in a mortgage application decision service, with the accompanying application code: + +.Example query definition in a DRL file +[source] +---- +query "people under the age of 21" + $person : Person( age < 21 ) +end +---- + +.Example application code to obtain query results +[source,java] +---- +QueryResults results = ksession.getQueryResults( "people under the age of 21" ); +System.out.println( "we have " + results.size() + " people under the age of 21" ); +---- + +You can also iterate over the returned `QueryResults` using a standard `for` loop. Each element is a `QueryResultsRow` that you can use to access each of the columns in the tuple. + +.Example application code to obtain and iterate over query results +[source,java] +---- +QueryResults results = ksession.getQueryResults( "people under the age of 21" ); +System.out.println( "we have " + results.size() + " people under the age of 21" ); + +System.out.println( "These people are under the age of 21:" ); + +for ( QueryResultsRow row : results ) { + Person person = ( Person ) row.get( "person" ); + System.out.println( person.getName() + "\n" ); +} +---- + +ifdef::DROOLS,JBPM,OP[] +Support for positional syntax has been added for more compact code. +By default the declared type order in the type declaration matches the argument position. +But it possible to override these using the @position annotation. +This allows patterns to be used with positional arguments, instead of the more verbose named arguments. + +[source] +---- +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. +Inheritance of classes is supported, but not interfaces or methods. +The isContainedIn query below demonstrates the use of positional arguments in a pattern; `Location(x, y;)` instead of `Location( thing == x, location == y).` + +Queries can now call other queries, this combined with optional query arguments provides derivation query style backward chaining. +Positional and named syntax is supported for arguments. +It is also possible to mix both positional and named, but positional must come first, separated by a semi colon. +Literal expressions can be passed as query arguments, but at this stage you cannot mix expressions with variables. +Here is 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 receive further results as the underlying data changes. + +[source] +---- +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 '?'. + +[source] +---- +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.core.runtime.rule.Variable.v - note you must use 'v' and not an alternative instance of 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. + +[source] +---- +results = ksession.getQueryResults( "isContainedIn", new Object[] { Variable.v, "office" } ); +l = new ArrayList>(); +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. + +It is also possible to use as input argument for a query both the field of a fact as in: + +[source] +---- +query contains(String $s, String $c) + $s := String( this.contains( $c ) ) +end + +rule PersonNamesWithA when + $p : Person() + contains( $p.name, "a"; ) +then +end +---- + +and more in general any kind of valid expression like in: + +[source] +---- +query checkLength(String $s, int $l) + $s := String( length == $l ) +end + +rule CheckPersonNameLength when + $i : Integer() + $p : Person() + checkLength( $p.name, 1 + $i + $p.age; ) +then +end +---- + + +The following is not yet supported: + +* List and Map unification +* Expression unification - pred( X, X + 1, X * Y / 7 ) +endif::[] diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rule-units-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rule-units-con.adoc new file mode 100644 index 00000000000..a907b88ee2d --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rule-units-con.adoc @@ -0,0 +1,603 @@ +[id='drl-rule-units-con_{context}'] + += Rule units in DRL rule sets + +WARNING: Rule units are experimental in Drools 7. Only supported in {KOGITO}. + +Rule units are groups of data sources, global variables, and DRL rules that function together for a specific purpose. You can use rule units to partition a rule set into smaller units, bind different data sources to those units, and then execute the individual unit. Rule units are an enhanced alternative to rule-grouping DRL attributes such as rule agenda groups or activation groups for execution control. + +Rule units are helpful when you want to coordinate rule execution so that the complete execution of one rule unit triggers the start of another rule unit and so on. For example, assume that you have a set of rules for data enrichment, another set of rules that processes that data, and another set of rules that extract the output from the processed data. If you add these rule sets into three distinct rule units, you can coordinate those rule units so that complete execution of the first unit triggers the start of the second unit and the complete execution of the second unit triggers the start of third unit. + +To define a rule unit, implement the `RuleUnit` interface as shown in the following example: + +.Example rule unit class +[source,java] +---- +package org.mypackage.myunit; + +public static class AdultUnit implements RuleUnit { + private int adultAge; + private DataSource persons; + + public AdultUnit( ) { } + + public AdultUnit( DataSource persons, int age ) { + this.persons = persons; + this.age = age; + } + + // A data source of `Persons` in this rule unit: + public DataSource getPersons() { + return persons; + } + + // A global variable in this rule unit: + public int getAdultAge() { + return adultAge; + } + + // Life-cycle methods: + @Override + public void onStart() { + System.out.println("AdultUnit started."); + } + + @Override + public void onEnd() { + System.out.println("AdultUnit ended."); + } +} +---- + +In this example, `persons` is a source of facts of type `Person`. A rule unit data source is a source of the data processed by a given rule unit and represents the entry point that the {RULE_ENGINE} uses to evaluate the rule unit. The `adultAge` global variable is accessible from all the rules belonging to this rule unit. The last two methods are part of the rule unit life cycle and are invoked by the {RULE_ENGINE}. + +The {RULE_ENGINE} supports the following optional life-cycle methods for rule units: + +[cols="2", options="header"] +.Rule unit life-cycle methods +|=== +|Method +|Invoked when + +|`onStart()` +|Rule unit execution starts + +|`onEnd()` +|Rule unit execution ends + +|`onSuspend()` +|Rule unit execution is suspended (used only with `runUntilHalt()`) + +|`onResume()` +|Rule unit execution is resumed (used only with `runUntilHalt()`) + +|`onYield(RuleUnit other)` +|The consequence of a rule in the rule unit triggers the execution of a different rule unit +|=== + +You can add one or more rules to a rule unit. By default, all the rules in a DRL file are automatically associated with a rule unit that follows the naming convention of the DRL file name. If the DRL file is in the same package and has the same name as a class that implements the `RuleUnit` interface, then all of the rules in that DRL file implicitly belong to that rule unit. For example, all the rules in the `AdultUnit.drl` file in the `org.mypackage.myunit` package are automatically part of the rule unit `org.mypackage.myunit.AdultUnit`. + +To override this naming convention and explicitly declare the rule unit that the rules in a DRL file belong to, use the `unit` keyword in the DRL file. The `unit` declaration must immediately follow the package declaration and contain the name of the class in that package that the rules in the DRL file are part of. + +.Example rule unit declaration in a DRL file +[source] +---- +package org.mypackage.myunit +unit AdultUnit + +rule Adult + when + $p : Person(age >= adultAge) from persons + then + System.out.println($p.getName() + " is adult and greater than " + adultAge); +end +---- + +WARNING: Do not mix rules with and without a rule unit in the same KIE base. Mixing two rule paradigms in a KIE base results in a compilation error. + +You can also rewrite the same pattern in a more convenient way using OOPath notation, as shown in the following example: + +.Example rule unit declaration in a DRL file that uses OOPath notation +[source] +---- +package org.mypackage.myunit +unit AdultUnit + +rule Adult + when + $p : /persons[age >= adultAge] + then + System.out.println($p.getName() + " is adult and greater than " + adultAge); +end +---- + +NOTE: OOPath is an object-oriented syntax extension of XPath that is designed for browsing graphs of objects in DRL rule condition constraints. OOPath uses the compact notation from XPath for navigating through related elements while handling collections and filtering constraints, and is specifically useful for graphs of objects. + +In this example, any matching facts in the rule conditions are retrieved from the `persons` data source defined in the `DataSource` definition in the rule unit class. The rule condition and action use the `adultAge` variable in the same way that a global variable is defined at the DRL file level. + +To execute one or more rule units defined in a KIE base, create a new `RuleUnitExecutor` class bound to the KIE base, create the rule unit from the relevant data source, and run the rule unit executer: + +.Example rule unit execution +[source,java] +---- +// Create a `RuleUnitExecutor` class and bind it to the KIE base: +KieBase kbase = kieContainer.getKieBase(); +RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); + +// Create the `AdultUnit` rule unit using the `persons` data source and run the executor: +RuleUnit adultUnit = new AdultUnit(persons, 18); +executor.run( adultUnit ); +---- + +Rules are executed by the `RuleUnitExecutor` class. The `RuleUnitExecutor` class creates KIE sessions and adds the required `DataSource` objects to those sessions, and then executes the rules based on the `RuleUnit` that is passed as a parameter to the `run()` method. + +The example execution code produces the following output when the relevant `Person` facts are inserted in the `persons` data source: + +.Example rule unit execution output +[source] +---- +org.mypackage.myunit.AdultUnit started. +Jane is adult and greater than 18 +John is adult and greater than 18 +org.mypackage.myunit.AdultUnit ended. +---- + +Instead of explicitly creating the rule unit instance, you can register the rule unit variables in the executor and pass to the executor the rule unit class that you want to run, and then the executor creates an instance of the rule unit. You can then set the `DataSource` definition and other variables as needed before running the rule unit. + +.Alternate rule unit execution option with registered variables +[source,java] +---- +executor.bindVariable( "persons", persons ); + .bindVariable( "adultAge", 18 ); +executor.run( AdultUnit.class ); +---- + +The name that you pass to the `RuleUnitExecutor.bindVariable()` method is used at run time to bind the variable to the field of the rule unit class with the same name. In the previous example, the `RuleUnitExecutor` inserts into the new rule unit the data source bound to the `"persons"` name and inserts the value `18` bound to the String `"adultAge"` into the fields with the corresponding names inside the `AdultUnit` class. + +To override this default variable-binding behavior, use the `@UnitVar` annotation to explicitly define a logical binding name for each field of the rule unit class. For example, the field bindings in the following class are redefined with alternative names: + +.Example code to modify variable binding names with `@UnitVar` +[source,java] +---- +package org.mypackage.myunit; + +public static class AdultUnit implements RuleUnit { + @UnitVar("minAge") + private int adultAge = 18; + + @UnitVar("data") + private DataSource persons; +} +---- + +You can then bind the variables to the executor using those alternative names and run the rule unit: + +.Example rule unit execution with modified variable names +[source,java] +---- +executor.bindVariable( "data", persons ); + .bindVariable( "minAge", 18 ); +executor.run( AdultUnit.class ); +---- + +You can execute a rule unit in _passive mode_ by using the `run()` method (equivalent to invoking `fireAllRules()` on a KIE session) +or in _active mode_ using the `runUntilHalt()` method (equivalent to invoking `fireUntilHalt()` on a KIE session). By default, the {RULE_ENGINE} runs in _passive mode_ and evaluates rule units only when a user or an application explicitly calls `run()` (or `fireAllRules()` for standard rules). If a user or application calls `runUntilHalt()` for rule units (or `fireUntilHalt()` for standard rules), the {RULE_ENGINE} starts in _active mode_ and evaluates rule units continually until the user or application explicitly calls `halt()`. + +If you use the `runUntilHalt()` method, invoke the method on a separate execution thread to avoid blocking the main thread: + +.Example rule unit execution with `runUntilHalt()` on a separate thread +[source,java] +---- +new Thread( () -> executor.runUntilHalt( adultUnit ) ).start(); +---- + +== Data sources for rule units + +A rule unit data source is a source of the data processed by a given rule unit and represents the entry point that the {RULE_ENGINE} uses to evaluate the rule unit. A rule unit can have zero or more data sources and each `DataSource` definition declared inside a rule unit can correspond to a different entry point into the rule unit executor. Multiple rule units can share a single data source, but each rule unit must use different entry points through which the same objects are inserted. + +You can create a `DataSource` definition with a fixed set of data in a rule unit class, as shown in the following example: + +.Example data source definition +[source,java] +---- +DataSource persons = DataSource.create( new Person( "John", 42 ), + new Person( "Jane", 44 ), + new Person( "Sally", 4 ) ); +---- + +Because a data source represents the entry point of the rule unit, you can insert, update, or delete facts in a rule unit: + +.Example code to insert, modify, and delete a fact in a rule unit +[source,java] +---- +// Insert a fact: +Person john = new Person( "John", 42 ); +FactHandle johnFh = persons.insert( john ); + +// Modify the fact and optionally specify modified properties (for property reactivity): +john.setAge( 43 ); +persons.update( johnFh, john, "age" ); + +// Delete the fact: +persons.delete( johnFh ); +---- + +== Rule unit execution control + +Rule units are helpful when you want to coordinate rule execution so that the execution of one rule unit triggers the start of another rule unit and so on. + +To facilitate rule unit execution control, the {RULE_ENGINE} supports the following rule unit methods that you can use in DRL rule actions to coordinate the execution of rule units: + +* `drools.run()`: Triggers the execution of a specified rule unit class. This method imperatively interrupts the execution of the rule unit and activates the other specified rule unit. +* `drools.guard()`: Prevents (guards) a specified rule unit class from being executed until the associated rule condition is met. This method declaratively schedules the execution of the other specified rule unit. When the {RULE_ENGINE} produces at least one match for the condition in the guarding rule, the guarded rule unit is considered active. A rule unit can contain multiple guarding rules. + +As an example of the `drools.run()` method, consider the following DRL rules that each belong to a specified rule unit. The `NotAdult` rule uses the `drools.run( AdultUnit.class )` method to trigger the execution of the `AdultUnit` rule unit: + +.Example DRL rules with controlled execution using `drools.run()` +[source] +---- +package org.mypackage.myunit +unit AdultUnit + +rule Adult + when + Person(age >= 18, $name : name) from persons + then + System.out.println($name + " is adult"); +end +---- + +[source] +---- +package org.mypackage.myunit +unit NotAdultUnit + +rule NotAdult + when + $p : Person(age < 18, $name : name) from persons + then + System.out.println($name + " is NOT adult"); + modify($p) { setAge(18); } + drools.run( AdultUnit.class ); +end +---- + +The example also uses a `RuleUnitExecutor` class created from the KIE base that was built from these rules and a `DataSource` definition of `persons` bound to it: + +.Example rule executor and data source definitions +[source,java] +---- +RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); +DataSource persons = executor.newDataSource( "persons", + new Person( "John", 42 ), + new Person( "Jane", 44 ), + new Person( "Sally", 4 ) ); +---- + +In this case, the example creates the `DataSource` definition directly from the `RuleUnitExecutor` class and binds it to the `"persons"` variable in a single statement. + +The example execution code produces the following output when the relevant `Person` facts are inserted in the `persons` data source: + +.Example rule unit execution output +[source] +---- +Sally is NOT adult +John is adult +Jane is adult +Sally is adult +---- + +The `NotAdult` rule detects a match when evaluating the person `"Sally"`, who is under 18 years old. The rule then modifies +her age to `18` and uses the `drools.run( AdultUnit.class )` method to trigger the execution of the `AdultUnit` rule unit. The `AdultUnit` rule unit contains a rule that can now be executed for all of the 3 `persons` in the `DataSource` definition. + +As an example of the `drools.guard()` method, consider the following `BoxOffice` class and `BoxOfficeUnit` rule unit class: + +.Example `BoxOffice` class +[source,java] +---- +public class BoxOffice { + private boolean open; + + public BoxOffice( boolean open ) { + this.open = open; + } + + public boolean isOpen() { + return open; + } + + public void setOpen( boolean open ) { + this.open = open; + } +} +---- + +.Example `BoxOfficeUnit` rule unit class +[source,java] +---- +public class BoxOfficeUnit implements RuleUnit { + private DataSource boxOffices; + + public DataSource getBoxOffices() { + return boxOffices; + } +} +---- + +The example also uses the following `TicketIssuerUnit` rule unit class to keep selling box office tickets for the event as long as at least one box office is open. This rule unit uses `DataSource` definitions of `persons` and `tickets`: + +.Example `TicketIssuerUnit` rule unit class +[source,java] +---- +public class TicketIssuerUnit implements RuleUnit { + private DataSource persons; + private DataSource tickets; + + private List results; + + public TicketIssuerUnit() { } + + public TicketIssuerUnit( DataSource persons, DataSource tickets ) { + this.persons = persons; + this.tickets = tickets; + } + + public DataSource getPersons() { + return persons; + } + + public DataSource getTickets() { + return tickets; + } + + public List getResults() { + return results; + } +} +---- + +The `BoxOfficeUnit` rule unit contains a `BoxOfficeIsOpen` DRL rule that uses the `drools.guard( TicketIssuerUnit.class )` method to guard the execution of the `TicketIssuerUnit` rule unit that distributes the event tickets, as shown in the following DRL rule examples: + + +.Example DRL rules with controlled execution using `drools.guard()` +[source] +---- +package org.mypackage.myunit; +unit TicketIssuerUnit; + +rule IssueAdultTicket when + $p: /persons[ age >= 18 ] +then + tickets.insert(new AdultTicket($p)); +end +rule RegisterAdultTicket when + $t: /tickets +then + results.add( $t.getPerson().getName() ); +end +---- + +[source] +---- +package org.mypackage.myunit; +unit BoxOfficeUnit; + +rule BoxOfficeIsOpen + when + $box: /boxOffices[ open ] + then + drools.guard( TicketIssuerUnit.class ); +end +---- + +In this example, so long as at least one box office is `open`, the guarded `TicketIssuerUnit` rule unit is active and distributes event tickets. When no more box offices are in `open` state, the guarded `TicketIssuerUnit` rule unit is prevented from being executed. + +The following example class illustrates a more complete box office scenario: + +.Example class for the box office scenario +[source,java] +---- +DataSource persons = executor.newDataSource( "persons" ); +DataSource boxOffices = executor.newDataSource( "boxOffices" ); +DataSource tickets = executor.newDataSource( "tickets" ); + +List list = new ArrayList<>(); +executor.bindVariable( "results", list ); + +// Two box offices are open: +BoxOffice office1 = new BoxOffice(true); +FactHandle officeFH1 = boxOffices.insert( office1 ); +BoxOffice office2 = new BoxOffice(true); +FactHandle officeFH2 = boxOffices.insert( office2 ); + +persons.insert(new Person("John", 40)); + +// Execute `BoxOfficeIsOpen` rule, run `TicketIssuerUnit` rule unit, and execute `RegisterAdultTicket` rule: +executor.run(BoxOfficeUnit.class); + +assertEquals( 1, list.size() ); +assertEquals( "John", list.get(0) ); +list.clear(); + +persons.insert(new Person("Matteo", 30)); + +// Execute `RegisterAdultTicket` rule: +executor.run(BoxOfficeUnit.class); + +assertEquals( 1, list.size() ); +assertEquals( "Matteo", list.get(0) ); +list.clear(); + +// One box office is closed, the other is open: +office1.setOpen(false); +boxOffices.update(officeFH1, office1); +persons.insert(new Person("Mark", 35)); +executor.run(BoxOfficeUnit.class); + +assertEquals( 1, list.size() ); +assertEquals( "Mark", list.get(0) ); +list.clear(); + +// All box offices are closed: +office2.setOpen(false); +boxOffices.update(officeFH2, office2); // Guarding rule is no longer true. +persons.insert(new Person("Edson", 35)); +executor.run(BoxOfficeUnit.class); // No execution + +assertEquals( 0, list.size() ); +---- + +== Rule unit identity conflicts + +In rule unit execution scenarios with guarded rule units, a rule can guard multiple rule units and at the same time a rule unit can be guarded and then activated by multiple rules. For these two-way guarding scenarios, rule units must have a clearly defined identity to avoid identity conflicts. + +By default, the identity of a rule unit is the rule unit class name and is treated as a singleton class by the `RuleUnitExecutor`. This identification behavior is encoded in the `getUnitIdentity()` default method of the `RuleUnit` interface: + +.Default identity method in the `RuleUnit` interface +[source,java] +---- +default Identity getUnitIdentity() { + return new Identity( getClass() ); +} +---- + +In some cases, you may need to override this default identification behavior to avoid conflicting identities between rule units. + +For example, the following `RuleUnit` class contains a `DataSource` definition that accepts any kind of object: + +.Example `Unit0` rule unit class +[source,java] +---- +public class Unit0 implements RuleUnit { + private DataSource input; + + public DataSource getInput() { + return input; + } +} +---- + +This rule unit contains the following DRL rule that guards another rule unit based on two conditions (in OOPath notation): + +.Example `GuardAgeCheck` DRL rule in the rule unit +[source] +---- +package org.mypackage.myunit +unit Unit0 + +rule GuardAgeCheck + when + $i: /input#Integer + $s: /input#String + then + drools.guard( new AgeCheckUnit($i) ); + drools.guard( new AgeCheckUnit($s.length()) ); +end +---- + +The guarded `AgeCheckUnit` rule unit verifies the age of a set of `persons`. The `AgeCheckUnit` contains a `DataSource` definition of the `persons` to check, a `minAge` variable that it verifies against, and a `List` for gathering the results: + +.Example `AgeCheckUnit` rule unit +[source,java] +---- +public class AgeCheckUnit implements RuleUnit { + private final int minAge; + private DataSource persons; + private List results; + + public AgeCheckUnit( int minAge ) { + this.minAge = minAge; + } + + public DataSource getPersons() { + return persons; + } + + public int getMinAge() { + return minAge; + } + + public List getResults() { + return results; + } +} +---- + +The `AgeCheckUnit` rule unit contains the following DRL rule that performs the verification of the `persons` in the data source: + +.Example `CheckAge` DRL rule in the rule unit +[source] +---- +package org.mypackage.myunit +unit AgeCheckUnit + +rule CheckAge + when + $p : /persons{ age > minAge } + then + results.add($p.getName() + ">" + minAge); +end +---- + +This example creates a `RuleUnitExecutor` class, binds the class to the KIE base that contains these two rule units, and creates +the two `DataSource` definitions for the same rule units: + +.Example executor and data source definitions +[source,java] +---- +RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); + +DataSource input = executor.newDataSource( "input" ); +DataSource persons = executor.newDataSource( "persons", + new Person( "John", 42 ), + new Person( "Sally", 4 ) ); + +List results = new ArrayList<>(); +executor.bindVariable( "results", results ); +---- + +You can now insert some objects into the input data source and execute the `Unit0` rule unit: + +.Example rule unit execution with inserted objects +[source,java] +---- +ds.insert("test"); +ds.insert(3); +ds.insert(4); +executor.run(Unit0.class); +---- + +.Example results list from the execution +[source,java] +---- +[Sally>3, John>3] +---- + +In this example, the rule unit named `AgeCheckUnit` is considered a singleton class and then executed only once, with the `minAge` +variable set to `3`. Both the String `"test"` and the Integer `4` inserted into the input data source can also trigger a second execution with the `minAge` variable set to `4`. However, the second execution does not occur because another rule unit with the same identity has already been evaluated. + +To resolve this rule unit identity conflict, override the `getUnitIdentity()` method in the `AgeCheckUnit` class to include also the `minAge` variable in the rule unit identity: + +.Modified `AgeCheckUnit` rule unit to override the `getUnitIdentity()` method +[source,java] +---- +public class AgeCheckUnit implements RuleUnit { + + ... + + @Override + public Identity getUnitIdentity() { + return new Identity(getClass(), minAge); + } +} +---- + +With this override in place, the previous example rule unit execution produces the following output: + +.Example results list from executing the modified rule unit +[source,java] +---- +[John>4, Sally>3, John>3] +---- + +The rule units with `minAge` set to `3` and `4` are now considered two different rule units and both are executed. diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-THEN-advanced-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-THEN-advanced-con.adoc new file mode 100644 index 00000000000..bca73b39677 --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-THEN-advanced-con.adoc @@ -0,0 +1,111 @@ +[id='drl-rules-THEN-advanced-con_{context}'] + += Advanced rule actions with conditional and named consequences + +In general, effective rule actions are small, declarative, and readable. However, in some cases, the limitation of having a single consequence for each rule can be challenging and lead to verbose and repetitive rule syntax, as shown in the following example rules: + +.Example rules with verbose and repetitive syntax +[source] +---- +rule "Give 10% discount to customers older than 60" + when + $customer : Customer( age > 60 ) + then + modify($customer) { setDiscount( 0.1 ) }; +end + +rule "Give free parking to customers older than 60" + when + $customer : Customer( age > 60 ) + $car : Car( owner == $customer ) + then + modify($car) { setFreeParking( true ) }; +end +---- + +A partial solution to the repetition is to make the second rule extend the first rule, as shown in the following modified example: + +.Partially enhanced example rules with an extended condition +[source] +---- +rule "Give 10% discount to customers older than 60" + when + $customer : Customer( age > 60 ) + then + modify($customer) { setDiscount( 0.1 ) }; +end + +rule "Give free parking to customers older than 60" + extends "Give 10% discount to customers older than 60" + when + $car : Car( owner == $customer ) + then + modify($car) { setFreeParking( true ) }; +end +---- + +As a more efficient alternative, you can consolidate the two rules into a single rule with modified conditions and labelled corresponding rule actions, as shown in the following consolidated example: + +.Consolidated example rule with conditional and named consequences +[source] +---- +rule "Give 10% discount and free parking to customers older than 60" + when + $customer : Customer( age > 60 ) + do[giveDiscount] + $car : Car( owner == $customer ) + then + modify($car) { setFreeParking( true ) }; + then[giveDiscount] + modify($customer) { setDiscount( 0.1 ) }; +end +---- + +This example rule uses two actions: the usual default action and another action named `giveDiscount`. The `giveDiscount` action is activated in the condition with the keyword `do` when a customer older than 60 years old is found in the KIE base, regardless of whether or not the customer owns a car. + +You can configure the activation of a named consequence with an additional condition, such as the `if` statement in the following example. The condition in the `if` statement is always evaluated on the pattern that immediately precedes it. + +.Consolidated example rule with an additional condition +[source] +---- +rule "Give free parking to customers older than 60 and 10% discount to golden ones among them" + when + $customer : Customer( age > 60 ) + if ( type == "Golden" ) do[giveDiscount] + $car : Car( owner == $customer ) + then + modify($car) { setFreeParking( true ) }; + then[giveDiscount] + modify($customer) { setDiscount( 0.1 ) }; +end +---- + +You can also evaluate different rule conditions using a nested `if` and `else if` construct, as shown in the following more complex example: + +.Consolidated example rule with more complex conditions +[source] +---- +rule "Give free parking and 10% discount to over 60 Golden customer and 5% to Silver ones" + when + $customer : Customer( age > 60 ) + if ( type == "Golden" ) do[giveDiscount10] + else if ( type == "Silver" ) break[giveDiscount5] + $car : Car( owner == $customer ) + then + modify($car) { setFreeParking( true ) }; + then[giveDiscount10] + modify($customer) { setDiscount( 0.1 ) }; + then[giveDiscount5] + modify($customer) { setDiscount( 0.05 ) }; +end +---- + +This example rule gives a 10% discount and free parking to Golden customers over 60, but only a 5% discount without free parking to Silver customers. The rule activates the consequence named `giveDiscount5` with the keyword `break` instead of `do`. The keyword `do` schedules a consequence in the {RULE_ENGINE} agenda, enabling the remaining part of the rule conditions to continue being evaluated, while `break` blocks any further condition evaluation. If a named consequence does not correspond to any condition with `do` but is activated with `break`, the rule fails to compile because the conditional part of the rule is never reached. + +ifdef::DROOLS,JBPM,OP[] +ifdef::backend-docbook[] +[index] +== Index +// Generated automatically by the DocBook toolchain. +endif::backend-docbook[] +endif::DROOLS,JBPM,OP[] diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-THEN-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-THEN-con.adoc new file mode 100644 index 00000000000..60815d55290 --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-THEN-con.adoc @@ -0,0 +1,19 @@ +[id='drl-rules-THEN-con_{context}'] += Rule actions in DRL (THEN) + +The `then` part of the rule (also known as the _Right Hand Side (RHS)_ of the rule) contains the actions to be performed when the conditional part of the rule has been met. Actions consist of one or more _methods_ that execute consequences based on the rule conditions and on available data objects in the package. For example, if a bank requires loan applicants to be over 21 years of age (with a rule condition `Applicant( age < 21 )`) and a loan applicant is under 21 years old, the `then` action of an `"Underage"` rule would be `setApproved( false )`, declining the loan because the applicant is under age. + +The main purpose of rule actions is to insert, delete, or modify data in the working memory of the {RULE_ENGINE}. Effective rule actions are small, declarative, and readable. If you need to use imperative or conditional code in rule actions, then divide the rule into multiple smaller and more declarative rules. + +.Example rule for loan application age limit +[source] +---- +rule "Underage" + when + application : LoanApplication() + Applicant( age < 21 ) + then + application.setApproved( false ); + application.setExplanation( "Underage" ); +end +---- diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-THEN-methods-ref.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-THEN-methods-ref.adoc new file mode 100644 index 00000000000..021daebde99 --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-THEN-methods-ref.adoc @@ -0,0 +1,128 @@ +[id='drl-rules-THEN-methods-ref_{context}'] += Supported rule action methods in DRL + +DRL supports the following rule action methods that you can use in DRL rule actions. You can use these methods to modify the working memory of the {RULE_ENGINE} without having to first reference a working memory instance. These methods act as shortcuts to the methods provided by the `RuleContext` class in your {PRODUCT} distribution. + +For all rule action methods, +ifdef::DM,PAM[] +download the *{PRODUCT_PAM} {PRODUCT_VERSION_LONG} Source Distribution* ZIP file from the https://access.redhat.com/jbossnetwork/restricted/listSoftware.html[Red Hat Customer Portal] and navigate to `~/{PRODUCT_FILE}-sources/src/kie-api-parent-$VERSION/kie-api/src/main/java/org/kie/api/runtime/rule/RuleContext.java`. +endif::[] +ifdef::DROOLS,JBPM,OP[] +see the {PRODUCT} https://github.com/kiegroup/droolsjbpm-knowledge/blob/{COMMUNITY_VERSION_BRANCH}/kie-api/src/main/java/org/kie/api/runtime/rule/RuleContext.java[RuleContext.java] page in GitHub. +endif::[] + +`set`:: +Use this to set the value of a field. ++ +-- +[source,subs="attributes+"] +---- +set ( ) +---- + +.Example rule action to set the values of a loan application approval +[source] +---- +$application.setApproved ( false ); +$application.setExplanation( "has been bankrupt" ); +---- +-- + +`modify`:: +Use this to specify fields to be modified for a fact and to notify the {RULE_ENGINE} of the change. This method provides a structured approach to fact updates. It combines the `update` operation with setter calls to change object fields. ++ +-- +[source] +---- +modify ( ) { + , + , + ... +} +---- + +.Example rule action to modify a loan application amount and approval +[source] +---- +modify( LoanApplication ) { + setAmount( 100 ), + setApproved ( true ) +} +---- +-- + +`update`:: +Use this to specify fields and the entire related fact to be updated and to notify the {RULE_ENGINE} of the change. After a fact has changed, you must call `update` before changing another fact that might be affected by the updated values. To avoid this added step, use the `modify` method instead. ++ +-- +[source,subs="attributes+"] +---- +update ( ) // Informs the {RULE_ENGINE} that an object has changed + +update ( ) // Causes `KieSession` to search for a fact handle of the object +---- + +.Example rule action to update a loan application amount and approval +[source] +---- +LoanApplication.setAmount( 100 ); +update( LoanApplication ); +---- + +NOTE: If you provide property-change listeners, you do not need to call this method when an object changes. For more information about property-change listeners, see +ifdef::DM,PAM[] +{URL_DEVELOPING_DECISION_SERVICES}#property-change-listeners-con_decision-engine[_{RULE_ENGINE_DOC}_]. +endif::[] +ifdef::DROOLS,JBPM,OP[] +xref:property-change-listeners-con_decision-engine[]. +endif::[] + +-- + +`insert`:: +Use this to insert a `new` fact into the working memory of the {RULE_ENGINE} and to define resulting fields and values as needed for the fact. ++ +-- +[source,subs="attributes+"] +---- +insert( new ); +---- + +.Example rule action to insert a new loan applicant object +[source] +---- +insert( new Applicant() ); +---- +-- + +`insertLogical`:: +Use this to insert a `new` fact logically into the {RULE_ENGINE}. The {RULE_ENGINE} is responsible for logical decisions on insertions and retractions of facts. After regular or stated insertions, facts must be retracted explicitly. After logical insertions, the facts that were inserted are automatically retracted when the conditions in the rules that inserted the facts are no longer true. ++ +-- +[source] +---- +insertLogical( new ); +---- + +.Example rule action to logically insert a new loan applicant object +[source] +---- +insertLogical( new Applicant() ); +---- +-- + +`delete`:: +Use this to remove an object from the {RULE_ENGINE}. The keyword `retract` is also supported in DRL and executes the same action, but `delete` is typically preferred in DRL code for consistency with the keyword `insert`. ++ +-- +[source] +---- +delete( ); +---- + +.Example rule action to delete a loan applicant object +[source] +---- +delete( Applicant ); +---- +-- diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-THEN-methods-variables-ref.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-THEN-methods-variables-ref.adoc new file mode 100644 index 00000000000..998feed80ee --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-THEN-methods-variables-ref.adoc @@ -0,0 +1,34 @@ +[id='drl-rules-THEN-methods-variables-ref_{context}'] += Other rule action methods from `drools` variable + +In addition to the standard rule action methods, the {RULE_ENGINE} supports methods in conjunction with the predefined `drools` variable that you can also use in rule actions. + +You can use the `drools` variable to call methods from the `org.kie.api.runtime.rule.RuleContext` class in your {PRODUCT} distribution, which is also the class that the standard rule action methods are based on. For all `drools` rule action options, +ifdef::DM,PAM[] +download the *{PRODUCT_PAM} {PRODUCT_VERSION_LONG} Source Distribution* ZIP file from the https://access.redhat.com/jbossnetwork/restricted/listSoftware.html[Red Hat Customer Portal] and navigate to `~/{PRODUCT_FILE}-sources/src/kie-api-parent-$VERSION/kie-api/src/main/java/org/kie/api/runtime/rule/RuleContext.java`. +endif::[] +ifdef::DROOLS,JBPM,OP[] +see the {PRODUCT} https://github.com/kiegroup/droolsjbpm-knowledge/blob/{COMMUNITY_VERSION_BRANCH}/kie-api/src/main/java/org/kie/api/runtime/rule/RuleContext.java[RuleContext.java] page in GitHub. +endif::[] + +The `drools` variable contains methods that provide information about the firing rule and the set of facts that activated the firing rule: + +* `drools.getRule().getName()`: Returns the name of the currently firing rule. +* `drools.getMatch()`: Returns the `Match` that activated the currently firing rule. It contains information that is useful for logging and debugging purposes, for instance `drools.getMatch().getObjects()` returns the list of objects, enabling rule to fire in the proper tuple order. + +From the `drools` variable, you can also obtain a reference to the `KieRuntime` providing useful methods to interact with the running session, for example: + +* `drools.getKieRuntime().halt()`: Terminates rule execution if a user or application previously called `fireUntilHalt()`. When a user or application calls `fireUntilHalt()` method, the {RULE_ENGINE} starts in `active` mode and evaluates rules until the user or application explicitly calls `halt()` method. Otherwise, by default, the {RULE_ENGINE} runs in `passive` mode and evaluates rules only when a user or an application explicitly calls `fireAllRules()` method. +* `drools.getKieRuntime().getAgenda()`: Returns a reference to the KIE session `Agenda`, and in turn provides access to rule activation groups, rule agenda groups, and ruleflow groups. + +.Example call to access agenda group "CleanUp" and set the focus +[source,java] +---- +drools.getKieRuntime().getAgenda().getAgendaGroup( "CleanUp" ).setFocus(); +---- ++ +This example sets the focus to a specified agenda group to which the rule belongs. + +* `drools.getKieRuntime().setGlobal()`, `~.getGlobal()`, `~.getGlobals()`: Sets or retrieves global variables. +* `drools.getKieRuntime().getEnvironment()`: Returns the runtime `Environment`, similar to your operating system environment. +* `drools.getKieRuntime().getQueryResults( query)`: Runs a query and returns the results. diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-WHEN-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-WHEN-con.adoc new file mode 100644 index 00000000000..0fb4c1700b0 --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-WHEN-con.adoc @@ -0,0 +1,377 @@ +[id='drl-rules-WHEN-con_{context}'] += Rule conditions in DRL (WHEN) + +ifdef::DROOLS,JBPM,OP[] +.Rule +image::language-reference/rule.png[align="center"] + +.Conditional element in a rule +image::language-reference/lhs.png[align="center"] +endif::[] + +The `when` part of a DRL rule (also known as the _Left Hand Side (LHS)_ of the rule) contains the conditions that must be met to execute an action. Conditions consist of a series of stated _patterns_ and _constraints_, with optional _bindings_ and supported rule condition elements (keywords), based on the available data objects in the package. For example, if a bank requires loan applicants to have over 21 years of age, then the `when` condition of an `"Underage"` rule would be `Applicant( age < 21 )`. + +NOTE: DRL uses `when` instead of `if` because `if` is typically part of a procedural execution flow during which a condition is checked at a specific point in time. In contrast, `when` indicates that the condition evaluation is not limited to a specific evaluation sequence or point in time, but instead occurs continually at any time. Whenever the condition is met, the actions are executed. + +If the `when` section is empty, then the conditions are considered to be true and the actions in the `then` section are executed the first time a `fireAllRules()` call is made in the {RULE_ENGINE}. This is useful if you want to use rules to set up the {RULE_ENGINE} state. + +The following example rule uses empty conditions to insert a fact every time the rule is executed: + +.Example rule without conditions +[source] +---- +rule "Always insert applicant" + when + // Empty + then // Actions to be executed once + insert( new Applicant() ); +end + +// The rule is internally rewritten in the following way: + +rule "Always insert applicant" + when + eval( true ) + then + insert( new Applicant() ); +end +---- + +If rule conditions use multiple patterns with no defined keyword conjunctions (such as `and`, `or`, or `not`), the default conjunction is `and`: + +.Example rule without keyword conjunctions +[source] +---- +rule "Underage" + when + application : LoanApplication() + Applicant( age < 21 ) + then + // Actions +end + +// The rule is internally rewritten in the following way: + +rule "Underage" + when + application : LoanApplication() + and Applicant( age < 21 ) + then + // Actions +end +---- + +== Patterns and constraints + +A _pattern_ in a DRL rule condition is the segment to be matched by the {RULE_ENGINE}. A pattern can potentially match each fact that is inserted into the working memory of the {RULE_ENGINE}. A pattern can also contain _constraints_ to further define the facts to be matched. + +ifdef::DROOLS,JBPM,OP[] +The railroad diagram below shows the syntax for this: + +.Pattern +image::language-reference/Pattern.png[align="center"] +endif::[] + +In the simplest form, with no constraints, a pattern matches a fact of the given type. In the following example, the type is `Person`, so the pattern will match against all `Person` objects in the working memory of the {RULE_ENGINE}: + +.Example pattern for a single fact type +[source] +---- +Person() +---- + +The type does not need to be the actual class of some fact object. Patterns can refer to superclasses or even interfaces, potentially matching facts from many different classes. For example, the following pattern matches all objects in the working memory of the {RULE_ENGINE}: + +.Example pattern for all objects +[source] +---- +Object() // Matches all objects in the working memory +---- + +The parentheses of a pattern enclose the constraints, such as the following constraint on the person's age: + +.Example pattern with a constraint +[source] +---- +Person( age == 50 ) +---- + +A _constraint_ is an expression that returns `true` or `false`. Pattern constraints in DRL are essentially Java expressions with some enhancements, such as property access, and some differences, such as `equals()` and `!equals()` semantics for `==` and `!=` (instead of the usual `same` and `not same` semantics). + +Any JavaBeans property can be accessed directly from pattern constraints. A bean property is exposed internally using a standard JavaBeans getter that takes no arguments and returns something. For example, the `age` property is written as `age` in DRL instead of the getter `getAge()`: + +.DRL constraint syntax with JavaBeans properties +[source] +---- +Person( age == 50 ) + +// This is the same as the following getter format: + +Person( getAge() == 50 ) +---- + +{PRODUCT} uses the standard JDK `Introspector` class to achieve this mapping, so it follows the standard JavaBeans specification. For optimal {RULE_ENGINE} performance, use the property access format, such as `age`, instead of using getters explicitly, such as `getAge()`. + +[WARNING] +==== +Do not use property accessors to change the state of the object in a way that might affect the rules because the {RULE_ENGINE} caches the results of the match between invocations for higher efficiency. + +For example, do not use property accessors in the following ways: + +[source,java] +---- +public int getAge() { + age++; // Do not do this. + return age; +} +---- + +[source,java] +---- +public int getAge() { + Date now = DateUtil.now(); // Do not do this. + return DateUtil.differenceInYears(now, birthday); +} +---- + +Instead of following the second example, insert a fact that wraps the current date in the working memory and update that fact between `fireAllRules()` as needed. +==== + +However, if the getter of a property cannot be found, the compiler uses the property name as a fallback method name, without arguments: + +.Fallback method if object is not found +[source] +---- +Person( age == 50 ) + +// If `Person.getAge()` does not exist, the compiler uses the following syntax: + +Person( age() == 50 ) +---- + +You can also nest access properties in patterns, as shown in the following example. Nested properties are indexed by the {RULE_ENGINE}. + +.Example pattern with nested property access +[source] +---- +Person( address.houseNumber == 50 ) + +// This is the same as the following format: + +Person( getAddress().getHouseNumber() == 50 ) +---- + +WARNING: In stateful KIE sessions, use nested accessors carefully because the working memory of the {RULE_ENGINE} is not aware of any of the nested values and does not detect when they change. Either consider the nested values immutable while any of their parent references are inserted into the working memory, or, if you want to modify a nested value, mark all of the outer facts as updated. In the previous example, when the `houseNumber` property changes, any `Person` with that `Address` must be marked as updated. + +You can use any Java expression that returns a `boolean` value as a constraint inside the parentheses of a pattern. Java expressions can be mixed with other expression enhancements, such as property access: + +.Example pattern with a constraint using property access and Java expression +[source] +---- +Person( age == 50 ) +---- + +You can change the evaluation priority by using parentheses, as in any logical or mathematical expression: + +.Example evaluation order of constraints +[source] +---- +Person( age > 100 && ( age % 10 == 0 ) ) +---- + +You can also reuse Java methods in constraints, as shown in the following example: + +.Example constraints with reused Java methods +[source] +---- +Person( Math.round( weight / ( height * height ) ) < 25.0 ) +---- + +[WARNING] +==== +Do not use constraints to change the state of the object in a way that might affect the rules because the {RULE_ENGINE} caches the results of the match between invocations for higher efficiency. Any method that is executed on a fact in the rule conditions must be a read-only method. Also, the state of a fact should not change between rule invocations unless those facts are marked as updated in the working memory on every change. + +For example, do not use a pattern constraint in the following ways: + +[source] +---- +Person( incrementAndGetAge() == 10 ) // Do not do this. +---- + +[source] +---- +Person( System.currentTimeMillis() % 1000 == 0 ) // Do not do this. +---- +==== + +Standard Java operator precedence applies to constraint operators in DRL, and DRL operators follow standard Java semantics except for the `==` and `!=` operators. + +The `==` operator uses null-safe `equals()` semantics instead of the usual `same` semantics. For example, the pattern `Person( firstName == "John" )` is similar to `java.util.Objects.equals(person.getFirstName(), "John")`, and because `"John"` is not null, the pattern is also similar to `"John".equals(person.getFirstName())`. + +The `!=` operator uses null-safe `!equals()` semantics instead of the usual `not same` semantics. For example, the pattern `Person( firstName != "John" )` is similar to `!java.util.Objects.equals(person.getFirstName(), "John")`. + +If the field and the value of a constraint are of different types, the {RULE_ENGINE} uses type coercion to resolve the conflict and reduce compilation errors. For instance, if `"ten"` is provided as a string in a numeric evaluator, a compilation error occurs, whereas `"10"` is coerced to a numeric 10. In coercion, the field type always takes precedence over the value type: + +.Example constraint with a value that is coerced +[source] +---- +Person( age == "10" ) // "10" is coerced to 10 +---- + +For groups of constraints, you can use a delimiting comma `,` to use implicit `and` connective semantics: + +.Example patterns with multiple constraints +[source] +---- +// Person is at least 50 years old and weighs at least 80 kilograms: +Person( age > 50, weight > 80 ) + +// Person is at least 50 years old, weighs at least 80 kilograms, and is taller than 2 meters: +Person( age > 50, weight > 80, height > 2 ) +---- + +NOTE: Although the `&&` and `,` operators have the same semantics, they are resolved with different priorities. The `&&` operator precedes the `||` operator, and both the `&&` and `||` operators together precede the `,` operator. Use the comma operator at the top-level constraint for optimal {RULE_ENGINE} performance and human readability. + +You cannot embed a comma operator in a composite constraint expression, such as in parentheses: + +.Example of misused comma in composite constraint expression +[source] +---- +// Do not use the following format: +Person( ( age > 50, weight > 80 ) || height > 2 ) + +// Use the following format instead: +Person( ( age > 50 && weight > 80 ) || height > 2 ) +---- + +== Bound variables in patterns and constraints + +You can bind variables to patterns and constraints to refer to matched objects in other portions of a rule. Bound variables can help you define rules more efficiently or more consistently with how you annotate facts in your data model. To differentiate more easily between variables and fields in a rule, use the standard format `$variable` for variables, especially in complex rules. This convention is helpful but not required in DRL. + +For example, the following DRL rule uses the variable `$p` for a pattern with the `Person` fact: + +.Pattern with a bound variable +[source] +---- +rule "simple rule" + when + $p : Person() + then + System.out.println( "Person " + $p ); +end +---- + +Similarly, you can also bind variables to properties in pattern constraints, as shown in the following example: + +[source] +---- +// Two persons of the same age: +Person( $firstAge : age ) // Binding +Person( age == $firstAge ) // Constraint expression +---- + +[NOTE] +==== +Constraint binding considers only the first atomic expression that follows it. In the following example the pattern only binds the age of the person to the variable `$a`: + +[source] +---- +Person( $a : age * 2 < 100 ) +---- + + +For clearer and more efficient rule definitions, separate constraint bindings and constraint expressions. Although mixed bindings and expressions are supported, which can complicate patterns and affect evaluation efficiency. + +[source] +---- +// Do not use the following format: +Person( $a : age * 2 < 100 ) + +// Use the following format instead: +Person( age * 2 < 100, $a : age ) +---- + +In the preceding example, if you want to bind to the variable `$a` the double of the person's age, you must make it an atomic expression by wrapping it in parentheses as shown in the following example: + +[source] +---- +Person( $a : (age * 2) ) +---- +==== + +The {RULE_ENGINE} does not support bindings to the same declaration, but does support _unification_ of arguments across several properties. While positional arguments are always processed with unification, the unification symbol `:=` exists for named arguments. + +The following example patterns unify the `age` property across two `Person` facts: + +.Example pattern with unification +[source] +---- +Person( $age := age ) +Person( $age := age ) +---- + +Unification declares a binding for the first occurrence and constrains to the same value of the bound field for sequence occurrences. + +== Nested constraints and inline casts + +In some cases, you might need to access multiple properties of a nested object, as shown in the following example: + +.Example pattern to access multiple properties +[source] +---- +Person( name == "mark", address.city == "london", address.country == "uk" ) +---- + +You can group these property accessors to nested objects with the syntax `.( )` for more readable rules, as shown in the following example: + +.Example pattern with grouped constraints +[source] +---- +Person( name == "mark", address.( city == "london", country == "uk") ) +---- + +NOTE: The period prefix `.` differentiates the nested object constraints from a method call. + +When you work with nested objects in patterns, you can use the syntax `#` to cast to a subtype and make the getters from the parent type available to the subtype. You can use either the object name or fully qualified class name, and you can cast to one or multiple subtypes, as shown in the following examples: + +.Example patterns with inline casting to a subtype +[source] +---- +// Inline casting with subtype name: +Person( name == "mark", address#LongAddress.country == "uk" ) + +// Inline casting with fully qualified class name: +Person( name == "mark", address#org.domain.LongAddress.country == "uk" ) + +// Multiple inline casts: +Person( name == "mark", address#LongAddress.country#DetailedCountry.population > 10000000 ) +---- + +These example patterns cast `Address` to `LongAddress`, and additionally to `DetailedCountry` in the last example, making the parent getters available to the subtypes in each case. + +You can use the `instanceof` operator to infer the results of the specified type in subsequent uses of that field with the pattern, as shown in the following example: + +[source] +---- +Person( name == "mark", address instanceof LongAddress, address.country == "uk" ) +---- + +If an inline cast is not possible (for example, if `instanceof` returns `false`), the evaluation is considered `false`. + +== Date literal in constraints + +By default, the {RULE_ENGINE} supports the date format `dd-mmm-yyyy`. You can customize the date format, including a time format mask if needed, by providing an alternative format mask with the system property `drools.dateformat="dd-mmm-yyyy hh:mm"`. You can also customize the date format by changing the language locale with the `drools.defaultlanguage` and `drools.defaultcountry` system properties (for example, the locale of Thailand is set as `drools.defaultlanguage=th` and `drools.defaultcountry=TH`). + +.Example pattern with a date literal restriction +[source] +---- +Person( bornBefore < "27-Oct-2009" ) +---- + +ifdef::DROOLS,JBPM,OP[] +== Auto-boxing and primitive types + +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 early Drools versions 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. +endif::[] diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-WHEN-elements-diagrams-ref.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-WHEN-elements-diagrams-ref.adoc new file mode 100644 index 00000000000..241c32a4392 --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-WHEN-elements-diagrams-ref.adoc @@ -0,0 +1,188 @@ +[id='drl-rules-WHEN-elements-diagrams-ref_{context}'] + += Railroad diagrams for rule condition elements in DRL + + +image::language-reference/AccumulateAction.png[align="center"] + +image::language-reference/AccumulateClause.png[align="center"] + +image::language-reference/AccumulateFunction.png[align="center"] + +image::language-reference/AccumulateInit.png[align="center"] + +image::language-reference/AccumulateResult.png[align="center"] + +image::language-reference/AccumulateReverse.png[align="center"] + +image::language-reference/AccumulateSteps.png[align="center"] + +image::language-reference/Accumulations.png[align="center"] + +image::language-reference/AdditiveExpr.png[align="center"] + +image::language-reference/Annotation.png[align="center"] + +image::language-reference/Arguments.png[align="center"] + +image::language-reference/ArrayCreatorRest.png[align="center"] + +image::language-reference/ArrayInitializer.png[align="center"] + +image::language-reference/AssignmentOperator.png[align="center"] + +image::language-reference/BindingPattern.png[align="center"] + +image::language-reference/Block.png[align="center"] + +image::language-reference/BooleanLiteral.png[align="center"] + +image::language-reference/CompilationUnit.png[align="center"] + +image::language-reference/ConditionalAnd.png[align="center"] + +image::language-reference/ConditionalElementAccumulate.png[align="center"] + +image::language-reference/ConditionalElementEval.png[align="center"] + +image::language-reference/ConditionalElementExists.png[align="center"] + +image::language-reference/ConditionalElementForall.png[align="center"] + +image::language-reference/ConditionalElementNot.png[align="center"] + +image::language-reference/ConditionalElement.png[align="center"] + +image::language-reference/ConditionalExpr.png[align="center"] + +image::language-reference/ConditionalOrExpr.png[align="center"] + +image::language-reference/ConditionalOr.png[align="center"] + +image::language-reference/Constraints.png[align="center"] + +image::language-reference/CreatedName.png[align="center"] + +image::language-reference/Creator.png[align="center"] + +image::language-reference/Definition.png[align="center"] + +image::language-reference/Digit.png[align="center"] + +image::language-reference/ExplicitGenericInvocationSuffix.png[align="center"] + +image::language-reference/ExplicitGenericInvocation.png[align="center"] + +image::language-reference/Exponent.png[align="center"] + +image::language-reference/ExpressionList.png[align="center"] + +image::language-reference/Expression.png[align="center"] + +image::language-reference/Field.png[align="center"] + +image::language-reference/Fraction.png[align="center"] + +image::language-reference/FromAccumulateClause.png[align="center"] + +image::language-reference/FromClause.png[align="center"] + +image::language-reference/FromCollectClause.png[align="center"] + +image::language-reference/FunctionDefinition.png[align="center"] + +image::language-reference/GlobalDefinition.png[align="center"] + +image::language-reference/IdentifierSuffix.png[align="center"] + +image::language-reference/ImportDefinition.png[align="center"] + +image::language-reference/InExpr.png[align="center"] + +image::language-reference/InlineListExpr.png[align="center"] + +image::language-reference/InlineMapExpr.png[align="center"] + +image::language-reference/InnerCreator.png[align="center"] + +image::language-reference/InstanceOfExpr.png[align="center"] + +image::language-reference/IntLiteral.png[align="center"] + +image::language-reference/Literal.png[align="center"] + +image::language-reference/ModifyStatement.png[align="center"] + +image::language-reference/NonWildcardTypeArguments.png[align="center"] + +image::language-reference/OrRestriction.png[align="center"] + +image::language-reference/OverClause.png[align="center"] + +image::language-reference/Parameters.png[align="center"] + +image::language-reference/Pattern.png[align="center"] + +image::language-reference/Placeholders.png[align="center"] + +image::language-reference/Primary.png[align="center"] + +image::language-reference/PrimitiveType.png[align="center"] + +image::language-reference/QualifiedName.png[align="center"] + +image::language-reference/QueryDefinition.png[align="center"] + +image::language-reference/QueryOptions.png[align="center"] + +image::language-reference/RealLiteral.png[align="center"] + +image::language-reference/RealTypeSuffix.png[align="center"] + +image::language-reference/RelationalExpr.png[align="center"] + +image::language-reference/RelationalOperator.png[align="center"] + +image::language-reference/RhsStatement.png[align="center"] + +image::language-reference/RuleAttributes.png[align="center"] + +image::language-reference/RuleAttribute.png[align="center"] + +image::language-reference/RuleDefinition.png[align="center"] + +image::language-reference/RuleOptions.png[align="center"] + +image::language-reference/Selector.png[align="center"] + +image::language-reference/ShiftExpr.png[align="center"] + +image::language-reference/SingleRestriction.png[align="center"] + +image::language-reference/SourcePattern.png[align="center"] + +image::language-reference/StringId.png[align="center"] + +image::language-reference/SuperSuffix.png[align="center"] + +image::language-reference/ThenPart.png[align="center"] + +image::language-reference/TypeArguments.png[align="center"] + +image::language-reference/TypeArgument.png[align="center"] + +image::language-reference/TypeDefinition.png[align="center"] + +image::language-reference/TypeOptions.png[align="center"] + +image::language-reference/Type.png[align="center"] + +image::language-reference/UnaryExprNotPlusMinus.png[align="center"] + +image::language-reference/UnaryExpr.png[align="center"] + +image::language-reference/Value.png[align="center"] + +image::language-reference/VariableInitializer.png[align="center"] + +image::language-reference/WhenPart.png[align="center"] diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-WHEN-elements-ref.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-WHEN-elements-ref.adoc new file mode 100644 index 00000000000..8299167b8af --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-WHEN-elements-ref.adoc @@ -0,0 +1,785 @@ +[id='drl-rules-WHEN-elements-ref_{context}'] + += Supported rule condition elements in DRL (keywords) + +DRL supports the following rule condition elements (keywords) that you can use with the patterns that you define in DRL rule conditions: + +`and`:: +Use this to group conditional components into a logical conjunction. Infix and prefix `and` are supported. You can group patterns explicitly with parentheses `()`. By default, all listed patterns are combined with `and` when no conjunction is specified. ++ +-- +ifdef::DROOLS,JBPM,OP[] +.infixAnd +image::language-reference/infixAnd.png[align="center"] + +.prefixAnd +image::language-reference/prefixAnd.png[align="center"] +endif::[] + +.Example patterns with `and` +[source] +---- +//Infix `and`: +Color( colorType : type ) and Person( favoriteColor == colorType ) + +//Infix `and` with grouping: +(Color( colorType : type ) and (Person( favoriteColor == colorType ) or Person( favoriteColor == colorType )) + +// Prefix `and`: +(and Color( colorType : type ) Person( favoriteColor == colorType )) + +// Default implicit `and`: +Color( colorType : type ) +Person( favoriteColor == colorType ) +---- + +[NOTE] +==== +Do not use a leading declaration binding with the `and` keyword (as you can with `or`, for example). A declaration can only reference a single fact at a time, and if you use a declaration binding with `and`, then when `and` is satisfied, it matches both facts and results in an error. + +.Example misuse of `and` +[source] +---- +// Causes compile error: +$person : (Person( name == "Romeo" ) and Person( name == "Juliet")) +---- +==== +-- + +`or`:: +Use this to group conditional components into a logical disjunction. Infix and prefix `or` are supported. You can group patterns explicitly with parentheses `()`. You can also use pattern binding with `or`, but each pattern must be bound separately. ++ +-- +ifdef::DROOLS,JBPM,OP[] +.infixOr +image::language-reference/infixOr.png[align="center"] + +.prefixOr +image::language-reference/prefixOr.png[align="center"] +endif::[] + +.Example patterns with `or` +[source] +---- +//Infix `or`: +Color( colorType : type ) or Person( favoriteColor == colorType ) + +//Infix `or` with grouping: +(Color( colorType : type ) or (Person( favoriteColor == colorType ) and Person( favoriteColor == colorType )) + +// Prefix `or`: +(or Color( colorType : type ) Person( favoriteColor == colorType )) +---- + +.Example patterns with `or` and pattern binding +[source] +---- +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 )) +---- + +The {RULE_ENGINE} does not directly interpret the `or` element but uses logical transformations to rewrite a rule with `or` as a number of sub-rules. This process ultimately results in a rule that has a single `or` as the root node and one sub-rule for each of its condition elements. Each sub-rule is activated and executed like any normal rule, with no special behavior or interaction between the sub-rules. + +Therefore, consider the `or` condition element a shortcut for generating two or more similar rules that, in turn, can create multiple activations when two or more terms of the disjunction are true. +-- + +`exists`:: +Use this to specify facts and constraints that must exist. This option is triggered on only the first match, not subsequent matches. If you use this element with multiple patterns, enclose the patterns with parentheses `()`. ++ +-- +ifdef::DROOLS,JBPM,OP[] +.Exists +image::language-reference/exists.png[align="center"] +endif::[] + +.Example patterns with `exists` +[source] +---- +exists Person( firstName == "John") + +exists (Person( firstName == "John", age == 42 )) + +exists (Person( firstName == "John" ) and + Person( lastName == "Doe" )) +---- +-- + +`not`:: +Use this to specify facts and constraints that must not exist. If you use this element with multiple patterns, enclose the patterns with parentheses `()`. ++ +-- +ifdef::DROOLS,JBPM,OP[] +.Not +image::language-reference/not.png[align="center"] +endif::[] + +.Example patterns with `not` +[source] +---- +not Person( firstName == "John") + +not (Person( firstName == "John", age == 42 )) + +not (Person( firstName == "John" ) and + Person( lastName == "Doe" )) +---- +-- + +`forall`:: +Use this to verify whether all facts that match the first pattern match all the remaining patterns. When a `forall` construct is satisfied, the rule evaluates to `true`. This element is a scope delimiter, so it can use any previously bound variable, but no variable bound inside of it is available for use outside of it. ++ +-- +ifdef::DROOLS,JBPM,OP[] +.Forall +image::language-reference/forall.png[align="center"] +endif::[] + +.Example rule with `forall` +[source] +---- +rule "All full-time employees have red ID badges" + when + forall( $emp : Employee( type == "fulltime" ) + Employee( this == $emp, badgeColor = "red" ) ) + then + // True, all full-time employees have red ID badges. +end +---- + +In this example, the rule selects all `Employee` objects whose type is `"fulltime"`. For each fact that matches this pattern, the rule evaluates the patterns that follow (badge color) and if they match, the rule evaluates to `true`. + +To state that all facts of a given type in the working memory of the {RULE_ENGINE} must match a set of constraints, you can use `forall` with a single pattern for simplicity. + +.Example rule with `forall` and a single pattern +[source] +---- +rule "All full-time employees have red ID badges" + when + forall( Employee( badgeColor = "red" ) ) + then + // True, all full-time employees have red ID badges. +end +---- + +You can use `forall` constructs with multiple patterns or nest them with other condition elements, such as inside a `not` element construct. + +.Example rule with `forall` and multiple patterns +[source] +---- +rule "All employees have health and dental care programs" + when + forall( $emp : Employee() + HealthCare( employee == $emp ) + DentalCare( employee == $emp ) + ) + then + // True, all employees have health and dental care. +end +---- + +.Example rule with `forall` and `not` +[source] +---- +rule "Not all employees have health and dental care" + when + not ( forall( $emp : Employee() + HealthCare( employee == $emp ) + DentalCare( employee == $emp ) ) + ) + then + // True, not all employees have health and dental care. +end +---- + +NOTE: The format `forall( p1 p2 p3 ...)` is equivalent to `not( p1 and not( and p2 p3 ... ) )`. + +-- + +`from`:: +Use this to specify a data source for a pattern. This enables the {RULE_ENGINE} to reason over data that is not in the working memory. The data source can be a sub-field on a bound variable or the result of a method call. The expression used to define the object source is any expression that follows regular MVEL syntax. Therefore, the `from` element enables you to easily use object property navigation, execute method calls, and access maps and collection elements. ++ +-- +ifdef::DROOLS,JBPM,OP[] +.from +image::language-reference/from.png[align="center"] +endif::[] + +.Example rule with `from` and pattern binding +[source] +---- +rule "Validate zipcode" + when + Person( $personAddress : address ) + Address( zipcode == "23920W" ) from $personAddress + then + // Zip code is okay. +end +---- + +.Example rule with `from` and a graph notation +[source] +---- +rule "Validate zipcode" + when + $p : Person() + $a : Address( zipcode == "23920W" ) from $p.address + then + // Zip code is okay. +end +---- + +.Example rule with `from` to iterate over all objects +[source] +---- +rule "Apply 10% discount to all items over US$ 100 in an order" + when + $order : Order() + $item : OrderItem( value > 100 ) from $order.items + then + // Apply discount to `$item`. +end +---- + +[NOTE] +==== +For large collections of objects, instead of adding an object with a large graph that the {RULE_ENGINE} must iterate over frequently, add the collection directly to the KIE session and then join the collection in the condition, as shown in the following example: + +[source] +---- +when + $order : Order() + OrderItem( value > 100, order == $order ) +---- +==== + +.Example rule with `from` and `lock-on-active` rule attribute +[source] +---- +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 the person to sales region 1. +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 the person. +end +---- + +[IMPORTANT] +==== +Using `from` with `lock-on-active` rule attribute can result in rules not being executed. You can address this issue in one of the following ways: + +* Avoid using the `from` element when you can insert all facts into the working memory of the {RULE_ENGINE} or use nested object references in your constraint expressions. +* Place the variable used in the `modify()` block as the last sentence in your rule condition. +* Avoid using the `lock-on-active` rule attribute when you can explicitly manage how rules within the same ruleflow group place activations on one another. +==== + +The pattern that contains a `from` clause cannot be followed by another pattern starting with a parenthesis. The reason for this restriction is that the DRL parser reads the `from` expression as `"from $l (String() or Number())"` and it cannot differentiate this expression from a function call. The simplest workaround to this is to wrap the `from` clause in parentheses, as shown in the following example: + +.Example rules with `from` used incorrectly and correctly +[source] +---- +// Do not use `from` in this way: +rule R + when + $l : List() + String() from $l + (String() or Number()) + then + // Actions +end + +// Use `from` in this way instead: +rule R + when + $l : List() + (String() from $l) + (String() or Number()) + then + // Actions +end +---- +-- + +`entry-point`:: +Use this to define an entry point, or _event stream_, corresponding to a data source for the pattern. This element is typically used with the `from` condition element. You can declare an entry point for events so that the {RULE_ENGINE} uses data from only that entry point to evaluate the rules. You can declare an entry point either implicitly by referencing it in DRL rules or explicitly in your Java application. ++ +-- +.Example rule with `from entry-point` +[source] +---- +rule "Authorize withdrawal" + when + WithdrawRequest( $ai : accountId, $am : amount ) from entry-point "ATM Stream" + CheckingAccount( accountId == $ai, balance > $am ) + then + // Authorize withdrawal. +end +---- + +.Example Java application code with EntryPoint object and inserted facts +[source,java] +---- +import org.kie.api.runtime.KieSession; +import org.kie.api.runtime.rule.EntryPoint; + +// Create your KIE base and KIE session as usual: +KieSession session = ... + +// Create a reference to the entry point: +EntryPoint atmStream = session.getEntryPoint("ATM Stream"); + +// Start inserting your facts into the entry point: +atmStream.insert(aWithdrawRequest); +---- +-- + +`collect`:: +Use this to define a collection of objects that the rule can use as part of the condition. The rule obtains the collection either from a specified source or from the working memory of the {RULE_ENGINE}. The result pattern of the `collect` element can be any concrete class that implements the `java.util.Collection` interface and provides a default no-arg public constructor. You can use Java collections like `List`, `LinkedList`, and `HashSet`, or your own class. If variables are bound before the `collect` element in a condition, you can use the variables to constrain both your source and result patterns. However, any binding made inside the `collect` element is not available for use outside of it. ++ +-- +ifdef::DROOLS,JBPM,OP[] +.Collect +image::language-reference/collect.png[align="center"] +endif::[] + +.Example rule with `collect` +[source] +---- +import java.util.List + +rule "Raise priority when system has more than three pending alarms" + when + $system : System() + $alarms : List( size >= 3 ) + from collect( Alarm( system == $system, status == 'pending' ) ) + then + // Raise priority because `$system` has three or more `$alarms` pending. +end +---- + +In this example, the rule assesses all pending alarms in the working memory of the {RULE_ENGINE} for each given system and groups them in a `List`. If three or more alarms are found for a given system, the rule is executed. + +You can also use the `collect` element with nested `from` elements, as shown in the following example: + +.Example rule with `collect` and nested `from` +[source] +---- +import java.util.LinkedList; + +rule "Send a message to all parents" + when + $town : Town( name == 'Paris' ) + $mothers : LinkedList() + from collect( Person( children > 0 ) + from $town.getPeople() + ) + then + // Send a message to all parents. +end +---- +-- + +`accumulate`:: +Use this to iterate over a collection of objects, execute custom actions for each of the elements, and return one or more result objects (if the constraints evaluate to `true`). This element is a more flexible and powerful form of the `collect` condition element. You can use predefined functions in your `accumulate` conditions or implement custom functions as needed. You can also use the abbreviation `acc` for `accumulate` in rule conditions. ++ +-- +Use the following format to define `accumulate` conditions in rules: + +.Preferred format for `accumulate` +[source] +---- +accumulate( ; [;] ) +---- + +ifdef::DROOLS,JBPM,OP[] +.Accumulate +image::language-reference/accumulate.png[align="center"] +endif::[] + +NOTE: Although the {RULE_ENGINE} supports alternate formats for the `accumulate` element for backward compatibility, this format is preferred for optimal performance in rules and applications. + +The {RULE_ENGINE} supports the following predefined `accumulate` functions. These functions accept any expression as input. + +* `average` +* `min` +* `max` +* `count` +* `sum` +* `collectList` +* `collectSet` + +In the following example rule, `min`, `max`, and `average` are `accumulate` functions that calculate the minimum, maximum, and average temperature values over all the readings for each sensor: + +.Example rule with `accumulate` to calculate temperature values +[source] +---- +rule "Raise alarm" + when + $s : Sensor() + accumulate( Reading( sensor == $s, $temp : temperature ); + $min : min( $temp ), + $max : max( $temp ), + $avg : average( $temp ); + $min < 20, $avg > 70 ) + then + // Raise the alarm. +end +---- + +The following example rule uses the `average` function with `accumulate` to calculate the average profit for all items in an order: + +.Example rule with `accumulate` to calculate average profit +[source] +---- +rule "Average profit" + when + $order : Order() + accumulate( OrderItem( order == $order, $cost : cost, $price : price ); + $avgProfit : average( 1 - $cost / $price ) ) + then + // Average profit for `$order` is `$avgProfit`. +end +---- + +To use custom, domain-specific functions in `accumulate` conditions, create a Java class that implements the `org.kie.api.runtime.rule.AccumulateFunction` interface. For example, the following Java class defines a custom implementation of an `AverageData` function: + +.Example Java class with custom implementation of `average` function +[source,java] +---- +// An implementation of an accumulator capable of calculating average values + +public class AverageAccumulateFunction implements org.kie.api.runtime.rule.AccumulateFunction { + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + + } + + public void writeExternal(ObjectOutput out) throws IOException { + + } + + public static class AverageData implements Externalizable { + public int count = 0; + public double total = 0; + + public AverageData() {} + + public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { + count = in.readInt(); + total = in.readDouble(); + } + + public void writeExternal(ObjectOutput out) throws IOException { + out.writeInt(count); + out.writeDouble(total); + } + + } + + /* (non-Javadoc) + * @see org.kie.api.runtime.rule.AccumulateFunction#createContext() + */ + public AverageData createContext() { + return new AverageData(); + } + + /* (non-Javadoc) + * @see org.kie.api.runtime.rule.AccumulateFunction#init(java.io.Serializable) + */ + public void init(AverageData context) { + context.count = 0; + context.total = 0; + } + + /* (non-Javadoc) + * @see org.kie.api.runtime.rule.AccumulateFunction#accumulate(java.io.Serializable, java.lang.Object) + */ + public void accumulate(AverageData context, + Object value) { + context.count++; + context.total += ((Number) value).doubleValue(); + } + + /* (non-Javadoc) + * @see org.kie.api.runtime.rule.AccumulateFunction#reverse(java.io.Serializable, java.lang.Object) + */ + public void reverse(AverageData context, Object value) { + context.count--; + context.total -= ((Number) value).doubleValue(); + } + + /* (non-Javadoc) + * @see org.kie.api.runtime.rule.AccumulateFunction#getResult(java.io.Serializable) + */ + public Object getResult(AverageData context) { + return new Double( context.count == 0 ? 0 : context.total / context.count ); + } + + /* (non-Javadoc) + * @see org.kie.api.runtime.rule.AccumulateFunction#supportsReverse() + */ + public boolean supportsReverse() { + return true; + } + + /* (non-Javadoc) + * @see org.kie.api.runtime.rule.AccumulateFunction#getResultType() + */ + public Class< ? > getResultType() { + return Number.class; + } + +} +---- + +To use the custom function in a DRL rule, import the function using the `import accumulate` statement: + +.Format to import a custom function +[source] +---- +import accumulate +---- + +.Example rule with the imported `average` function +[source] +---- +import accumulate AverageAccumulateFunction.AverageData average + +rule "Average profit" + when + $order : Order() + accumulate( OrderItem( order == $order, $cost : cost, $price : price ); + $avgProfit : average( 1 - $cost / $price ) ) + then + // Average profit for `$order` is `$avgProfit`. +end +---- + +ifdef::DROOLS,JBPM,OP[] +[NOTE] +==== +For backward compatibility, the {RULE_ENGINE} also supports the configuration of `accumulate` functions through configuration files and system properties, but this is a deprecated method. To configure the `average` function from the previous example using the configuration file or system property, set a property as shown in the following example: + +[source] +---- +drools.accumulate.function.average = AverageAccumulateFunction.AverageData +---- + +Note that `drools.accumulate.function` is a required prefix, `average` is how the function is used in the DRL files, and `AverageAccumulateFunction.AverageData` is the fully qualified name of the class that implements the function behavior. +==== +endif::[] +-- + +ifdef::DROOLS,JBPM,OP[] +`accumulate` alternate syntax for a single function with return type:: +The accumulate syntax evolved over time with the goal of becoming more compact and expressive. +Nevertheless, {PRODUCT} still supports previous syntaxes for backward compatibility purposes. ++ +-- +In case the rule is using a single accumulate function on a given accumulate, the author may add a pattern for the result object and use the "from" keyword to link it to the accumulate result. + +Example: a rule to apply a 10% discount on orders over $100 could be written in the following way: + +[source] +---- +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 previous example, the accumulate element is using only one function (sum), and so, the rules author opted to explicitly write a pattern for the result type of the accumulate function (Number) and write the constraints inside it. +There are no problems in using this syntax over the compact syntax presented before, except that is is a bit more verbose. +Also note that it is not allowed to use both the return type and the functions binding in the same accumulate statement. + +Compile-time checks are performed in order to ensure the pattern used with the $$"$$``from``$$"$$ keyword is assignable from the result of the accumulate function used. + +[NOTE] +==== +With this syntax, the $$"$$``from``$$"$$ binds to the single result returned by the accumulate function, and it does not iterate. +==== + +In the previous example, $$"$$``$total``$$"$$ is bound to the result returned by the accumulate sum() function. + +As another example however, if the result of the accumulate function is a collection, $$"$$``from``$$"$$ still binds to the single result and it does not iterate: + +[source] +---- +rule "Person names" +when + $x : Object() from accumulate(MyPerson( $val : name ); + collectList( $val ) ) +then + // $x is a List +end +---- + +The bound $$"$$``$x : Object()``$$"$$ is the List itself, returned by the collectList accumulate function used. + +This is an important distinction to highlight, as the $$"$$``from``$$"$$ keyword can also be used separately of accumulate, to iterate over the elements of a collection: + +[source] +---- +rule "Iterate the numbers" +when + $xs : List() + $x : Integer() from $xs +then + // $x matches and binds to each Integer in the collection +end +---- + +While this syntax is still supported for backward compatibility purposes, for this and other reasons we encourage rule authors to make use instead of the preferred `accumulate` syntax (described previously), to avoid any potential pitfalls. +-- + +`accumulate` with inline custom code:: +Another possible syntax for the `accumulate` is to define inline custom code, instead of using accumulate functions. ++ +-- +[WARNING] +==== +The use of accumulate with inline custom code is not a good practice for several reasons, including difficulties 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. + +Only limited support for inline accumulate is provided while using the executable model. +For example, you cannot use an external binding in the code while using the MVEL dialect: + +[source] +---- +rule R +dialect "mvel" +when + String( $l : length ) + $sum : Integer() from accumulate ( + Person( age > 18, $age : age ), + init( int sum = 0 * $l; ), + action( sum += $age; ), + reverse( sum -= $age; ), + result( sum ) + ) +---- +==== + +The general syntax of the `accumulate` CE with inline custom code is: + +[source] +---- + from accumulate( , + init( ), + action( ), + reverse( ), + result( ) ) +---- + + +The meaning of each of the elements is the following: + +* ____: the source pattern is a regular pattern that the {RULE_ENGINE} will try to match against each of the source objects. +* ____: this is a semantic block of code in the selected dialect that will be executed once for each tuple, before iterating over the source objects. +* ____: this is a semantic block of code in the selected dialect that will be executed for each of the source objects. +* ____: this is an optional semantic block of code in the selected dialect that if present will be executed for each source object that no longer matches the source pattern. The objective of this code block is to undo any calculation done in the __ block, so that the {RULE_ENGINE} can do decremental calculation when a source object is modified or deleted, hugely improving performance of these operations. +* ____: this is a semantic expression in the selected dialect that is executed after all source objects are iterated. +* ____: this is a regular pattern that the {RULE_ENGINE} tries to match against the object returned from the ____. If it matches, the `accumulate` conditional element evaluates to _true_ and the {RULE_ENGINE} proceeds with the evaluation of the next CE in the rule. If it does not matches, the `accumulate` CE evaluates to _false_ and the {RULE_ENGINE} stops evaluating CEs for that rule. + +It is easier to understand if we look at an example: + +[source] +---- +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 previous example, for each `Order` in the Working Memory, the {RULE_ENGINE} will execute the _init + code_ initializing the total variable to zero. +Then it will iterate over all `OrderItem` objects for that order, executing the _action_ for each one (in the example, it will sum the value of all items into the total variable). After iterating over all `OrderItem` objects, it will return the value corresponding to the _result + expression_ (in the previous example, the value of variable ``total``). Finally, the {RULE_ENGINE} will try to match the result with the `Number` pattern, and if the double value is greater than 100, the rule will fire. + +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 delete__. + +The `accumulate` CE can be used to execute any action on source objects. +The following example instantiates and populates a custom object: + +[source] +---- +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 +---- +-- + +`eval`:: +The conditional element `eval` is essentially a catch-all which allows any semantic code (that returns a primitive boolean) to be executed. +This code can refer to variables that were bound in the conditions of the rule and functions in the rule package. +Overuse of `eval` reduces the declarativeness of your rules and can result in a poorly performing {RULE_ENGINE}. +While `eval` can be used anywhere in the patterns, it is typically added as the last conditional element in the conditions of a rule. ++ +-- +.Eval +image::language-reference/eval.png[align="center"] + +Instances of `eval` 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 those who are familiar with {PRODUCT} 2.x lineage, the old {PRODUCT} parameter and condition tags are equivalent to binding a variable to an appropriate type, and then using it in an `eval` node. + +[source] +---- +p1 : Parameter() +p2 : Parameter() +eval( p1.getList().containsKey( p2.getItem() ) ) +---- + +{empty} + +[source] +---- +p1 : Parameter() +p2 : Parameter() +// call function isValid in the LHS +eval( isValid( p1, p2 ) ) +---- +-- +endif::[] diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-comments-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-comments-con.adoc new file mode 100644 index 00000000000..85925b68433 --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-comments-con.adoc @@ -0,0 +1,28 @@ +[id='drl-rules-comments-con_{context}'] + += Comments in DRL files + +DRL supports single-line comments prefixed with a double forward slash `//` and multi-line comments enclosed with a forward slash and asterisk `/* ... */`. You can use DRL comments to annotate rules or any related components in DRL files. DRL comments are ignored by the {RULE_ENGINE} when the DRL file is processed. + +.Example rule with comments +[source] +---- +rule "Underage" + // This is a single-line comment. + when + $application : LoanApplication() // This is an in-line comment. + Applicant( age < 21 ) + then + /* This is a multi-line comment + in the rule actions. */ + $application.setApproved( false ); + $application.setExplanation( "Underage" ); +end +---- + +ifdef::DROOLS,JBPM,OP[] +.Multi-line comment +image::language-reference/multi_line_comment.png[align="center"] +endif::[] + +IMPORTANT: The hash symbol `#` is not supported for DRL comments. diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-errors-ref.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-errors-ref.adoc new file mode 100644 index 00000000000..b59ec8c9f5c --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-errors-ref.adoc @@ -0,0 +1,207 @@ +[id='drl-rules-errors-ref_{context}'] + += Error messages for DRL troubleshooting + +{PRODUCT} provides standardized messages for DRL errors to help you troubleshoot and resolve problems in your DRL files. The error messages use the following format: + +.Error message format for DRL file problems +image::language-reference/error_message.png[align="center"] + +* *1st Block:* Error code +* *2nd Block:* Line and column in the DRL source where the error occurred +* *3rd Block:* Description of the problem +* *4th Block:* Component in the DRL source (rule, function, query) where the error occurred +* *5th Block:* Pattern in the DRL source where the error occurred (if applicable) + +{PRODUCT} supports the following standardized error messages: + +101: no viable alternative:: +Indicates that the parser reached a decision point but could not identify an alternative. ++ +-- +.Example rule with incorrect spelling +[source] +---- +1: rule "simple rule" +2: when +3: exists Person() +4: exits Student() // Must be `exists` +5: then +6: end +---- + +.Error message +[source] +---- +[ERR 101] Line 4:4 no viable alternative at input 'exits' in rule "simple rule" +---- + +.Example rule without a rule name +[source] +---- +1: package org.drools.examples; +2: rule // Must be `rule "rule name"` (or `rule rule_name` if no spacing) +3: when +4: Object() +5: then +6: System.out.println("A RHS"); +7: end +---- + +.Error message +[source] +---- +[ERR 101] Line 3:2 no viable alternative at input 'when' +---- + +In this example, the parser encountered the keyword `when` but expected the rule name, so it flags `when` as the incorrect expected token. + +.Example rule with incorrect syntax +[source] +---- +1: rule "simple rule" +2: when +3: Student( name == "Andy ) // Must be `"Andy"` +4: then +5: end +---- + +.Error message +[source] +---- +[ERR 101] Line 0:-1 no viable alternative at input '' in rule "simple rule" in pattern Student +---- + +NOTE: A line and column value of `0:-1` means the parser reached the end of the source file (``) but encountered incomplete constructs, usually due to missing quotation marks `"..."`, apostrophes `'...'`, or parentheses `(...)`. + +-- + +102: mismatched input:: +Indicates that the parser expected a particular symbol that is missing at the current input position. ++ +-- +.Example rule with an incomplete rule statement +[source] +---- +1: rule simple_rule +2: when +3: $p : Person( + // Must be a complete rule statement +---- + +.Error message +[source] +---- +[ERR 102] Line 0:-1 mismatched input '' expecting ')' in rule "simple rule" in pattern Person +---- + +NOTE: A line and column value of `0:-1` means the parser reached the end of the source file (``) but encountered incomplete constructs, usually due to missing quotation marks `"..."`, apostrophes `'...'`, or parentheses `(...)`. + +.Example rule with incorrect syntax +[source] +---- +1: package org.drools.examples; +2: +3: rule "Wrong syntax" +4: when +5: not( Car( ( type == "tesla", price == 10000 ) || ( type == "kia", price == 1000 ) ) from $carList ) + // Must use `&&` operators instead of commas `,` +6: then +7: System.out.println("OK"); +8: end +---- + +.Error messages +[source] +---- +[ERR 102] Line 5:36 mismatched input ',' expecting ')' in rule "Wrong syntax" in pattern Car +[ERR 101] Line 5:57 no viable alternative at input 'type' in rule "Wrong syntax" +[ERR 102] Line 5:106 mismatched input ')' expecting 'then' in rule "Wrong syntax" +---- + +In this example, the syntactic problem results in multiple error messages related to each other. The single solution of replacing the commas `,` with `&&` operators resolves all errors. If you encounter multiple errors, resolve one at a time in case errors are consequences of previous errors. +-- + +103: failed predicate:: +Indicates that a validating semantic predicate evaluated to `false`. These semantic predicates are typically used to identify component keywords in DRL files, such as `declare`, `rule`, `exists`, `not`, and others. ++ +-- +.Example rule with an invalid keyword +[source] +---- + 1: package nesting; + 2: + 3: import org.drools.compiler.Person + 4: import org.drools.compiler.Address + 5: + 6: Some text // Must be a valid DRL keyword + 7: + 8: rule "test something" + 9: when +10: $p: Person( name=="Michael" ) +11: then +12: $p.name = "other"; +13: System.out.println(p.name); +14: end +---- + +.Error message +[source] +---- +[ERR 103] Line 6:0 rule 'rule_key' failed predicate: {(validateIdentifierKey(DroolsSoftKeywords.RULE))}? in rule +---- + +The `Some text` line is invalid because it does not begin with or is not a part of a DRL keyword construct, so the parser fails to validate the rest of the DRL file. + +NOTE: This error is similar to `102: mismatched input`, but usually involves DRL keywords. + +-- + +104: trailing semi-colon not allowed:: +Indicates that an `eval()` clause in a rule condition uses a semicolon `;` but must not use one. ++ +-- +.Example rule with `eval()` and trailing semicolon +[source] +---- +1: rule "simple rule" +2: when +3: eval( abc(); ) // Must not use semicolon `;` +4: then +5: end +---- + +.Error message +[source] +---- +[ERR 104] Line 3:4 trailing semi-colon not allowed in rule "simple rule" +---- +-- + +105: did not match anything:: +Indicates that the parser reached a sub-rule in the grammar that must match an alternative at least once, but the sub-rule did not match anything. The parser has entered a branch with no way out. ++ +-- +.Example rule with invalid text in an empty condition +[source] +---- +1: rule "empty condition" +2: when +3: None // Must remove `None` if condition is empty +4: then +5: insert( new Person() ); +6: end +---- + +.Error message +[source] +---- +[ERR 105] Line 2:2 required (...)+ loop did not match anything at input 'WHEN' in rule "empty condition" +---- + +In this example, the condition is intended to be empty but the word `None` is used. This error is resolved by removing `None`, which is not a valid DRL keyword, data type, or pattern construct. +-- + +ifdef::DM,PAM[] +NOTE: If you encounter other DRL error messages that you cannot resolve, contact your Red Hat Technical Account Manager. +endif::[] diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-oopath-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-oopath-con.adoc new file mode 100644 index 00000000000..98acfd420e5 --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-oopath-con.adoc @@ -0,0 +1,158 @@ +[id='drl-rules-oopath-con_{context}'] + += OOPath syntax with graphs of objects in DRL rule conditions + +OOPath is an object-oriented syntax extension of XPath that is designed for browsing graphs of objects in DRL rule condition constraints. OOPath uses the compact notation from XPath for navigating through related elements while handling collections and filtering constraints, and is specifically useful for graphs of objects. + +When the field of a fact is a collection, you can use the `from` condition element (keyword) to bind and reason over all the items in that collection one by one. If you need to browse a graph of objects in the rule condition constraints, the extensive use of the `from` condition element results in a verbose and repetitive syntax, as shown in the following example: + +.Example rule that browses a graph of objects with `from` +[source] +---- +rule "Find all grades for Big Data exam" + when + $student: Student( $plan: plan ) + $exam: Exam( course == "Big Data" ) from $plan.exams + $grade: Grade() from $exam.grades + then + // Actions +end +---- + +In this example, the domain model contains a `Student` object with a `Plan` of study. The `Plan` can have zero or more `Exam` instances and an `Exam` can have zero or more `Grade` instances. Only the root object of the graph, the `Student` in this case, needs to be in the working memory of the {RULE_ENGINE} for this rule setup to function. + +As a more efficient alternative to using extensive `from` statements, you can use the abbreviated OOPath syntax, as shown in the following example: + +.Example rule that browses a graph of objects with OOPath syntax +[source] +---- +rule "Find all grades for Big Data exam" + when + Student( $grade: /plan/exams[course == "Big Data"]/grades ) + then + // Actions +end +---- + +Formally, the core grammar of an OOPath expression is defined in extended Backus-Naur form (EBNF) notation in the following way: + +.EBNF notation for OOPath expressions +[source] +---- +OOPExpr = [ID ( ":" | ":=" )] ( "/" | "?/" ) OOPSegment { ( "/" | "?/" | "." ) OOPSegment } ; +OOPSegment = ID ["#" ID] ["[" ( Number | Constraints ) "]"] +---- + +In practice, an OOPath expression has the following features and capabilities: + +* Starts with a forward slash `/` or with a question mark and forward slash `?/` if it is a non-reactive OOPath expression (described later in this section). +* Can dereference a single property of an object with the period `.` operator. +* Can dereference multiple properties of an object with the forward slash `/` operator. If a collection is returned, the expression iterates over the values in the collection. +* Can filter out traversed objects that do not satisfy one or more constraints. The constraints are written as predicate expressions between square brackets, as shown in the following example: ++ +.Constraints as a predicate expression +[source] +---- +Student( $grade: /plan/exams[ course == "Big Data" ]/grades ) +---- +* Can downcast a traversed object to a subclass of the class declared in the generic collection. Subsequent constraints can also safely access the properties declared only in that subclass, as shown in the following example. Objects that are not instances of the class specified in this inline cast are automatically filtered out. ++ +.Constraints with downcast objects +[source] +---- +Student( $grade: /plan/exams#AdvancedExam[ course == "Big Data", level > 3 ]/grades ) +---- +* Can backreference an object of the graph that was traversed before the currently iterated graph. For example, the following OOPath expression matches only the grades that are above the average for the passed exam: ++ +.Constraints with backreferenced object +[source] +---- +Student( $grade: /plan/exams/grades[ result > ../averageResult ] ) +---- +* Can recursively be another OOPath expression, as shown in the following example: ++ +.Recursive constraint expression +[source] +---- +Student( $exam: /plan/exams[ /grades[ result > 20 ] ] ) +---- +* Can access objects by their index between square brackets `[]`, as shown in the following example. To adhere to Java convention, OOPath indexes are 0-based, while XPath indexes are 1-based. ++ +.Constraints with access to objects by index +[source] +---- +Student( $grade: /plan/exams[0]/grades ) +---- + +OOPath expressions can be reactive or non-reactive. The {RULE_ENGINE} does not react to updates involving a deeply nested object that is traversed during the evaluation of an OOPath expression. + +To make these objects reactive to changes, modify the objects to extend the class `org.drools.core.phreak.ReactiveObject`. After you modify an object to extend the `ReactiveObject` class, the domain object invokes the inherited method `notifyModification` to notify the {RULE_ENGINE} when one of the fields has been updated, as shown in the following example: + +.Example object method to notify the {RULE_ENGINE} that an exam has been moved to a different course +[source,java] +---- +public void setCourse(String course) { + this.course = course; + notifyModification(this); + } +---- + +With the following corresponding OOPath expression, when an exam is moved to a different course, the rule is re-executed and the list of grades matching the rule is recomputed: + +.Example OOPath expression from "Big Data" rule +[source] +---- +Student( $grade: /plan/exams[ course == "Big Data" ]/grades ) +---- + +You can also use the `?/` separator instead of the `/` separator to disable reactivity in only one sub-portion of an OOPath expression, as shown in the following example: + +.Example OOPath expression that is partially non-reactive +[source] +---- +Student( $grade: /plan/exams[ course == "Big Data" ]?/grades ) +---- + +With this example, the {RULE_ENGINE} reacts to a change made to an exam or if an exam is added to the plan, but not if a new grade is added to an existing exam. + +If an OOPath portion is non-reactive, all remaining portions of the OOPath expression also become non-reactive. For example, the following OOPath expression is completely non-reactive: + +.Example OOPath expression that is completely non-reactive +[source] +---- +Student( $grade: ?/plan/exams[ course == "Big Data" ]/grades ) +---- + +For this reason, you cannot use the `?/` separator more than once in the same OOPath expression. For example, the following expression causes a compilation error: + +.Example OOPath expression with duplicate non-reactivity markers +[source] +---- +Student( $grade: /plan?/exams[ course == "Big Data" ]?/grades ) +---- + +Another alternative for enabling OOPath expression reactivity is to use the dedicated implementations for `List` and `Set` interfaces in {PRODUCT}. These implementations are the `ReactiveList` and `ReactiveSet` classes. A `ReactiveCollection` class is also available. The implementations also provide reactive support for performing mutable operations through the `Iterator` and `ListIterator` classes. + +The following example class uses these classes to configure OOPath expression reactivity: + +.Example Java class to configure OOPath expression reactivity +[source,java] +---- +public class School extends AbstractReactiveObject { + private String name; + private final List children = new ReactiveList(); // <1> + + public void setName(String name) { + this.name = name; + notifyModification(); // <2> + } + + public void addChild(Child child) { + children.add(child); // <3> + // No need to call `notifyModification()` here + } + } +---- +<1> Uses the `ReactiveList` instance for reactive support over the standard Java `List` instance. +<2> Uses the required `notifyModification()` method for when a field is changed in reactive support. +<3> The `children` field is a `ReactiveList` instance, so the `notifyModification()` method call is not required. The notification is handled automatically, like all other mutating operations performed over the `children` field. diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-traditional.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-traditional.adoc new file mode 100644 index 00000000000..8e9e55ad8b9 --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-traditional.adoc @@ -0,0 +1,5 @@ +[id="con-drl_{context}] += Traditional DRL Syntax + +[role="_abstract"] +This chapter explains the traditional DRL syntax. This syntax can be used instead of the RuleUnit and OOPath based syntax. The traditional syntax is still fully supported. diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-timers-calendars-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-timers-calendars-con.adoc new file mode 100644 index 00000000000..6de35c1795d --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-timers-calendars-con.adoc @@ -0,0 +1,173 @@ +[id='drl-timers-calendars-con_{context}'] += Timer and calendar rule attributes in DRL + +Timers and calendars are DRL rule attributes that enable you to apply scheduling and timing constraints to your DRL rules. These attributes require additional configurations depending on the use case. + +The `timer` attribute in DRL rules is a string identifying either `int` (interval) or `cron` timer definitions for scheduling a rule and supports the following formats: + +.Timer attribute formats +[source] +---- +timer ( int: ) + +timer ( cron: ) +---- + +.Example interval timer attributes +[source] +---- +// Run after a 30-second delay +timer ( int: 30s ) + +// Run every 5 minutes after a 30-second delay each time +timer ( int: 30s 5m ) +---- + +.Example cron timer attribute +[source] +---- +// Run every 15 minutes +timer ( cron:* 0/15 * * * ? ) +---- + +Interval timers follow the semantics of `java.util.Timer` objects, with an initial delay and an optional repeat interval. Cron timers follow standard Unix cron expressions. + +The following example DRL rule uses a cron timer to send an SMS text message every 15 minutes: + +.Example DRL rule with a cron timer +[source] +---- +rule "Send SMS message every 15 minutes" + timer ( cron:* 0/15 * * * ? ) + when + $a : Alarm( on == true ) + then + channels[ "sms" ].insert( new Sms( $a.mobileNumber, "The alarm is still on." ); +end +---- + +Generally, a rule that is controlled by a timer becomes active when the rule is triggered and the rule consequence is executed repeatedly, according to the timer settings. The execution stops when the rule condition no longer matches incoming facts. However, the way the {RULE_ENGINE} handles rules with timers depends on whether the {RULE_ENGINE} is in _active mode_ or in _passive mode_. + +By default, the {RULE_ENGINE} runs in _passive mode_ and evaluates rules, according to the defined timer settings, when a user or an application explicitly calls `fireAllRules()`. Conversely, if a user or application calls `fireUntilHalt()`, the {RULE_ENGINE} starts in _active mode_ and evaluates rules continually until the user or application explicitly calls `halt()`. + +When the {RULE_ENGINE} is in active mode, rule consequences are executed even after control returns from a call to `fireUntilHalt()` and the {RULE_ENGINE} remains _reactive_ to any changes made to the working memory. For example, removing a fact that was involved in triggering the timer rule execution causes the repeated execution to terminate, and inserting a fact so that some rule matches causes that rule to be executed. However, the {RULE_ENGINE} is not continually _active_, but is active only after a rule is executed. Therefore, the {RULE_ENGINE} does not react to asynchronous fact insertions until the next execution of a timer-controlled rule. Disposing a KIE session terminates all timer activity. + +When the {RULE_ENGINE} is in passive mode, rule consequences of timed rules are evaluated only when `fireAllRules()` is invoked again. However, you can change the default timer-execution behavior in passive mode by configuring the KIE session with a `TimedRuleExecutionOption` option, as shown in the following example: + +.KIE session configuration to automatically execute timed rules in passive mode +[source,java] +---- +KieSessionConfiguration ksconf = KieServices.Factory.get().newKieSessionConfiguration(); +ksconf.setOption( TimedRuleExecutionOption.YES ); +KSession ksession = kbase.newKieSession(ksconf, null); +---- + +You can additionally set a `FILTERED` specification on the `TimedRuleExecutionOption` option that enables you to define a +callback to filter those rules, as shown in the following example: + +.KIE session configuration to filter which timed rules are automatically executed +[source,java] +---- +KieSessionConfiguration ksconf = KieServices.Factory.get().newKieSessionConfiguration(); +conf.setOption( new TimedRuleExecutionOption.FILTERED(new TimedRuleExecutionFilter() { + public boolean accept(Rule[] rules) { + return rules[0].getName().equals("MyRule"); + } +}) ); +---- + +For interval timers, you can also use an expression timer with `expr` instead of `int` to define both the delay and interval as an expression instead of a fixed value. + +The following example DRL file declares a fact type with a delay and period that are then used in the subsequent rule with an expression timer: + +.Example rule with an expression timer +[source] +---- +declare Bean + delay : String = "30s" + period : long = 60000 +end + +rule "Expression timer" + timer ( expr: $d, $p ) + when + Bean( $d : delay, $p : period ) + then + // Actions +end +---- + +The expressions, such as `$d` and `$p` in this example, can use any variable defined in the pattern-matching part of the rule. The variable can be any `String` value that can be parsed into a time duration or any numeric value that is internally converted in a `long` value for a duration in milliseconds. + +Both interval and expression timers can use the following optional parameters: + +* `start` and `end`: A `Date` or a `String` representing a `Date` or a `long` value. The value can also be a `Number` that is transformed into a Java `Date` in the format `new Date( ((Number) n).longValue() )`. +* `repeat-limit`: An integer that defines the maximum number of repetitions allowed by the timer. If both the `end` and the `repeat-limit` parameters are set, the timer stops when the first of the two is reached. + +.Example timer attribute with optional `start`, `end`, and `repeat-limit` parameters +[source,java] +---- +timer (int: 30s 1h; start=3-JAN-2020, end=4-JAN-2020, repeat-limit=50) +---- + +In this example, the rule is scheduled for every hour, after a delay of 30 seconds each hour, beginning on 3 January 2020 and ending either on 4 January 2020 or when the cycle repeats 50 times. + +If the system is paused (for example, the session is serialized and then later deserialized), the rule is scheduled only one time to recover from missing activations regardless of how many activations were missed during the pause, and then the rule is subsequently scheduled again to continue in sync with the timer setting. + +The `calendar` attribute in DRL rules is a http://www.quartz-scheduler.org/[Quartz] calendar definition for scheduling a rule and supports the following format: + +.Calendar attribute format +[source] +---- +calendars "" +---- + +.Example calendar attributes +[source] +---- +// Exclude non-business hours +calendars "* * 0-7,18-23 ? * *" + +// Weekdays only, as registered in the KIE session +calendars "weekday" +---- + +You can adapt a Quartz calendar based on the Quartz calendar API and then register the calendar in the KIE session, as shown in the following example: + +.Adapting a Quartz Calendar +[source,java] +---- +Calendar weekDayCal = QuartzHelper.quartzCalendarAdapter(org.quartz.Calendar quartzCal) +---- + +.Registering the calendar in the KIE session +[source,java] +---- +ksession.getCalendars().set( "weekday", weekDayCal ); +---- + +You can use calendars with standard rules and with rules that use timers. The calendar attribute can contain one or more comma-separated calendar names written as `String` literals. + +The following example rules use both calendars and timers to schedule the rules: + +.Example rules with calendars and timers +[source] +---- +rule "Weekdays are high priority" + calendars "weekday" + timer ( int:0 1h ) + when + Alarm() + then + send( "priority high - we have an alarm" ); +end + +rule "Weekends are low priority" + calendars "weekend" + timer ( int:0 4h ) + when + Alarm() + then + send( "priority low - we have an alarm" ); +end +---- diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/index.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/index.adoc new file mode 100644 index 00000000000..3f34f629a6e --- /dev/null +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/index.adoc @@ -0,0 +1,62 @@ +:DROOLS: +include::../_artifacts/document-attributes.adoc[] + +[[_droolslanguagereferencetraditionalchapter]] += Rule Language Reference (Traditional) +:context: drl-rules-traditional + +include::_drl-rules-traditional.adoc[leveloffset=+1] + +include::_drl-packages-con.adoc[leveloffset=+2] + +include::_drl-imports-con.adoc[leveloffset=+2] + +include::_drl-functions-con.adoc[leveloffset=+2] + +include::_drl-queries-con.adoc[leveloffset=+2] + +include::_drl-declarations-con.adoc[leveloffset=+2] + +include::_drl-declarations-without-metadata-con.adoc[leveloffset=+3] + +include::_drl-declarations-enumerative-con.adoc[leveloffset=+3] + +include::_drl-declarations-extended-con.adoc[leveloffset=+3] + +include::_drl-declarations-with-metadata-con.adoc[leveloffset=+3] + +include::_drl-declarations-metadata-tags-ref.adoc[leveloffset=+3] + +include::_drl-declarations-access-con.adoc[leveloffset=+3] + +include::_drl-globals-con.adoc[leveloffset=+2] + +include::_drl-timers-calendars-con.adoc[leveloffset=+3] + +include::_drl-rules-WHEN-con.adoc[leveloffset=+2] + +include::_drl-operators-ref.adoc[leveloffset=+3] + +include::_drl-operator-precedence-ref.adoc[leveloffset=+3] + +include::_drl-rules-WHEN-elements-ref.adoc[leveloffset=+3] + +include::_drl-rules-oopath-con.adoc[leveloffset=+3] + +include::_drl-rules-WHEN-elements-diagrams-ref.adoc[leveloffset=+3] + +include::_drl-rules-THEN-con.adoc[leveloffset=+2] + +include::_drl-rules-THEN-methods-ref.adoc[leveloffset=+3] + +include::_drl-rules-THEN-methods-variables-ref.adoc[leveloffset=+3] + +include::_drl-rules-THEN-advanced-con.adoc[leveloffset=+3] + +include::_drl-rules-comments-con.adoc[leveloffset=+2] + +include::_drl-rules-errors-ref.adoc[leveloffset=+2] + +include::_drl-rule-units-con.adoc[leveloffset=+2] + +include::_DSL-section.adoc[leveloffset=+1] diff --git a/drools-docs/src/modules/ROOT/pages/language-reference/_drl-rules.adoc b/drools-docs/src/modules/ROOT/pages/language-reference/_drl-rules.adoc index 4d5142347e0..07aefbce971 100644 --- a/drools-docs/src/modules/ROOT/pages/language-reference/_drl-rules.adoc +++ b/drools-docs/src/modules/ROOT/pages/language-reference/_drl-rules.adoc @@ -70,6 +70,8 @@ rule "Underage" end ---- +For the traditional syntax, please refer to xref:ref-con-drl_drl-rules-traditional[] + [id="con-drl-packages_{context}"] == Packages in DRL From 0d0ab464fdf8833f0f75f871722b253da1b88be2 Mon Sep 17 00:00:00 2001 From: Toshiya Kobayashi Date: Mon, 22 Jan 2024 19:56:31 +0900 Subject: [PATCH 2/2] - Remove ruleunit and oopath from traditional syntax chapter --- .../_drl-rule-units-con.adoc | 603 ------------------ ..._drl-rules-WHEN-elements-diagrams-ref.adoc | 188 ------ .../_drl-rules-oopath-con.adoc | 158 ----- .../language-reference-traditional/index.adoc | 6 - 4 files changed, 955 deletions(-) delete mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rule-units-con.adoc delete mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-WHEN-elements-diagrams-ref.adoc delete mode 100644 drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-oopath-con.adoc diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rule-units-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rule-units-con.adoc deleted file mode 100644 index a907b88ee2d..00000000000 --- a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rule-units-con.adoc +++ /dev/null @@ -1,603 +0,0 @@ -[id='drl-rule-units-con_{context}'] - -= Rule units in DRL rule sets - -WARNING: Rule units are experimental in Drools 7. Only supported in {KOGITO}. - -Rule units are groups of data sources, global variables, and DRL rules that function together for a specific purpose. You can use rule units to partition a rule set into smaller units, bind different data sources to those units, and then execute the individual unit. Rule units are an enhanced alternative to rule-grouping DRL attributes such as rule agenda groups or activation groups for execution control. - -Rule units are helpful when you want to coordinate rule execution so that the complete execution of one rule unit triggers the start of another rule unit and so on. For example, assume that you have a set of rules for data enrichment, another set of rules that processes that data, and another set of rules that extract the output from the processed data. If you add these rule sets into three distinct rule units, you can coordinate those rule units so that complete execution of the first unit triggers the start of the second unit and the complete execution of the second unit triggers the start of third unit. - -To define a rule unit, implement the `RuleUnit` interface as shown in the following example: - -.Example rule unit class -[source,java] ----- -package org.mypackage.myunit; - -public static class AdultUnit implements RuleUnit { - private int adultAge; - private DataSource persons; - - public AdultUnit( ) { } - - public AdultUnit( DataSource persons, int age ) { - this.persons = persons; - this.age = age; - } - - // A data source of `Persons` in this rule unit: - public DataSource getPersons() { - return persons; - } - - // A global variable in this rule unit: - public int getAdultAge() { - return adultAge; - } - - // Life-cycle methods: - @Override - public void onStart() { - System.out.println("AdultUnit started."); - } - - @Override - public void onEnd() { - System.out.println("AdultUnit ended."); - } -} ----- - -In this example, `persons` is a source of facts of type `Person`. A rule unit data source is a source of the data processed by a given rule unit and represents the entry point that the {RULE_ENGINE} uses to evaluate the rule unit. The `adultAge` global variable is accessible from all the rules belonging to this rule unit. The last two methods are part of the rule unit life cycle and are invoked by the {RULE_ENGINE}. - -The {RULE_ENGINE} supports the following optional life-cycle methods for rule units: - -[cols="2", options="header"] -.Rule unit life-cycle methods -|=== -|Method -|Invoked when - -|`onStart()` -|Rule unit execution starts - -|`onEnd()` -|Rule unit execution ends - -|`onSuspend()` -|Rule unit execution is suspended (used only with `runUntilHalt()`) - -|`onResume()` -|Rule unit execution is resumed (used only with `runUntilHalt()`) - -|`onYield(RuleUnit other)` -|The consequence of a rule in the rule unit triggers the execution of a different rule unit -|=== - -You can add one or more rules to a rule unit. By default, all the rules in a DRL file are automatically associated with a rule unit that follows the naming convention of the DRL file name. If the DRL file is in the same package and has the same name as a class that implements the `RuleUnit` interface, then all of the rules in that DRL file implicitly belong to that rule unit. For example, all the rules in the `AdultUnit.drl` file in the `org.mypackage.myunit` package are automatically part of the rule unit `org.mypackage.myunit.AdultUnit`. - -To override this naming convention and explicitly declare the rule unit that the rules in a DRL file belong to, use the `unit` keyword in the DRL file. The `unit` declaration must immediately follow the package declaration and contain the name of the class in that package that the rules in the DRL file are part of. - -.Example rule unit declaration in a DRL file -[source] ----- -package org.mypackage.myunit -unit AdultUnit - -rule Adult - when - $p : Person(age >= adultAge) from persons - then - System.out.println($p.getName() + " is adult and greater than " + adultAge); -end ----- - -WARNING: Do not mix rules with and without a rule unit in the same KIE base. Mixing two rule paradigms in a KIE base results in a compilation error. - -You can also rewrite the same pattern in a more convenient way using OOPath notation, as shown in the following example: - -.Example rule unit declaration in a DRL file that uses OOPath notation -[source] ----- -package org.mypackage.myunit -unit AdultUnit - -rule Adult - when - $p : /persons[age >= adultAge] - then - System.out.println($p.getName() + " is adult and greater than " + adultAge); -end ----- - -NOTE: OOPath is an object-oriented syntax extension of XPath that is designed for browsing graphs of objects in DRL rule condition constraints. OOPath uses the compact notation from XPath for navigating through related elements while handling collections and filtering constraints, and is specifically useful for graphs of objects. - -In this example, any matching facts in the rule conditions are retrieved from the `persons` data source defined in the `DataSource` definition in the rule unit class. The rule condition and action use the `adultAge` variable in the same way that a global variable is defined at the DRL file level. - -To execute one or more rule units defined in a KIE base, create a new `RuleUnitExecutor` class bound to the KIE base, create the rule unit from the relevant data source, and run the rule unit executer: - -.Example rule unit execution -[source,java] ----- -// Create a `RuleUnitExecutor` class and bind it to the KIE base: -KieBase kbase = kieContainer.getKieBase(); -RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); - -// Create the `AdultUnit` rule unit using the `persons` data source and run the executor: -RuleUnit adultUnit = new AdultUnit(persons, 18); -executor.run( adultUnit ); ----- - -Rules are executed by the `RuleUnitExecutor` class. The `RuleUnitExecutor` class creates KIE sessions and adds the required `DataSource` objects to those sessions, and then executes the rules based on the `RuleUnit` that is passed as a parameter to the `run()` method. - -The example execution code produces the following output when the relevant `Person` facts are inserted in the `persons` data source: - -.Example rule unit execution output -[source] ----- -org.mypackage.myunit.AdultUnit started. -Jane is adult and greater than 18 -John is adult and greater than 18 -org.mypackage.myunit.AdultUnit ended. ----- - -Instead of explicitly creating the rule unit instance, you can register the rule unit variables in the executor and pass to the executor the rule unit class that you want to run, and then the executor creates an instance of the rule unit. You can then set the `DataSource` definition and other variables as needed before running the rule unit. - -.Alternate rule unit execution option with registered variables -[source,java] ----- -executor.bindVariable( "persons", persons ); - .bindVariable( "adultAge", 18 ); -executor.run( AdultUnit.class ); ----- - -The name that you pass to the `RuleUnitExecutor.bindVariable()` method is used at run time to bind the variable to the field of the rule unit class with the same name. In the previous example, the `RuleUnitExecutor` inserts into the new rule unit the data source bound to the `"persons"` name and inserts the value `18` bound to the String `"adultAge"` into the fields with the corresponding names inside the `AdultUnit` class. - -To override this default variable-binding behavior, use the `@UnitVar` annotation to explicitly define a logical binding name for each field of the rule unit class. For example, the field bindings in the following class are redefined with alternative names: - -.Example code to modify variable binding names with `@UnitVar` -[source,java] ----- -package org.mypackage.myunit; - -public static class AdultUnit implements RuleUnit { - @UnitVar("minAge") - private int adultAge = 18; - - @UnitVar("data") - private DataSource persons; -} ----- - -You can then bind the variables to the executor using those alternative names and run the rule unit: - -.Example rule unit execution with modified variable names -[source,java] ----- -executor.bindVariable( "data", persons ); - .bindVariable( "minAge", 18 ); -executor.run( AdultUnit.class ); ----- - -You can execute a rule unit in _passive mode_ by using the `run()` method (equivalent to invoking `fireAllRules()` on a KIE session) -or in _active mode_ using the `runUntilHalt()` method (equivalent to invoking `fireUntilHalt()` on a KIE session). By default, the {RULE_ENGINE} runs in _passive mode_ and evaluates rule units only when a user or an application explicitly calls `run()` (or `fireAllRules()` for standard rules). If a user or application calls `runUntilHalt()` for rule units (or `fireUntilHalt()` for standard rules), the {RULE_ENGINE} starts in _active mode_ and evaluates rule units continually until the user or application explicitly calls `halt()`. - -If you use the `runUntilHalt()` method, invoke the method on a separate execution thread to avoid blocking the main thread: - -.Example rule unit execution with `runUntilHalt()` on a separate thread -[source,java] ----- -new Thread( () -> executor.runUntilHalt( adultUnit ) ).start(); ----- - -== Data sources for rule units - -A rule unit data source is a source of the data processed by a given rule unit and represents the entry point that the {RULE_ENGINE} uses to evaluate the rule unit. A rule unit can have zero or more data sources and each `DataSource` definition declared inside a rule unit can correspond to a different entry point into the rule unit executor. Multiple rule units can share a single data source, but each rule unit must use different entry points through which the same objects are inserted. - -You can create a `DataSource` definition with a fixed set of data in a rule unit class, as shown in the following example: - -.Example data source definition -[source,java] ----- -DataSource persons = DataSource.create( new Person( "John", 42 ), - new Person( "Jane", 44 ), - new Person( "Sally", 4 ) ); ----- - -Because a data source represents the entry point of the rule unit, you can insert, update, or delete facts in a rule unit: - -.Example code to insert, modify, and delete a fact in a rule unit -[source,java] ----- -// Insert a fact: -Person john = new Person( "John", 42 ); -FactHandle johnFh = persons.insert( john ); - -// Modify the fact and optionally specify modified properties (for property reactivity): -john.setAge( 43 ); -persons.update( johnFh, john, "age" ); - -// Delete the fact: -persons.delete( johnFh ); ----- - -== Rule unit execution control - -Rule units are helpful when you want to coordinate rule execution so that the execution of one rule unit triggers the start of another rule unit and so on. - -To facilitate rule unit execution control, the {RULE_ENGINE} supports the following rule unit methods that you can use in DRL rule actions to coordinate the execution of rule units: - -* `drools.run()`: Triggers the execution of a specified rule unit class. This method imperatively interrupts the execution of the rule unit and activates the other specified rule unit. -* `drools.guard()`: Prevents (guards) a specified rule unit class from being executed until the associated rule condition is met. This method declaratively schedules the execution of the other specified rule unit. When the {RULE_ENGINE} produces at least one match for the condition in the guarding rule, the guarded rule unit is considered active. A rule unit can contain multiple guarding rules. - -As an example of the `drools.run()` method, consider the following DRL rules that each belong to a specified rule unit. The `NotAdult` rule uses the `drools.run( AdultUnit.class )` method to trigger the execution of the `AdultUnit` rule unit: - -.Example DRL rules with controlled execution using `drools.run()` -[source] ----- -package org.mypackage.myunit -unit AdultUnit - -rule Adult - when - Person(age >= 18, $name : name) from persons - then - System.out.println($name + " is adult"); -end ----- - -[source] ----- -package org.mypackage.myunit -unit NotAdultUnit - -rule NotAdult - when - $p : Person(age < 18, $name : name) from persons - then - System.out.println($name + " is NOT adult"); - modify($p) { setAge(18); } - drools.run( AdultUnit.class ); -end ----- - -The example also uses a `RuleUnitExecutor` class created from the KIE base that was built from these rules and a `DataSource` definition of `persons` bound to it: - -.Example rule executor and data source definitions -[source,java] ----- -RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); -DataSource persons = executor.newDataSource( "persons", - new Person( "John", 42 ), - new Person( "Jane", 44 ), - new Person( "Sally", 4 ) ); ----- - -In this case, the example creates the `DataSource` definition directly from the `RuleUnitExecutor` class and binds it to the `"persons"` variable in a single statement. - -The example execution code produces the following output when the relevant `Person` facts are inserted in the `persons` data source: - -.Example rule unit execution output -[source] ----- -Sally is NOT adult -John is adult -Jane is adult -Sally is adult ----- - -The `NotAdult` rule detects a match when evaluating the person `"Sally"`, who is under 18 years old. The rule then modifies -her age to `18` and uses the `drools.run( AdultUnit.class )` method to trigger the execution of the `AdultUnit` rule unit. The `AdultUnit` rule unit contains a rule that can now be executed for all of the 3 `persons` in the `DataSource` definition. - -As an example of the `drools.guard()` method, consider the following `BoxOffice` class and `BoxOfficeUnit` rule unit class: - -.Example `BoxOffice` class -[source,java] ----- -public class BoxOffice { - private boolean open; - - public BoxOffice( boolean open ) { - this.open = open; - } - - public boolean isOpen() { - return open; - } - - public void setOpen( boolean open ) { - this.open = open; - } -} ----- - -.Example `BoxOfficeUnit` rule unit class -[source,java] ----- -public class BoxOfficeUnit implements RuleUnit { - private DataSource boxOffices; - - public DataSource getBoxOffices() { - return boxOffices; - } -} ----- - -The example also uses the following `TicketIssuerUnit` rule unit class to keep selling box office tickets for the event as long as at least one box office is open. This rule unit uses `DataSource` definitions of `persons` and `tickets`: - -.Example `TicketIssuerUnit` rule unit class -[source,java] ----- -public class TicketIssuerUnit implements RuleUnit { - private DataSource persons; - private DataSource tickets; - - private List results; - - public TicketIssuerUnit() { } - - public TicketIssuerUnit( DataSource persons, DataSource tickets ) { - this.persons = persons; - this.tickets = tickets; - } - - public DataSource getPersons() { - return persons; - } - - public DataSource getTickets() { - return tickets; - } - - public List getResults() { - return results; - } -} ----- - -The `BoxOfficeUnit` rule unit contains a `BoxOfficeIsOpen` DRL rule that uses the `drools.guard( TicketIssuerUnit.class )` method to guard the execution of the `TicketIssuerUnit` rule unit that distributes the event tickets, as shown in the following DRL rule examples: - - -.Example DRL rules with controlled execution using `drools.guard()` -[source] ----- -package org.mypackage.myunit; -unit TicketIssuerUnit; - -rule IssueAdultTicket when - $p: /persons[ age >= 18 ] -then - tickets.insert(new AdultTicket($p)); -end -rule RegisterAdultTicket when - $t: /tickets -then - results.add( $t.getPerson().getName() ); -end ----- - -[source] ----- -package org.mypackage.myunit; -unit BoxOfficeUnit; - -rule BoxOfficeIsOpen - when - $box: /boxOffices[ open ] - then - drools.guard( TicketIssuerUnit.class ); -end ----- - -In this example, so long as at least one box office is `open`, the guarded `TicketIssuerUnit` rule unit is active and distributes event tickets. When no more box offices are in `open` state, the guarded `TicketIssuerUnit` rule unit is prevented from being executed. - -The following example class illustrates a more complete box office scenario: - -.Example class for the box office scenario -[source,java] ----- -DataSource persons = executor.newDataSource( "persons" ); -DataSource boxOffices = executor.newDataSource( "boxOffices" ); -DataSource tickets = executor.newDataSource( "tickets" ); - -List list = new ArrayList<>(); -executor.bindVariable( "results", list ); - -// Two box offices are open: -BoxOffice office1 = new BoxOffice(true); -FactHandle officeFH1 = boxOffices.insert( office1 ); -BoxOffice office2 = new BoxOffice(true); -FactHandle officeFH2 = boxOffices.insert( office2 ); - -persons.insert(new Person("John", 40)); - -// Execute `BoxOfficeIsOpen` rule, run `TicketIssuerUnit` rule unit, and execute `RegisterAdultTicket` rule: -executor.run(BoxOfficeUnit.class); - -assertEquals( 1, list.size() ); -assertEquals( "John", list.get(0) ); -list.clear(); - -persons.insert(new Person("Matteo", 30)); - -// Execute `RegisterAdultTicket` rule: -executor.run(BoxOfficeUnit.class); - -assertEquals( 1, list.size() ); -assertEquals( "Matteo", list.get(0) ); -list.clear(); - -// One box office is closed, the other is open: -office1.setOpen(false); -boxOffices.update(officeFH1, office1); -persons.insert(new Person("Mark", 35)); -executor.run(BoxOfficeUnit.class); - -assertEquals( 1, list.size() ); -assertEquals( "Mark", list.get(0) ); -list.clear(); - -// All box offices are closed: -office2.setOpen(false); -boxOffices.update(officeFH2, office2); // Guarding rule is no longer true. -persons.insert(new Person("Edson", 35)); -executor.run(BoxOfficeUnit.class); // No execution - -assertEquals( 0, list.size() ); ----- - -== Rule unit identity conflicts - -In rule unit execution scenarios with guarded rule units, a rule can guard multiple rule units and at the same time a rule unit can be guarded and then activated by multiple rules. For these two-way guarding scenarios, rule units must have a clearly defined identity to avoid identity conflicts. - -By default, the identity of a rule unit is the rule unit class name and is treated as a singleton class by the `RuleUnitExecutor`. This identification behavior is encoded in the `getUnitIdentity()` default method of the `RuleUnit` interface: - -.Default identity method in the `RuleUnit` interface -[source,java] ----- -default Identity getUnitIdentity() { - return new Identity( getClass() ); -} ----- - -In some cases, you may need to override this default identification behavior to avoid conflicting identities between rule units. - -For example, the following `RuleUnit` class contains a `DataSource` definition that accepts any kind of object: - -.Example `Unit0` rule unit class -[source,java] ----- -public class Unit0 implements RuleUnit { - private DataSource input; - - public DataSource getInput() { - return input; - } -} ----- - -This rule unit contains the following DRL rule that guards another rule unit based on two conditions (in OOPath notation): - -.Example `GuardAgeCheck` DRL rule in the rule unit -[source] ----- -package org.mypackage.myunit -unit Unit0 - -rule GuardAgeCheck - when - $i: /input#Integer - $s: /input#String - then - drools.guard( new AgeCheckUnit($i) ); - drools.guard( new AgeCheckUnit($s.length()) ); -end ----- - -The guarded `AgeCheckUnit` rule unit verifies the age of a set of `persons`. The `AgeCheckUnit` contains a `DataSource` definition of the `persons` to check, a `minAge` variable that it verifies against, and a `List` for gathering the results: - -.Example `AgeCheckUnit` rule unit -[source,java] ----- -public class AgeCheckUnit implements RuleUnit { - private final int minAge; - private DataSource persons; - private List results; - - public AgeCheckUnit( int minAge ) { - this.minAge = minAge; - } - - public DataSource getPersons() { - return persons; - } - - public int getMinAge() { - return minAge; - } - - public List getResults() { - return results; - } -} ----- - -The `AgeCheckUnit` rule unit contains the following DRL rule that performs the verification of the `persons` in the data source: - -.Example `CheckAge` DRL rule in the rule unit -[source] ----- -package org.mypackage.myunit -unit AgeCheckUnit - -rule CheckAge - when - $p : /persons{ age > minAge } - then - results.add($p.getName() + ">" + minAge); -end ----- - -This example creates a `RuleUnitExecutor` class, binds the class to the KIE base that contains these two rule units, and creates -the two `DataSource` definitions for the same rule units: - -.Example executor and data source definitions -[source,java] ----- -RuleUnitExecutor executor = RuleUnitExecutor.create().bind( kbase ); - -DataSource input = executor.newDataSource( "input" ); -DataSource persons = executor.newDataSource( "persons", - new Person( "John", 42 ), - new Person( "Sally", 4 ) ); - -List results = new ArrayList<>(); -executor.bindVariable( "results", results ); ----- - -You can now insert some objects into the input data source and execute the `Unit0` rule unit: - -.Example rule unit execution with inserted objects -[source,java] ----- -ds.insert("test"); -ds.insert(3); -ds.insert(4); -executor.run(Unit0.class); ----- - -.Example results list from the execution -[source,java] ----- -[Sally>3, John>3] ----- - -In this example, the rule unit named `AgeCheckUnit` is considered a singleton class and then executed only once, with the `minAge` -variable set to `3`. Both the String `"test"` and the Integer `4` inserted into the input data source can also trigger a second execution with the `minAge` variable set to `4`. However, the second execution does not occur because another rule unit with the same identity has already been evaluated. - -To resolve this rule unit identity conflict, override the `getUnitIdentity()` method in the `AgeCheckUnit` class to include also the `minAge` variable in the rule unit identity: - -.Modified `AgeCheckUnit` rule unit to override the `getUnitIdentity()` method -[source,java] ----- -public class AgeCheckUnit implements RuleUnit { - - ... - - @Override - public Identity getUnitIdentity() { - return new Identity(getClass(), minAge); - } -} ----- - -With this override in place, the previous example rule unit execution produces the following output: - -.Example results list from executing the modified rule unit -[source,java] ----- -[John>4, Sally>3, John>3] ----- - -The rule units with `minAge` set to `3` and `4` are now considered two different rule units and both are executed. diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-WHEN-elements-diagrams-ref.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-WHEN-elements-diagrams-ref.adoc deleted file mode 100644 index 241c32a4392..00000000000 --- a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-WHEN-elements-diagrams-ref.adoc +++ /dev/null @@ -1,188 +0,0 @@ -[id='drl-rules-WHEN-elements-diagrams-ref_{context}'] - -= Railroad diagrams for rule condition elements in DRL - - -image::language-reference/AccumulateAction.png[align="center"] - -image::language-reference/AccumulateClause.png[align="center"] - -image::language-reference/AccumulateFunction.png[align="center"] - -image::language-reference/AccumulateInit.png[align="center"] - -image::language-reference/AccumulateResult.png[align="center"] - -image::language-reference/AccumulateReverse.png[align="center"] - -image::language-reference/AccumulateSteps.png[align="center"] - -image::language-reference/Accumulations.png[align="center"] - -image::language-reference/AdditiveExpr.png[align="center"] - -image::language-reference/Annotation.png[align="center"] - -image::language-reference/Arguments.png[align="center"] - -image::language-reference/ArrayCreatorRest.png[align="center"] - -image::language-reference/ArrayInitializer.png[align="center"] - -image::language-reference/AssignmentOperator.png[align="center"] - -image::language-reference/BindingPattern.png[align="center"] - -image::language-reference/Block.png[align="center"] - -image::language-reference/BooleanLiteral.png[align="center"] - -image::language-reference/CompilationUnit.png[align="center"] - -image::language-reference/ConditionalAnd.png[align="center"] - -image::language-reference/ConditionalElementAccumulate.png[align="center"] - -image::language-reference/ConditionalElementEval.png[align="center"] - -image::language-reference/ConditionalElementExists.png[align="center"] - -image::language-reference/ConditionalElementForall.png[align="center"] - -image::language-reference/ConditionalElementNot.png[align="center"] - -image::language-reference/ConditionalElement.png[align="center"] - -image::language-reference/ConditionalExpr.png[align="center"] - -image::language-reference/ConditionalOrExpr.png[align="center"] - -image::language-reference/ConditionalOr.png[align="center"] - -image::language-reference/Constraints.png[align="center"] - -image::language-reference/CreatedName.png[align="center"] - -image::language-reference/Creator.png[align="center"] - -image::language-reference/Definition.png[align="center"] - -image::language-reference/Digit.png[align="center"] - -image::language-reference/ExplicitGenericInvocationSuffix.png[align="center"] - -image::language-reference/ExplicitGenericInvocation.png[align="center"] - -image::language-reference/Exponent.png[align="center"] - -image::language-reference/ExpressionList.png[align="center"] - -image::language-reference/Expression.png[align="center"] - -image::language-reference/Field.png[align="center"] - -image::language-reference/Fraction.png[align="center"] - -image::language-reference/FromAccumulateClause.png[align="center"] - -image::language-reference/FromClause.png[align="center"] - -image::language-reference/FromCollectClause.png[align="center"] - -image::language-reference/FunctionDefinition.png[align="center"] - -image::language-reference/GlobalDefinition.png[align="center"] - -image::language-reference/IdentifierSuffix.png[align="center"] - -image::language-reference/ImportDefinition.png[align="center"] - -image::language-reference/InExpr.png[align="center"] - -image::language-reference/InlineListExpr.png[align="center"] - -image::language-reference/InlineMapExpr.png[align="center"] - -image::language-reference/InnerCreator.png[align="center"] - -image::language-reference/InstanceOfExpr.png[align="center"] - -image::language-reference/IntLiteral.png[align="center"] - -image::language-reference/Literal.png[align="center"] - -image::language-reference/ModifyStatement.png[align="center"] - -image::language-reference/NonWildcardTypeArguments.png[align="center"] - -image::language-reference/OrRestriction.png[align="center"] - -image::language-reference/OverClause.png[align="center"] - -image::language-reference/Parameters.png[align="center"] - -image::language-reference/Pattern.png[align="center"] - -image::language-reference/Placeholders.png[align="center"] - -image::language-reference/Primary.png[align="center"] - -image::language-reference/PrimitiveType.png[align="center"] - -image::language-reference/QualifiedName.png[align="center"] - -image::language-reference/QueryDefinition.png[align="center"] - -image::language-reference/QueryOptions.png[align="center"] - -image::language-reference/RealLiteral.png[align="center"] - -image::language-reference/RealTypeSuffix.png[align="center"] - -image::language-reference/RelationalExpr.png[align="center"] - -image::language-reference/RelationalOperator.png[align="center"] - -image::language-reference/RhsStatement.png[align="center"] - -image::language-reference/RuleAttributes.png[align="center"] - -image::language-reference/RuleAttribute.png[align="center"] - -image::language-reference/RuleDefinition.png[align="center"] - -image::language-reference/RuleOptions.png[align="center"] - -image::language-reference/Selector.png[align="center"] - -image::language-reference/ShiftExpr.png[align="center"] - -image::language-reference/SingleRestriction.png[align="center"] - -image::language-reference/SourcePattern.png[align="center"] - -image::language-reference/StringId.png[align="center"] - -image::language-reference/SuperSuffix.png[align="center"] - -image::language-reference/ThenPart.png[align="center"] - -image::language-reference/TypeArguments.png[align="center"] - -image::language-reference/TypeArgument.png[align="center"] - -image::language-reference/TypeDefinition.png[align="center"] - -image::language-reference/TypeOptions.png[align="center"] - -image::language-reference/Type.png[align="center"] - -image::language-reference/UnaryExprNotPlusMinus.png[align="center"] - -image::language-reference/UnaryExpr.png[align="center"] - -image::language-reference/Value.png[align="center"] - -image::language-reference/VariableInitializer.png[align="center"] - -image::language-reference/WhenPart.png[align="center"] diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-oopath-con.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-oopath-con.adoc deleted file mode 100644 index 98acfd420e5..00000000000 --- a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/_drl-rules-oopath-con.adoc +++ /dev/null @@ -1,158 +0,0 @@ -[id='drl-rules-oopath-con_{context}'] - -= OOPath syntax with graphs of objects in DRL rule conditions - -OOPath is an object-oriented syntax extension of XPath that is designed for browsing graphs of objects in DRL rule condition constraints. OOPath uses the compact notation from XPath for navigating through related elements while handling collections and filtering constraints, and is specifically useful for graphs of objects. - -When the field of a fact is a collection, you can use the `from` condition element (keyword) to bind and reason over all the items in that collection one by one. If you need to browse a graph of objects in the rule condition constraints, the extensive use of the `from` condition element results in a verbose and repetitive syntax, as shown in the following example: - -.Example rule that browses a graph of objects with `from` -[source] ----- -rule "Find all grades for Big Data exam" - when - $student: Student( $plan: plan ) - $exam: Exam( course == "Big Data" ) from $plan.exams - $grade: Grade() from $exam.grades - then - // Actions -end ----- - -In this example, the domain model contains a `Student` object with a `Plan` of study. The `Plan` can have zero or more `Exam` instances and an `Exam` can have zero or more `Grade` instances. Only the root object of the graph, the `Student` in this case, needs to be in the working memory of the {RULE_ENGINE} for this rule setup to function. - -As a more efficient alternative to using extensive `from` statements, you can use the abbreviated OOPath syntax, as shown in the following example: - -.Example rule that browses a graph of objects with OOPath syntax -[source] ----- -rule "Find all grades for Big Data exam" - when - Student( $grade: /plan/exams[course == "Big Data"]/grades ) - then - // Actions -end ----- - -Formally, the core grammar of an OOPath expression is defined in extended Backus-Naur form (EBNF) notation in the following way: - -.EBNF notation for OOPath expressions -[source] ----- -OOPExpr = [ID ( ":" | ":=" )] ( "/" | "?/" ) OOPSegment { ( "/" | "?/" | "." ) OOPSegment } ; -OOPSegment = ID ["#" ID] ["[" ( Number | Constraints ) "]"] ----- - -In practice, an OOPath expression has the following features and capabilities: - -* Starts with a forward slash `/` or with a question mark and forward slash `?/` if it is a non-reactive OOPath expression (described later in this section). -* Can dereference a single property of an object with the period `.` operator. -* Can dereference multiple properties of an object with the forward slash `/` operator. If a collection is returned, the expression iterates over the values in the collection. -* Can filter out traversed objects that do not satisfy one or more constraints. The constraints are written as predicate expressions between square brackets, as shown in the following example: -+ -.Constraints as a predicate expression -[source] ----- -Student( $grade: /plan/exams[ course == "Big Data" ]/grades ) ----- -* Can downcast a traversed object to a subclass of the class declared in the generic collection. Subsequent constraints can also safely access the properties declared only in that subclass, as shown in the following example. Objects that are not instances of the class specified in this inline cast are automatically filtered out. -+ -.Constraints with downcast objects -[source] ----- -Student( $grade: /plan/exams#AdvancedExam[ course == "Big Data", level > 3 ]/grades ) ----- -* Can backreference an object of the graph that was traversed before the currently iterated graph. For example, the following OOPath expression matches only the grades that are above the average for the passed exam: -+ -.Constraints with backreferenced object -[source] ----- -Student( $grade: /plan/exams/grades[ result > ../averageResult ] ) ----- -* Can recursively be another OOPath expression, as shown in the following example: -+ -.Recursive constraint expression -[source] ----- -Student( $exam: /plan/exams[ /grades[ result > 20 ] ] ) ----- -* Can access objects by their index between square brackets `[]`, as shown in the following example. To adhere to Java convention, OOPath indexes are 0-based, while XPath indexes are 1-based. -+ -.Constraints with access to objects by index -[source] ----- -Student( $grade: /plan/exams[0]/grades ) ----- - -OOPath expressions can be reactive or non-reactive. The {RULE_ENGINE} does not react to updates involving a deeply nested object that is traversed during the evaluation of an OOPath expression. - -To make these objects reactive to changes, modify the objects to extend the class `org.drools.core.phreak.ReactiveObject`. After you modify an object to extend the `ReactiveObject` class, the domain object invokes the inherited method `notifyModification` to notify the {RULE_ENGINE} when one of the fields has been updated, as shown in the following example: - -.Example object method to notify the {RULE_ENGINE} that an exam has been moved to a different course -[source,java] ----- -public void setCourse(String course) { - this.course = course; - notifyModification(this); - } ----- - -With the following corresponding OOPath expression, when an exam is moved to a different course, the rule is re-executed and the list of grades matching the rule is recomputed: - -.Example OOPath expression from "Big Data" rule -[source] ----- -Student( $grade: /plan/exams[ course == "Big Data" ]/grades ) ----- - -You can also use the `?/` separator instead of the `/` separator to disable reactivity in only one sub-portion of an OOPath expression, as shown in the following example: - -.Example OOPath expression that is partially non-reactive -[source] ----- -Student( $grade: /plan/exams[ course == "Big Data" ]?/grades ) ----- - -With this example, the {RULE_ENGINE} reacts to a change made to an exam or if an exam is added to the plan, but not if a new grade is added to an existing exam. - -If an OOPath portion is non-reactive, all remaining portions of the OOPath expression also become non-reactive. For example, the following OOPath expression is completely non-reactive: - -.Example OOPath expression that is completely non-reactive -[source] ----- -Student( $grade: ?/plan/exams[ course == "Big Data" ]/grades ) ----- - -For this reason, you cannot use the `?/` separator more than once in the same OOPath expression. For example, the following expression causes a compilation error: - -.Example OOPath expression with duplicate non-reactivity markers -[source] ----- -Student( $grade: /plan?/exams[ course == "Big Data" ]?/grades ) ----- - -Another alternative for enabling OOPath expression reactivity is to use the dedicated implementations for `List` and `Set` interfaces in {PRODUCT}. These implementations are the `ReactiveList` and `ReactiveSet` classes. A `ReactiveCollection` class is also available. The implementations also provide reactive support for performing mutable operations through the `Iterator` and `ListIterator` classes. - -The following example class uses these classes to configure OOPath expression reactivity: - -.Example Java class to configure OOPath expression reactivity -[source,java] ----- -public class School extends AbstractReactiveObject { - private String name; - private final List children = new ReactiveList(); // <1> - - public void setName(String name) { - this.name = name; - notifyModification(); // <2> - } - - public void addChild(Child child) { - children.add(child); // <3> - // No need to call `notifyModification()` here - } - } ----- -<1> Uses the `ReactiveList` instance for reactive support over the standard Java `List` instance. -<2> Uses the required `notifyModification()` method for when a field is changed in reactive support. -<3> The `children` field is a `ReactiveList` instance, so the `notifyModification()` method call is not required. The notification is handled automatically, like all other mutating operations performed over the `children` field. diff --git a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/index.adoc b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/index.adoc index 3f34f629a6e..aa7ff6ffb11 100644 --- a/drools-docs/src/modules/ROOT/pages/language-reference-traditional/index.adoc +++ b/drools-docs/src/modules/ROOT/pages/language-reference-traditional/index.adoc @@ -41,10 +41,6 @@ include::_drl-operator-precedence-ref.adoc[leveloffset=+3] include::_drl-rules-WHEN-elements-ref.adoc[leveloffset=+3] -include::_drl-rules-oopath-con.adoc[leveloffset=+3] - -include::_drl-rules-WHEN-elements-diagrams-ref.adoc[leveloffset=+3] - include::_drl-rules-THEN-con.adoc[leveloffset=+2] include::_drl-rules-THEN-methods-ref.adoc[leveloffset=+3] @@ -57,6 +53,4 @@ include::_drl-rules-comments-con.adoc[leveloffset=+2] include::_drl-rules-errors-ref.adoc[leveloffset=+2] -include::_drl-rule-units-con.adoc[leveloffset=+2] - include::_DSL-section.adoc[leveloffset=+1]