I am new to Drools and either I misunderstand what Drools can do or I am missing something.
I have a simple Fact class
public class MeterReadTO {
private String status = "";
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public boolean isInvalid() {
return status.equals("invalid");
};
}
With the rules
rule "Rule Invalid"
dialect "mvel"
when
MeterReadTO( isInvalid());
then
System.out.println("Rule Invalid Fired");
end
rule "Rule Not Invalid"
dialect "mvel"
when
not MeterReadTO( isInvalid());
then
System.out.println("Rule Not Invalid Fired");
end
rule "Set Status"
dialect "mvel"
when
$mr: MeterReadTO()
then
System.out.println("Rule Set Status Fires");
modify($mr) {
setStatus("invalid")
}
end
and I run the rules with
@RunWith(SpringRunner.class)
@SpringBootTest
public class Dependancy {
static KieSession ksession;
Logger logger = LoggerFactory.getLogger(Dependancy.class);
MeterReadTO mr;
@BeforeClass
public static void start() {
KieContainer kieClasspathContainer = KieServices.Factory.get().getKieClasspathContainer();
ksession = kieClasspathContainer.newKieSession("DependancyTest");
}
@Test
public void contextLoadsAndRetrievesData() {
MeterReadTO mr = new MeterReadTO();
mr.setId(50);
ksession.insert(mr);
// when
ksession.fireAllRules();
ksession.dispose();
// Then
System.out.println("After Drools, status is " + mr.getStatus());
}
}
and I get the outcome
Rule Not Invalid Fired
Rule Set Status Fires
After Drools, status is invalid
By using a stateful session I was expecting to see the "rule Invalid" fire after "rule Set Status" fires. If I move the Set Status rule from the bottom of the list of rules to the top, then I get
Rule Set Status Fires
Rule Not Invalid Fired
After Drools, status is invalid
So clearly Drools is not aware of the status change in rule Set Status. What am I missing?
Regards
FOLLOW UP.
Now I'm really confused. If I remove the call to the method isInvalid() in my rules file and reference the getStatus() method instead so the rules now look like
rule "Rule Invalid"
dialect "mvel"
when
MeterReadTO( getStatus() == "ïnvalid");
then
System.out.println("Rule Invalid Fired");
end
rule "Rule Not Invalid"
dialect "mvel"
when
MeterReadTO( getStatus() == "");
then
System.out.println("Rule Not Invalid Fired");
end
rule "Set Status"
dialect "mvel"
when
$mr: MeterReadTO()
then
System.out.println("Rule Set Status Fires");
modify($mr) {
setStatus("ïnvalid")
}
end
Then I get the output that I expected
Rule Not Invalid Fired
Rule Set Status Fires
Rule Invalid Fired
After Drools, status is ïnvalid
Can anyone explain what is happening?
Basically, what's happening is that there is no way for Drools to understand that when you change the status
of your fact, the result of the method isInvalid()
is affected.
When you modify a fact, Drools will try to minimize the reevaluation of the rules as much as possible. In your case, Drools has no way to make a connection between the setStatus()
and isInvalid()
methods.
In the second case, you are using the getStatus()
method in your rules and Drools is then able to do the connections (by simple POJO naming conventions).
One way to make sure Drools understands the connection between setStatus()
and isInvalid()
could be to use the @watch
annotation in your rule:
rule "Rule Invalid"
dialect "mvel"
when
MeterReadTO( isInvalid()) @watch(status)
then
System.out.println("Rule Invalid Fired");
end
rule "Rule Not Invalid"
dialect "mvel"
when
not MeterReadTO( isInvalid()) @watch(status)
then
System.out.println("Rule Not Invalid Fired");
end
The problem with this approach is that your rules get really tied to your model. If the way you check whether a fact is invalid ahs to be modified to involve more fields, you has to go rule by rule modifying the @watch
annotation.
Another solution would be to mark the relationship in the model itself using the @Modifies
annotation:
public class MeterReadTO {
private String status = "";
public String getStatus() {
return status;
}
@Modifies( { "invalid" } )
public void setStatus(String status) {
this.status = status;
}
public boolean isInvalid() {
return status.equals("invalid");
};
}
You can find more information about this topic in Drools' official documentation.
Hope it helps,