16
votes

I have a problem where the when clause of a drools rule throws a MethodNotFoundException. I'm looking for a way to figure out which rule it is during runtime to be able to remove it from the list of rules to use.

Rule Example

Rule "FooBar"
when
 $V1 : Foo (    )  AND
 $V2 : FooBar(    ) from $V1.getGoodMethod()  AND
 $V3 : FooBarBar( status == "FooBar" ) from $V2.getBadMethod()  
reply : FooFooBar()
then
reply.getList().add("FooBar");
end

So, the getBadMethod on FooBar doesn't exist. I would like a way of telling what rule it is, and removing it from the list of rules to use.

Tried and Failed Solutions:

I've tried extending the DefaultAgendaEventListener and overriding the beforeActivationFired method to add the rule being fired to a list. I was hoping the last one in the list would be the one that threw the error, but sadly it didn't work out that way.

I've now tried adding "always true" rules before all my rules. They log the name of the rule that comes after it. The problem being when there is an exception in the "WHEN" clause nothing gets logged. It's as if no rules get fired when an Exception such as the one above occurs.

The problem all lies with the dynamic drools generation code. I would like to take a two pronged approach of fixing the generation code, and catching exceptions like the one listed in this post.

Side note: I do check for errors in the builder. I receive no errors from the below code.

KnowledgeBuilderErrors errors = builder.getErrors();

if (!errors.isEmpty()) {
    for (KnowledgeBuilderError error : errors) {
        ...
    }
}
1
Hi, afaik, mvel doesn't support exception catching so you could either change your rule to use java dialect or use try and catch statement when you trying to fire your rule.kucing_terbang
@kucing_terbang I can use the java dialect in the LHS of the drools rule? Also, I do catch the exception when I fire the rule, the problem being it sometimes it's difficult to diagnose the problem from the stacktrace.Mason T.
well, i don't think you can do that. The closest thing that i can think of is that you create your own function that handle the error which you'll call in the LHS.kucing_terbang
I can't really do that, the drools code gets dynamically generated. Which means I would have to not only dynamically generate drools rules, but also dynamically generate java methods.Mason T.

1 Answers

6
votes

As per my understanding, before doing fireAllRules() method below steps should be followed:

  1. Add rules to the Package / Knowledge Builder
  2. Validate that there are no errors in the Rules
  3. Inject Rules in the working memory

Of Course, it is possible to fireRules without Step 2, but this practice can result in problems as mentioned in this question. If I were you, I would follow the below logic to fix your this issue:

Step 1:

private RuleBase initialiseDrools() throws IOException, DroolsParserException {
    PackageBuilder packageBuilder = readRules();
    return addRulesToWorkingMemory(packageBuilder);
}

Step 2:

private PackageBuilder readRules() throws DroolsParserException, IOException {
    PackageBuilder packageBuilder = new PackageBuilder();
    PackageBuilder intermPackageBuilder = null;

    listOfReader = dynamicRuleReader(); // Here goes your application code

    for(Reader reader : listOfReader){ 
        try{
            intermPackageBuilder = new PackageBuilder();
            intermPackageBuilder.addPackage(reader);
            assertNoRuleErrors(intermPackageBuilder); // This is the core step. 
            // Above line throws an exception, every time a rules fails. You can persist this exception for production debugging
            packageBuilder.addPackage(reader);
        }catch(DroolsParserException | IOException e){
            logger.error("Rules contain error, so skip adding them to the Package Builder");
        }
    }

    return packageBuilder;
}

Step 3:

public void shouldFireAllRules() throws IOException, DroolsParserException {
    RuleBase ruleBase = initialiseDrools();
    WorkingMemory workingMemory = ruleBase.newStatefulSession();
    int expectedNumberOfRulesFired = 2; // Idealy this number should be equal to the number of total rules injected in the Working memory

    int actualNumberOfRulesFired = workingMemory.fireAllRules();

    assertThat(actualNumberOfRulesFired, is(expectedNumberOfRulesFired));
}

Using above method, you will not executing a rule that has errors, and the situation described above will not arise. However, I still believe you should focus more on the piece of code that generate erroneous Rules, and the method described above only to track and persist such occurrences.