I am currently learning Drools and reading the book Mastering JBoss Drools 6
In the beginning of chapter 4 an example is given to show the use of the delete keyword. This is that example:
rule "Init current date"
when
then
insert(new Date());
end
rule "Expire coupons"
when
$now: Date()
$cp: Coupon(validUntil before $now)
then
delete($cp);
end
rule "Execute coupon"
when
$o: Order()
$cp: Coupon(order == $o)
then
System.out.println(" We have a coupon for this order!");
end
Now my question is: Why is the "Execute coupon" rule fired later than the "Expire coupon" rule. As I've learned the order of the rules are non deterministic, so I was thinking that the "Execute coupon" rule can be fired before the other two rules
You are right. From my experience, I'd even bet some money on "Execute coupon" firing first, because later rules are typically fired first.
Clearly the example would have to be corrected, either by
rule "Execute coupon"
when
$now: Date()
$o: Order()
$cp: Coupon(order == $o, validUntil after $now )
then
System.out.println(" We have a coupon for this order!");
end
or by using salience (which one should try to avoid, if possible).
However (I don't have the book) I can also imagine a scenario where the rule set might work as given:
session.insert( new Date() );
session.insert( coupon );
session.fireAllRules();
session.insert( order );
session.fireAllRules();
Related
I am using decision tables and would like to trigger one rule per input item.
I am using decision have set the Sequential = true and defined all rules as part of the same ACTIVATION-GROUP.
When I trigger the drools rules engine using below it just evaluates for the first input item and others are ignored. The behavior I want is to evaluate at most 1 rule per input item (rule order defined by the Salience).
kieStatelessSession.execute(inputList)
I can get this working by sending one item at a time to the kieStatelessSession, but would prefer to execute all at once.
I am using Drools verison 6.5.0.FINAL and Java 7.
There is no out of the box support in Drools for what you are trying to achieve. If you want your rules to be evaluated once for each fact, you will need to code it yourself.
One approach could be to have another type of facts to mark when one of the inputs is processed:
declare Marker
fact : Object
end
//Bellow are the rules that should be coming from your decision table.
//Each rule will do whatever it needs to do, and then it will create a
//Marker fact for the fact that was processed.
//These rules now include a "not" Conditional Element to avoid a fact to be
//evaluated more than once.
rule "Rule 1"
salience 100
when
$fact: Object(...) //your conditions
not Marker(fact == $fact)
then
//... Your logic
insert(new Marker($fact));
end
...
rule "Rule 50"
salience 50
when
$fact: Object(...) //your conditions
not Marker(fact == $fact)
then
//... Your logic
insert(new Marker($fact));
end
Hope it helps,
I want to run a rule that calls Java method and passes the fact (or rather, its logical value) from another rule, although I don't know if the java methods are at all important for this problem. It's not easy to describe so I'll try to show it based on an example:
rule "Some rule determining fact"
when
... //some conditions
then
insert(new SomeCondition(callSomeJavaMethodReturningBoolean()))
end
rule "Some rule using SomeCondition"
when
SomeCondition($value: value)
... //some other conditions
then
insert(callJavaMethodUsingSomeCondition($value))
end
The problem here is that the first rule doesn't always fire, so SomeCondition is not always definded and second rule isn't evaluated.
My second try was to create two separate rules like this:
rule "Some rule determining fact"
when
... //some conditions
then
insert(new SomeCondition(callSomeJavaMethodReturningBoolean()))
end
rule "SomeConditionTrueRule"
when
SomeCondition(value == true)
... //some other conditions
then
insert(callJavaMethodUsingSomeCondition(true))
end
rule "SomeConditionFalseRule"
when
not SomeCondition() or SomeCondition(value == false)
... //some other conditions
then
insert(callJavaMethodUsingSomeCondition(false))
end
This doesn't work as intended either, because it first evaluates SomeConditionFalseRule before even evaluating my first rule. I'll appreciate any suggestions on how to solve this problem. Drools version used is 6.5.0 if that matters. Also, I want to avoid using salience if possible since I've read it is a bad practice (correct me if I'm wrong).
In this case, you need to separate the 2 groups of rules (the ones inserting SomeCondition objects and the ones executing the java code).
The easiest way would be to use a lower salience in the second group:
rule "Some rule determining fact"
when
... //some conditions
then
insert(new SomeCondition(callSomeJavaMethodReturningBoolean()))
end
rule "SomeConditionTrueRule"
salience -1
when
SomeCondition(value == true)
... //some other conditions
then
insert(callJavaMethodUsingSomeCondition(true))
end
rule "SomeConditionFalseRule"
salience -1
when
not SomeCondition() or SomeCondition(value == false)
... //some other conditions
then
insert(callJavaMethodUsingSomeCondition(false))
end
A more robust approach would be to use 2 agenda-groups and to activate them one after the other.
But the idea is that the rules executing the java code give time to the rules determining what needs to be executed to come up with the final decision.
In the case I've presented above, as soon as you create your session, you will have an activation (A1) for SomeConditionFalseRule but the activation will not be executed until you call fireAllRules(). If you then insert the necessary facts to make SomeConditionTrueRule true, you will have now an activation (A2) for it.
At this point, the agenda will look like this: | A1 | A2 |.
When calling fireAllRules(), Drools will pick the activation in the agenda with the higher salience (by default, rules have a salience of 0). In this case, A2 will be picked and executed.
The execution of A2 will insert a new Fact that will make A1 invalid and it will create a new activation (A3) for SomeConditionTrueRule. Drools will then proceed to remove A1 from the agenda, so the rule SomeConditionFalseRule will not be executed.
The agenda after A2 is executed will look like this: | A3 |
Hope it helps,
I tried using facts to set the values for date-effective and date-expires in a rule similar to how salience can be set dynamically, but keep getting parser errors. Are these attributes fixed at rule parse time or can they be set dynamically?
I couldn't find any further hints, so I just wanted to check if anyone knows if this is possible?
This works for me for dynamic salience:
rule "my rule XYZ"
salience $priority
when
// condition facts
$rrd : RuleRuntimeData(ruleCode == "xyz", $priority : priority)
...
Is something like this possible? And if so what is the syntax?
rule "my rule XYZ"
date-expires $dateExpires
date-effective $dateEffective
salience $priority
when
$rrd : RuleRuntimeData(ruleCode == "xyz", $priority : priority, $dateExpires : endDate, $dateEffective : startDate)
...
(I might have to use date facts or an agendafilter instead of the date-effective and date-expires attributes if it is a value that has to be fixed at rule parse time)
It could be that setting these values dynamically would have some use cases. But the idea of defining a time where rules should fire from the data they are about to evaluate boggles my mind. Rule evaluation would have to be done, at least partly, to get at a fact T carrying a date/time for date-effective. If the time hasn't arrived yet it should probably fail. But then imagine another rule where another fact X must arrive before evaluation arrives at the fact T carrying a date/time for date-effective. If X arrives past the time given in T, then the evaluation of T will set the date/time to a time in the past and evaluation proceeds. In sum: technical details of LHS evaluation will influence whether a rule succeeds or fails.
Note that dynamic salience is completely different: it merely sets firing priority as an afterthought after successful evaluation.
An agenda filter sounds a very reasonable way to define a window of time for rules to be active.
I'm not so sure about "date facts", but who knows - I haven't seen your idea.
This is my code into an .rdl
rule "Fulfilment 24345"
when
$evPtr: LeftArmStretched($tsmp:time)
eval((3>$tsmp)==true)
then
System.err.println("$tsmp= "+$tsmp);
end
rule "set timestamp"
when
$las:LeftArmStretched();
then
System.out.println("//change timestamp!!");
$las.setTime(6);
end
If I run my example, 1st and 2nd rules fire and print:
//change timestamp!!
$tsmp= 6
but if $tsmp=3 the rule1 does not fire!!!! (3>6 false!)
If I manually write eval((3>6)==true) into rule1, the rule1 doesn't correctly fire!
A change to a fact object must be announced to the Rule Engine, i.e., use
modify( $las ){
setTime( 6 )
}
Also, do not rely on rule "set timestamp" being fired first. Use some additional constraint, or salience.
I'm using JBoss Drools to write some business rules. I got a problem on the "not exists" rule.Here is my code.
rule "ATL 27R-A12 Subfleet A319-100 Departure configuration list has flap 1"
salience 20
no-loop true
when
AircraftConfig(aircraftType=="A319-100")
RunwayInfo(airport3lCode== "ATL", runwayId == "27R-A12" )
not (exists (DepartureConfiguration( flap == 1 )))
then
throw new RuleNotMatchException("The configurations do not match the rule of this runway.");
end
My facts contains:An AircraftConfig, an RunwayInfo and several DepartureConfigurations. I want to fire the rule when there are no DepartureConfiguration which flap=1. I mean, if there are three DepartureConfigurations, one of them has the flap=1, the others are flap=2 or flap=3, then this rule will not fire.
How could I make this work?
The keyword for checking for the non-existence of a fact is not, not not exists. Change the last line of your condition to:
not DepartureConfiguration( flap == 1 )
Actually, I made some conflict in my rules. I used to think the rules should be ran from the top to the end of the drl file. I solved my problem by adding a rule flow. Also thanks to you guys who give me suggestions.