JBoss.orgCommunity Documentation

Chapter 10. Experimental Features

10.1. Declarative Agenda
10.2. Browsing graphs of objects with OOPath
10.2.1. Reactive and Non-Reactive OOPath

The declarative agenda allows to use rules to control which other rules can fire and when. While this will add a lot more overhead than the simple use of salience, the advantage is it is declarative and thus more readable and maintainable and should allow more use cases to be achieved in a simpler fashion.

This feature is off by default and must be explicitly enabled, that is because it is considered highly experimental for the moment and will be subject to change, but can be activated on a given KieBase by adding the declarativeAgenda='enabled' attribute in the corresponding kbase tag of the kmodule.xml file as in the following example.


The basic idea is:

  • All rule's Matches are inserted into WorkingMemory as facts. So you can now do pattern matching against a Match. The rule's metadata and declarations are available as fields on the Match object.

  • You can use the kcontext.blockMatch( Match match ) for the current rule to block the selected match. Only when that rule becomes false will the match be eligible for firing. If it is already eligible for firing and is later blocked, it will be removed from the agenda until it is unblocked.

  • A match may have multiple blockers and a count is kept. All blockers must became false for the counter to reach zero to enable the Match to be eligible for firing.

  • kcontext.unblockAllMatches( Match match ) is an over-ride rule that will remove all blockers regardless

  • An activation may also be cancelled, so it never fires with cancelMatch

  • An unblocked Match is added to the Agenda and obeys normal salience, agenda groups, ruleflow groups etc.

  • The @Direct annotations allows a rule to fire as soon as it's matched, this is to be used for rules that block/unblock matches, it is not desirable for these rules to have side effects that impact else where.


Here is a basic example that will block all matches from rules that have metadata @department('sales'). They will stay blocked until the blockerAllSalesRules rule becomes false, i.e. "go2" is retracted.


Warning

Further than annotate the blocking rule with @Direct, it is also necessary to annotate all the rules that could be potentially blocked by it with @Eager. This is because, since the Match has to be evaluated by the pattern matching of the blocking rule, the potentially blocked ones cannot be evaluated lazily, otherwise won't be any Match to be evaluated.

This example shows how you can use active property to count the number of active or inactive (already fired) matches.


When the field of a fact is a collection it is possible to bind and reason over all the items in that collection on by one using the from keyword. Nevertheless, when it is required to browse a graph of object the extensive use of the from conditional element may result in a verbose and cubersome syntax like in the following example:


In this example it has been assumed to use a domain model consisting of a Student who has a Plan of study: a Plan can have zero or more Exams and an Exam zero or more Grades. Note that only the root object of the graph (the Student in this case) needs to be in the working memory in order to make this works.

By borrowing ideas from XPath, this syntax can be made more succinct, as XPath has a compact notation for navigating through related elements while handling collections and filtering constraints. This XPath-inspired notation has been called OOPath since it is explictly intended to browse graph of objects. Using this notation the former example can be rewritten as it follows:


Formally, the core grammar of an OOPath expression can be defined in EBNF notation in this way.

OOPExpr = ( "/" | "?/" ) OOPSegment { ( "/" | "?/" | "." ) OOPSegment } ;
    OOPSegment = [ID ( ":" | ":=" )] ID ["[" Number "]"] ["{" Constraints "}"];

In practice an OOPath expression has the following features.

  • It has to start with / or with a ?/ in case of a completely non-reactive OOPath (see below).

  • It can dereference a single property of an object with the . operator

  • It can dereference a multiple property of an object using the / operator. If a collection is returned, it will iterate over the values in the collection

  • While traversing referenced objects it can filter away those not satisfying one or more constraints, written as predicate expressions between curly brackets like in:

    Student( $grade: /plan/exams{ course == "Big Data" }/grades )
  • A constraint can also have a beckreference to an object of the graph traversed before the currently iterated one. For example the following OOPath:

    Student( $grade: /plan/exams/grades{ result > ../averageResult } )

    will match only the grades having a result above the average for the passed exam.

  • A constraint can also recursively be another OOPath as it follows:

    Student( $exam: /plan/exams{ /grades{ result > 20 } } )
  • Items can also be accessed by their index by putting it between square brackets like in:

    Student( $grade: /plan/exams[0]/grades )

    To adhere to Java convention OOPath indexes are 0-based, compared to XPath 1-based

At the moment Drools is not able to react to updates involving a deeply nested object traversed during the evaluation of an OOPath expression. To make these objects reactive to changes it is then necessary to make them extend the class org.drools.core.phreak.ReactiveObject. It is planned to overcome this limitation by implementing a mechanism that automatically instruments the classes belonging to a specific domain model.

Having extendend that class, the domain objects can notify the engine when one of its field has been updated by invoking the inherited method notifyModification as in the following example:


In this way when using an OOPath like the following:

Student( $grade: /plan/exams{ course == "Big Data" }/grades )

if an exam is moved to a different course, the rule is re-triggered and the list of grades matching the rule recomputed.

It is also possible to have reactivity only in one subpart of the OOPath as in:

Student( $grade: /plan/exams{ course == "Big Data" }?/grades )

Here, using the ?/ separator instead of the / one, the engine will react 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. Of course if a OOPath chunk is not reactive, all remaining part of the OOPath from there till the end of the expression will be non-reactive as well. For instance the following OOPath

Student( $grade: ?/plan/exams{ course == "Big Data" }/grades )

will be completely non-reactive. For this reason it is not allowed to use the ?/ separator more than once in the same OOPath so an expression like:

Student( $grade: /plan?/exams{ course == "Big Data" }?/grades )

will cause a compile time error.