As we already know, the right-hand side of a rule in Drools may contain a combination of the following elements:
mvel
, MVEL expressions are enabled on the right-hand side of the rule in Drools.drools
and kcontext
and methods such as insert
, update
, and delete
are also allowed. The special modify(){}
structure could also be used on the right-hand side of the rule in Drools.In the previous section, we introduced the notion of how, when compiled, the left-hand side of all the rules in a KIE Base is converted into a network of nodes. For the right-hand side of the rules, the situation is different. When a KIE Base is compiled, the right-hand side of each of the rules that it contains is converted into a Java class. This Java class will basically define a single method containing all the code that was on the right-hand side of the source rule. Inside the PHREAK network, a reference to this class is then added as a terminal node (this will be covered in greater detail in the next chapter).
Even if there is a way to use breakpoints to debug the right-hand side of the rule in a traditional way, this is only possible under some strict circumstances; this is why having a useful set of good practices and techniques to make the right-hand side of a rule easier to debug becomes mandatory.
There is a Drools Eclipse plugin that can be used, among other things, to debug the right-hand side of the rules in a DRL resource. This capability is only enabled for Drools projects. The scope of this plugin is beyond this book. More information about how to set up this plugin in Eclipse can be found in the following Drools' documentation: http://docs.jboss.org/drools/release/latestF
The good news about the right-hand side of a rule being converted into a Java class is that the errors that we may experience will look much more familiar to us than the errors found on the left-hand side of it.
Just like with any piece of Java code, two types of errors might be found: compilation errors and runtime errors.
Compilation errors are caused by invalid syntax or grammar on the right-hand side of a rule. Similar to the compilation errors on the left-hand side of a rule, compilation errors on the right-hand side will create an error containing information about what the error was and where in the corresponding resource it occurred. The same consideration for compilation errors on the left-hand side of a rule also applies here: Drools will not complain about compilation errors in a KIE Base by itself. The verification of a newly created KIE Container is mandatory.
As an example, let's go back to the rule that was used to send suspicious operations to an audit service introduced in Chapter 5, Understanding KIE Sessions:
rule "Send Suspicious Operation to Audit Service"
when
$so: SuspiciousOperation()
then
auditServiceXXX.notifySuspiciousOperation($so);
end
In the preceding rule, we have intentionally introduced a compilation error: the name of the global being used as a service was changed from auditService
to auditServiceXXX
.
When a KIE Container containing the erroneous rule is validated, the following error will be generated:
[ERROR] - chapter05/globals-5/globals-5.drl[26,0]: Rule Compilation error auditServiceXXX cannot be resolved
Similar to the compilation errors on the left-hand side of a rule, the generated error will indicate what the error was (auditServiceXXX cannot be resolved) and resource where the error occurred (chapter05/globals-5/globals-5.drl
). An estimated line (26
) and column (0
) for the error will also be generated.
Using all this information, the identification and resolution of compilation errors in the right-hand side of a rule is, most of the times, trivial.
Runtime errors on the right-hand side of a rule are caused by unhandled exceptions in the code. This type of errors on the right-hand side of the rule are as potentially harmful as runtime errors on the left-hand side: they can leave a session in an inconsistent and irrecoverable state. Special attention is then required for this type of errors in our knowledge bases. Just like we stated before for runtime errors on the left-hand side of our rules, the best mechanism to mitigate this kind of errors is to provide an extensive set of test scenarios covering all the different situations that our sessions could be exposed to.
Using the same rule from the previous section, now let's see what happens if the audit service used by this rule throws an exception during runtime. In this scenario, IllegalStateException
will be thrown if the audit service can't be contacted. The stack trace generated during runtime for this scenario will look similar to the following (the stack trace was shortened to make it easier to read):
Exception executing consequence for rule "Send Suspicious Operation to Audit Service" in chapter05.globals5: java.lang.IllegalStateException: Unable to contact Audit Service: No route to host. at o.d.c.r.r.i.DefaultConsequenceExceptionHandler.handleException(DefaultConsequenceExceptionHandler.java:39) at o.d.c.c.DefaultAgenda.fireActivation(DefaultAgenda.java:1083) ... 37 more Caused by: java.lang.IllegalStateException: Unable to contact Audit Service: No route to host. at org.drools.devguide.chapter05.GlobalsTest$4.notifySuspiciousOperation(GlobalsTest.java:286) at chapter05.globals5.Rule_Send_Suspicious_Operation_to_Audit_Service1050594099.defaultConsequence(Rule_Send_Suspicious_Operation_to_Audit_Service1050594099.java:7) ... 40 more
The preceding stack trace shows the IllegalStateException
being thrown by the service, and where in our code, the exception actually takes place. There are two important things to notice in the stack trace, other than the concrete exception: one is where the exception actually occurred. In this case, the concrete implementation of the service being used is defined as an anonymous class (GlobalsTest$4
) in GlobalTest.java
. The second thing to notice is where in our rule is the exception thrown. In this case, the class where the exception is thrown is Rule_Send_Suspicious_Operation_to_Audit_Service1050594099
. This is the class that Drools generated for the right-hand side of the rule when it was compiled. We will come back to these generated classes later in this chapter.
Some of the good practices regarding the right-hand side of the rules that were already covered in this chapter and some of the good practices regarding the left-hand side also apply here. The use of global variables to reference services, for example, makes our rules independent of the context. Different context (that is, production, testing, and so on) could use different implementations of these global variables to obtain different behaviors. Using event listeners, we can also be aware of when the right-hand side of a rule modifies the state of the session by inserting, updating, or deleting facts.
As debugging the right-hand side of the rules requires some extra steps (either the Drools Eclipse plug-in or the generation of the corresponding Java classes), creating simple right-hand sides, when possible, is always a good idea. For example, instead of having 10 lines of code in the action part of a rule, we could extract this into a regular Java class and invoke it from the rule. This simplification will allow us to easily debug our Java class and not worry about the rule itself.
Along with keeping the right-hand side of our rules simple, we must also avoid any unhandled exception to happen in it. We already talked about the problems that this kind of exceptions cause in Drools.
Another good practice that should sound obvious for any experienced developer is to use a logger framework. Remember that the right-hand side of our rules is just Java. We could, and should, use a logger in our rules to get a more detailed idea of what is going on in our knowledge bases.
As already mentioned earlier, the right-hand side of our rules is converted, on the fly, into Java classes by Drools. This conversion is, by default, invisible to the application using Drools: we never get to see these generated classes. When dealing with errors on the right-hand side of a rule it is, sometimes, useful to understand which exact Java code is being executed during runtime.
Thankfully, Drools allows us to dump the generated Java classes from our rules into a directory in the filesystem. These classes are not only useful to understand what is the concrete Java code that is being executed by Drools, but they can also be used to attach a debugger to them in order to debug the right-hand side of the rules as a regular class.
Drools provides two simple ways to enable the dump of the generated classes into a directory via a system property or declaratively in the kmodule.xml
file.
Using the drools.dump.dir
system variable, the directory where we want to dump the generated classes can be specified. For example, if we are using Maven to run our tests and want to dump any generated class, we can invoke the following command:
mvn test -Ddrools.dump.dir ="/tmp/classes"
We could achieve the same result by adding the following property to our <kmodule>
section in the kmodule.xml
files:
<kmodule>
<configuration>
<property key="drools.dump.dir" value="/tmp/classes"/>
</configuration>
...
</kmodule>
If we take a look at the kmodule.xml
file present in the code bundle associated with this chapter, we will notice that it is actually using the property that was mentioned earlier.
To get a better understanding of how these classes look and how the code on the right-hand side of our rules is executed inside them, it is highly recommended to execute the tests associated with this chapter. To get an even better understanding, try to use the drools.
dump.dir
property in other examples of this book.
18.221.35.58