I am looking for a way to categorize rules in my drl files and determine which category/ categories of rules fired.
I see that the Drools Guvnor documentation has something about categorization http://docs.jboss.org/drools/release/5.2.0.Final/drools-guvnor-docs/html/ch03.html#d0e228. However, it is unclear to me if this is metadata maintained by Guvnor or if it is part of the drl file/ rules execution. I have not been able to find a sample DRL file with category mentioned in it. And, a way to determine the category/ categories of rules that got fired.
I am aware of activation-group but that doesn't quite fit my need since once a rule in the activation-group fires the other rules in the group are not evaluated.
Thanks
The best way to classify rules is to do it via metadata. A metadata entry is added to the rule:
rule metademo
#meta2( 123 )
#meta3( foo )
#meta4( "foo" )
when...then...end
You access the metadata via a Rule, obtained from a package or some event:
for( Rule rule: kPackage.getRules() ){
Map<String,Object> key2meta = rule.getMetaData();
for( Map.Entry<String,Object> entry: key2meta.entrySet() ){
System.out.print( " #" + entry.getKey() + "( " );
Object value = entry.getValue();
System.out.print( "[" + value.getClass().getSimpleName() + "] "
+ value.toString() );
System.out.println( " ) " );
}
}
Output:
#meta2( [Integer] 123 )
#meta4( [String] foo )
#meta3( [String] foo )
There was a plan to permit Maps as value so you might use
#meta7( foo = "foo", bar = "bar" )
This didn't work in 5.5, and I never tried it again.
Related
I am trying to pass a map in DRT template of the of the drools like this
TEMPLATE
template "another map"
rule "test map_2"
no-loop true
dialect "java"
when
namedmap:Map(map:"${namedTags}")
review:FactNew(
(compareNamedTags(namedmap,namedTags))
)
then
System.out.println("Value is present in the list: "+ namedmap.get("objective"));
modify(review){setComment("accepted")}
end
end template
DRL GENERATED
rule "test map_2"
no-loop true
dialect "java"
when
namedmap:Map(map:"{key1=value1, key2=value2}")
review:FactNew(
(compareNamedTags(namedmap,namedTags))
)
then
System.out.println("Value is present in the list: "+ namedmap.get("key2"));
modify(review){setComment("accepted")}
end
FUNCTION TO CHECK THE MAP
function Boolean compareNamedTags(Map namedTagsMap, Map namedTags){
System.out.println("Comparing the named Tags map");
System.out.println("namedTag -->"+namedTagsMap );
System.out.println("namedTagsMap -->"+namedTags );
System.out.println("equality -->"+namedTagsMap.keySet().equals(namedTags.keySet()) );
boolean flag = false;
flag = namedTagsMap.equals(namedTags);
return true;
}
While firing the rule there is no error but no output is printed. Also there is no call to the function why? Is this the right to pass the map in DRT and use it in the when condition of the rule.
Please provide some guidance on how one can pass a map in the DRT template and use it.
I want to get expression value from drools.The drl like this.
global java.util.Map $globalVar;
global java.lang.Object $result;
import java.util.Map;
function myFunc(Map map){
return "hello";
}
rule "onlyone"
when
$factMap: Map($tmpResult:(myFunc($globalVar)), eval(true))
then
$result = $tmpResult;
end
When the rule get exception,like this
Caused by: org.mvel2.PropertyAccessException: [unable to resolve method: java.util.Map.$globalVar() [argslength=0]]
The global variable $globarVar has no relation with the fact.
It is likely drools can't recognize the global variable.Is there any grammar error in my drl?
Your syntax is so wrong I can't even figure out what you're trying to do.
Functions are very simple, they're just like methods in Java. They even look like methods in Java -- yours is missing a return type.
function String myFunc(Map map) {
return "hello";
}
function String greeting(String name) {
return "Hello, " + name + ". How are you?"
}
You can invoke them in your rules as you would any other function, either on the left hand side ("when") or on the right hand side ("then").
rule "Apply Greeting and Send Memo"
when
$memo: Memo( $recipient: recipient, sent == false )
$greeting: String() from greeting($recipient)
then
MemoSender.send($greeting, $memo);
modify( $memo ) {
setSent(true)
};
end
rule "Apply Greeting and Send Memo - v2"
when
$memo: Memo( $recipient: recipient, sent == false )
then
String $greeting = greeting($recipient);
MemoSender.send( $greeting, $memo );
modify( $memo ) {
setSent(true)
}
end
The bits with the globals in your question are red herrings. Yes, they're not working and throwing errors, but the syntax is so wrong that I'm not sure how it was supposed to work in the first place.
One way you might interact with a global in a function would possibly be something like ...
global Float taxRate; // passed in as a global because it's an external constant value
global Float freeShippingThreshold;
function float calculateTotal(float subtotal) {
// some math here that references taxRate
return calculatedTotal
}
rule "Apply free shipping"
when
Cart( $subtotal: subtotal )
Float( this >= freeShippingThreshold) from calculateTotal($subtotal)
then
// you get free shipping
end
I'm trying to run this GitHub project using Drools 7.57.0.Final instead of 7.39.0.Final which was used in original project. And I found some issues. The issue that most triggers me is the one in the Section 6, Step 5. The problem lies in the Drools file VisaApplicationValidationWithAgendaAndSalience.drl. Here is the content with the "debug" statement that I have added:
package io.github.aasaru.drools.section06.step5
import io.github.aasaru.drools.domain.Passport;
import io.github.aasaru.drools.domain.Validation;
import io.github.aasaru.drools.domain.VisaApplication;
import java.time.LocalDate;
rule "Invalidate visa application with invalid passport"
dialect "mvel"
agenda-group "validate-application"
salience 20
when
$passport: Passport( validation == Validation.FAILED )
$visaApplication: VisaApplication( passportNumber == $passport.passportNumber,
validation != Validation.FAILED )
then
System.out.println( "Set " + $visaApplication + " invalid as " + $passport + " hasn't passed validation");
modify($visaApplication) {
setValidation( Validation.FAILED )
}
end
rule "Set application without validation info as passed"
dialect "mvel"
agenda-group "validate-application"
salience 10
when
$visaApplication: VisaApplication( validation == Validation.UNKNOWN )
then
System.out.println("debug >>>>>>>>>>>>> " + $visaApplication.validation);
System.out.println( $visaApplication + " is without validation info, consider OK for now" );
modify($visaApplication) {
setValidation( Validation.PASSED )
}
end
rule "Invalidate visa application where passport expires earlier than 6 months after visit end date"
dialect "mvel"
agenda-group "validate-application"
salience 20
when
$passport: Passport( validation != Validation.FAILED )
$visaApplication: VisaApplication( passportNumber == $passport.passportNumber,
$passport.expiresOn.isBefore(visitEndDate.plusMonths(6)),
validation != Validation.FAILED )
then
System.out.println( "Set " + $visaApplication + " invalid as " + $passport + " not valid 6 months after visit");
modify($visaApplication) {
setValidation( Validation.FAILED )
}
end
and here is the part of the console output that regards to the previous rules:
Set VisaApplication(#1, pass:CA-SARAH-1) invalid as Passport[no:CA-SARAH-1, name:Sarah Murphy] hasn't passed validation
Set VisaApplication(#4, pass:AU-JAMES-4) invalid as Passport[no:AU-JAMES-4, name:James Brown] not valid 6 months after visit
debug >>>>>>>>>>>>> FAILED
VisaApplication(#1, pass:CA-SARAH-1) is without validation info, consider OK for now
Set VisaApplication(#1, pass:CA-SARAH-1) invalid as Passport[no:CA-SARAH-1, name:Sarah Murphy] hasn't passed validation
debug >>>>>>>>>>>>> UNKNOWN
VisaApplication(#2, pass:CA-SIMON-2) is without validation info, consider OK for now
debug >>>>>>>>>>>>> UNKNOWN
VisaApplication(#3, pass:AU-EMILY-3) is without validation info, consider OK for now
As is obvious, the middle rule, the one with the salience 10, has been fired not only for the pattern validation == Validation.UNKNOWN but also for the pattern validation == Validation.FAILED! That is wrong and it happens in Drools 7.57.0.Final and doesn't happen in Drools 7.39.0.Final used by the original project. Is it a Drools bug or what? I'm pretty new to Drools so this really confuses me.
Second, why do I get the console output Set VisaApplication(#1, pass:CA-SARAH-1) ... twice? It seems to be a consequence of the reevaluation of the 1st rule, but this shouldn't have happened as this is the stateful and not the stateless session.
If you want to reproduce the console log, download the project, change Drools version to the latest one 7.57.0.Final, run the app io.github.aasaru.drools.section06.VisaIssue, and when asked,
enter 5 for the step and no when asked whether all passports should be considered as expired.
Congratulation, you found drools bug DROOLS-6542 - fixed in 7.60.0.Final
There is a workaround - remove mvel dialect for the rule "Invalidate visa application with invalid passport".
BTW, I'd like to propose you drools testing library which may save you great amount of time to mind complicated rules and simplify writing test scenarios. Here is how test may look like.
#DroolsSession(
resources = "classpath*:**/section06/step5/*",
showStateTransitionPopup = true)
public class Section6Step5Test {
#RegisterExtension
public DroolsAssert drools = new DroolsAssert();
#Test
public void test() {
ApplicationRepository.getPassports().forEach(drools::insert);
ApplicationRepository.getVisaApplications().forEach(drools::insert);
drools.getSession().getAgenda().getAgendaGroup("issue-visa").setFocus();
drools.getSession().getAgenda().getAgendaGroup("validate-application").setFocus();
drools.getSession().getAgenda().getAgendaGroup("validate-passport").setFocus();
drools.fireAllRules();
assertEquals(1, drools.getObjects(Visa.class).size());
}
}
Here is visualization popup of the test on 7.39.0.Final (or 7.57.0.Final without mvel dialect).
and the same on 7.57.0.Final (with mvel dialect)
I'm using OptaPlanner to solve a planning problem. I use drools to do score calculation. When I tried to print out the justificationList of each constraintMatch, I got objects which are instances of SubnetworkTuple besides my domain class objects. These seem to be objects that are used in
its internal implementation.
I have not used drools rule engine before :) . And this is my first time to use OptaPlanner. As far I know, justificationList should contain all objects that are involved in that rule.
drools rule:
rule "roomPriority"
when
EduClassRoomPriority($left : leftEduClass, $right : rightEduClass, $priority : priority)
exists(LectureOfEduClass(eduClass == $left,$room1 : room ) and LectureOfEduClass(eduClass == $right, $room1 != room))
then
scoreHolder.addSoftConstraintMatch(kcontext,-$priority);
end
java:
for (ConstraintMatchTotal constraintMatchTotal : constraintMatchTotals) {
String constraintName = constraintMatchTotal.getConstraintName();
Score totalScore = constraintMatchTotal.getScore();
Set<ConstraintMatch> constraintMatchSet = constraintMatchTotal.getConstraintMatchSet();
logger.info(totalScore.toShortString() + " constraint(" + constraintName + ") has " + constraintMatchSet.size() + " matches");
for (ConstraintMatch constraintMatch : constraintMatchSet) {
List<Object> justificationList = constraintMatch.getJustificationList();
Score score = constraintMatch.getScore();
logger.info(score.toShortString() + justificationList.toString());
}
}
I wonder what I can do to fix that problem, because these extra objects would confuse users. I can do instance of check. But That shouldn't be the right way to resolve it.
Thank everyone in advance.
Caused and fixed by https://issues.jboss.org/browse/DROOLS-4423 for 7.26.
I have a question about 2 Drools attributes - salience and no-loop
salience:
rule "Hello World"
salience -10
when
$m : Message( status == Message.HELLO, $myMessage : message )
then
System.out.println( $myMessage );
$m.setMessage( "Goodbye cruel world" );
$m.setStatus( Message.GOODBYE );
update( $m );
end
rule "GoodBye"
when
Message( status == Message.GOODBYE, $myMessage : message )
then
System.out.println( $myMessage );
end
We should expect the "GoodBye" rule to be fired first(since its salience is higher) however that doesn't happen and instead the "Hello World" rule gets fired up first and only then "GoodBye"
no-loop:
I understand that this attribute prevent from a rule to be executed to the same fact which will cause an infinite loop. My question is about an example about this attribute that I don't quite understand:
rule "interest calculation"
no-loop
when
$account : Account( )
then
modify($account) {
setBalance((long)($account.getBalance() * 1.03));
}
end
If there wasn't "no-loop" why that will cause an infinite loop?
Re salience: Logic always beats salience. If Message.status is initially set to Message.HELLO, the other rule doesn't qualify and "Hello World" is executed.
Re no-loop: A modify/update simply means that re-evaluation of everything begins from scratch as far as the modified fact is concerned. So, Account is updated, goes back to square one, and reevaluation creates another activation of this trivially matching rule.