The Default Consequence Exception Handler provides a minimum of information: the rule, where it happened, and the cause, with the Java stack dumps, which usually doen't tell you much. Luckily, Drools lets you set up your own handler, making better use of the available information.
Overriding the default consequence exception handler is done by setting the Consequence Exception Handler Option of the Knowledge Base Configuration with which the Knowledge Base is created.
import org.drools.KnowledgeBaseConfiguration; import org.drools.KnowledgeBaseFactory; import org.drools.conf.ConsequenceExceptionHandlerOption; import org.drools.runtime.rule.ConsequenceExceptionHandler; KnowledgeBaseConfiguration kBaseConfig = KnowledgeBaseFactory.newKnowledgeBaseConfiguration(); @SuppressWarnings("unchecked") Class ehClass = Class extends ConsequenceExceptionHandler>)MyConsequenceExceptionHandler.class; ConsequenceExceptionHandlerOption cehOption = ConsequenceExceptionHandlerOption.get( ehClass ); kBaseConfig.setOption( cehOption ); KnowledgeBase kBase = KnowledgeBaseFactory.newKnowledgeBase( kBaseConfig );
Looking at the Default Consequence Exception Handler shows that the Activation object of the rule firing that went down the drain is available. Therefore, we'll throw a Runtime Exception of our own making, which will take this Activation as an argument. The remainder is copied from the default.
package rss.drools.monitor; import java.io.Externalizable; import org.drools.runtime.rule.ConsequenceExceptionHandler; public class MyConsequenceExceptionHandler implements ConsequenceExceptionHandler, Externalizable { public void readExternal( ObjectInput in ) throws IOException, ClassNotFoundException {} public void writeExternal( ObjectOutput out ) throws IOException {} public void handleException( Activation activation, WorkingMemory workingMemory, Exception exception ){ throw new MyConsequenceException( exception, workingMemory, activation ); } }So far, this doesn't amount to much, as we have left the hard work to our custom Consequence Exception. The next section shows what we can do with the Activation passed to that Runtime Exception.
The constructor takes the cause that's passed to the Exception Handler shown in the previous section, the Working Memory and the Activation of the rule, with the latter two being stored in the extension. These two objects are used in several methods.
public class MyConsequenceException extends RuntimeException { private static final long serialVersionUID = 510l; private WorkingMemory workingMemory; private Activation activation; public ConsequenceException( final Throwable rootCause, final WorkingMemory workingMemory, final Activation activation ){ super( rootCause ); this.workingMemory = workingMemory; this.activation = activation; } @Override public String getMessage() { StringBuilder sb = new StringBuilder( "Exception executing consequence for " ); Rule rule = null; if( activation != null && ( rule = activation.getRule() ) != null ){ String packageName = rule.getPackageName(); String ruleName = rule.getName(); sb.append( "rule \"" ).append( ruleName ).append( "\" in " ).append( packageName ); } else { sb.append( "rule, name unknown" ); } sb.append( ": " ).append( super.getMessage() ); return sb.toString(); } public void printFactDump(){ printFactDump( System.err ); } public void printFactDump( PrintStream pStream ){ Collection extends FactHandle> handles = activation.getFactHandles(); for( FactHandle handle: handles ){ Object object = workingMemory.getObject( handle ); if( object != null ){ pStream.println( " Fact " + object.getClass().getSimpleName() + ": " + object.toString() ); } } } @Override public String toString() { return getMessage(); } }