Search code examples
droolsexecutionrulesevaluation

How to control rule evaluation (or rule execution) stages in Drools?


I know that "salience" in Drools provides control under rule execution sequence. But above is an example of the problem when "saliences" cannot help anymore that I've faced with.

Here I have three rules being executed one after another:

rule "Rule 1"
salience 30
when

then
    Resource resource1 = new Resource();
    resource1.setName("Resource 1");
    resource1.setAmount("5");
    insert(resource1);
    System.out.println("First");
end

rule "Rule 2"
salience 20
//no-loop (interesting, it doesn't lead to a loop)
when
    $resource1: Resource(name == "Resource 1")
then
    modify($resource1) {setAmount("20")};
    System.out.println("Second");
end

rule "Rule 3"
salience 10
when
    $resource1: Resource(name == "Resource 1", 
    Double.parseDouble(amount) > 10)
then
    System.out.println("Rule is fired");
end

I expected the third rule is fired and there's a "Rule is fired" line in the console, but it is not executed.
As I understand the issue is with rules evaluation stage when all three rules are evaluated at once before execution and only then are executed according to their "salience" turn.
And on the moment of evaluation $resource1.amount is 5, that is why third rule wasn't fired. If you put a number more than 10 in the first rule the 3d rule will fire. And if you don't set amount at all - it leads to exception.
How can I solve this issue so that the 3d rule fires?


Solution

  • My guess is that Drools doesn't understand that the expression Double.parseDouble(amount) > 10 must be re-evaluated when you change the amount of your fact. The problem is related with the way you are writing your expression.

    You can take a look at my answer in this other question. Take a look at the "Another solution" part.

    What I would suggest you to do is to modify your model and add a getAmountAsDouble() method to you class so the conversion happens inside it. You will also need to annotate the setAmount() method to let Drools know that it modifies the value returned by getAmountAsDouble():

    public class Resource {
    
      private String amount;
    
      @Modifies( { "amountAsDouble" } )
      private void setAmount(String amount){
        this.amount = amount;
      }
    
      private String getAmount(){
        return this.amount;
      }
    
      private String getAmountAsDouble(){
        return Double.parseDouble(this.amount);
      }
    
    }
    

    Now your rule can be rewritten as:

    rule "Rule 3"
    salience 10
    when
        $resource1: Resource(name == "Resource 1", 
        amountAsDouble > 10)
    then
        System.out.println("Rule is fired");
    end
    

    Hope it helps,