Drools - tutorial on writing DSL template
Few guidelines on writing domain specific language template for JBoss Rules
Now, having said that, I want to say that I am not planning to repeat the whole JBoss Rules manual - the Drools team done a great job. I just want to share from my personal experience, what I have come across while working with Drools when writing DSLs.
There are few upcoming changes coming in the future release of Drools (version 5) in terms of DSL. For example DSL will become more powerful in terms of regular expressions - it will be possible to include regexp in the DSL tokens. Edson Tirelli posted an article written by Matt Geis on Drools Blog, that talks about it and gives few examples.
Since I am working with Drools 4.0.7 these days, I will describe in the current post how to write DSLs for Drools 4.0.7.
Within a scope of Drools, DSL's job to map (expand) DRL expressions written in natural language into underlying programming code.
When writing DSL there several basic DO's and DONT's you should be aware of:
- Don’t put closing semi-column at the end of DSL expression line:
[when]Message status is {1} = m : Message(status == {1}); - Avoid different token names on the left hand side (before the “=”) and right hand side (after the “=”) of the DSL expansion:
[when]Message status is {3} = m:Message(status == {1})
[then]Log "{message}" = System.out.println("{bobo}"); - DSL expansion should be on one line. You can even have several expressions on the same line, but you cannot break the line. By putting expression on a new line will cause an exception during parsing:
[when]Message id is {id} = m:Message(id == {id})
[then]LogUpdate "{2}" = System.out.println("{2}");
update(m);
[when]Message status is {s} = m:Message(status == {s})
[then]Log "{2}" =
System.out.println("{2}"); - Avoid having DSLs with the same name. It can create a confusion. Remember that DSL is parsed from top to bottom, so when two DSLs with the same name exist, the top one will apply during expansion since it will be matched first:
[then]SetStatus {s} = System.out.println({s});
[then]SetStatus {s} = m.setStatus({s}); - If token has quotes on the left hand side of the DSL, then the token must have quotes on the right hand side of the DSL:
[then]SetMessage "{msg}" = m.setStatus("{msg}");
- There is no room for typos:
rule "set-hello"
when
True
then
SetMessage "Hello"
end[then]SetMessag "{msg}" = m.setStatus("{msg}");
- Token names can be literal or numeric characters, or both:
[then]Print1 "{1}" = System.out.println("{1}");
[then]Print2 "{msg}" = System.out.println("{msg}");
[then]Print3 "{23msg}" = System.out.println("{23msg}"); - If token on the left hand side has no quotes, and expression/function on the right hand side can accept parameter of type String, then the token can be placed between quotes on the right hand side:
[then]PrintInteger {1} = System.out.println("{1}");
[then]PrintInteger2 {1} = printingFunction("{1}"); - If you want to use declared variable in right hand side, make sure it is declared on the left hand side of the rule. Remember, DSL template is ONLY a template, it is a not a place to declare variables, declaration occurs only in DRL, DSL contains the expansion. Keep in mind that declaration and usage of the variable is per-rule basis:
rule "set-hello"
when
Message
then
SetMessage "Hello"
end[when]Message = m : Message() //declare and initialize 'm'
[then]SetMessage "{msg}" = m.setStatus("{msg}"); - White spaces before the “=” and after are allowed when writing a DSL line:
[then]SetMessage "{msg}" = m.setStatus("{msg}"); - If you are using a function in the right hand side of the DSL of the expansion, then make sure you provide necessary imports in DRL source for all the objects the function is using. For example if your function gets a proxy to a JMS Queue from JBoss, then you have to include imports for all the objects associated with this operation, like you normally would do it in normal Java class. Also the function itself must be present in source DRL.
- Left hand side of the rule, can evaluate boolean function instead of expression:
rule "CheeseType"
when
Cheese is of type "silton"
then
DoSomething
end
function boolean
someEvaluatingFunction(Cheese cheese,String type) {
if (cheese.getType().equals(type))
return true;
else
return false;
}[when]Cheese is of type "{1}" =
chz:Cheese() eval(someEvaluatingFunction(chz, "{1}"))
[then]DoSomething = System.out.println("Something");
Cheers
Related Posts
Drools 5 Case Study 2 - Complex Event Processing
Rule Engine Stress Testing
Brainteaser Drools: Testing Objects
Feedback by the Drools Team
JBoss Security - JMX Console
Drools 5 Case Study 1- Writing DSL for DRL rule
Drools 5 - Complex Event Processing
Drools - Stop executing current agenda group and all rules
If you like this post, then consider subscribing to the full feed RSS.
Re: Drools - tutorial on writing DSL template
I am pretty new to Drools. Can you explain how to write a drl file to access a POJO which is a member variable of another POJO. For example. If we have a School object which has a String variable name and another POJO student as its member variables. The classes are as shown below
class School{
private String name;
private Student student;
/*getter and setters*/
}
class Student{
private String age;
/*getters and setters*/
}
I know how to access and print the name variable of the School in the drl. How can I access the student object via the school object inside a drl file and print its age.
Re: Drools - tutorial on writing DSL template
rule "Blah"
when
//assign student into local variable 'stud'
s : School( stud : student )
then
System.out.println( stud.age );
//or try System.out.println( stud.getAge() );
end
Keep in mind, i haven't tested the above
Also, the following is a sample DRL from the Drools 5.0 examples (the DRL uses mvel dialect):
rule "Purchase notification"
when
$c : Customer()
$p : Purchase( customer == $c)
then
System.out.println( "Customer " + $c.name + " just purchased " + $p.product.name );
end
I think you should get an idea by now how to do what you want :)
Cheers
Re: Drools - tutorial on writing DSL template
Hi, I'm quite new to drools and while learning i'm bit confuse on conversion from drl to dsl, can help me by giving sample conversion for the following code,
rule 'Rank accomodation name'
salience 90
when
$accBase: AccomodationBase()
not AccomodationBase( eval( $accBase instanceof AccomodationRank) )
$accRank: AccomodationRank( level == $accBase.level, description == $accBase.description )
then
$accRank.setScore($accRank.getScore()+1);
end
thanks
Re: Drools - tutorial on writing DSL template
DRL
*****
rule 'Rank accomodation name'
salience 90
when
AccomodationBaseObj
IsAccomodationBase
AccomodationRank
then
IncrementScoreByOne
end
function boolean
isAccomodationBaseFunc(AccomodationBase accBase) {
if (accBase instanceof AccomodationRank)
return true;
else
return false;
}
DSL
*****
[when]AccomodationBaseObj = $accBase: AccomodationBase()
[when]IsAccomodationBase = eval(isAccomodationBaseFunc($accBase))
[when]AccomodationRank = $accRank: AccomodationRank( level == $accBase.level, description == $accBase.description )
[then]IncrementScoreByOne = $accRank.setScore($accRank.getScore()+1);
Keep in mind - I haven't tested it. But please do come back and tell me if my solution worked for you or you had to make any corrections.
Cheers :)
Re: Drools - tutorial on writing DSL template
1. what are the errors that you are getting when validating?
2. and yes - you have to create .dslr and dsl for particular rule. in .dslr you put your rules and in .dsl you put your expansion
3. What version of Drools and MVEL are you using?
4. Have a look at this article Human-readable Rules with Drools JBoss Rules 5.0, the guy gives few nice examples about how to create DSL with rules using MVEL dialect
Re: Drools - tutorial on writing DSL template
I'm receiving as : Sorry, a technical error occurred. Please contact a system administrator.
i'm using drools 5.0.1 and what is mvel - is mvn version, if yes i'm using Apache Maven 2.2.1
i would like to tell my situation here, i have to create dsl for existing drl which use drools-jbrms 4.0.7,
since i'm new for drools i just go through with Jboss Drools Busi Rules material, so i use drools 5.0.1, when i follow the book i feel easy but when start create dsl for existing i feel complexity, my steps are:
- create jar file from eclipse for the existing project's java file which have to create dsl
- create new category in admin, create new package, create pojo model jar by upload created jar file into created package
- then create DRL rule (technical rule - test editor) and paste as below;
salience 90
when
AccomodationBaseObj
IsAccomodationBase
AccomodationRank
then
IncrementScoreByOne
end
function boolean
isAccomodationBaseFunc(AccomodationBase accBase) {
if (accBase instanceof AccomodationRank)
return true;
else
return false;
}
- then create DSL busi rule (test editor) and paste below:
[when]AccomodationBaseObj = $accBase: AccomodationBase()
[when]IsAccomodationBase = eval(isAccomodationBaseFunc($accBase))
[when]AccomodationRank = $accRank: AccomodationRank( level == $accBase.level, description == $accBase.description )
[then]IncrementScoreByOne = $accRank.setScore($accRank.getScore()+1);
- so when i validate it, i received Sorry, a technical error occurred. Please contact a system administrator
- the existing drl content as below:
salience 90
when
$accBase: AccomodationBase()
not AccomodationBase( eval( $accBase instanceof AccomodationRank) )
$accRank: AccomodationRank( level == $accBase.level, description == $accBase.description )
then
$accRank.setScore($accRank.getScore()+1);
- sorry for disturbing u, thanks for Human-readable Rules with Drools JBoss Rules 5.0, its really useful
Re: Drools - tutorial on writing DSL template
1. I meant MVEL dialect, not Maven. When you create rules, you can write them using Java or MVEL dialect. If you are using dollar sign '$' in your rules, it means you are using MVEL dialect . I suggest you do some more reading on Drools.
From reading Drools mailing list, some user were experiencing problems when using MVEL dialect in curtain situations (i am not suggesting that this is your case, just an example nor i am suggesting not to use MVEL)
2. It seems to me you are throwing a lot of problems in one bucket. I thought you had a problem with my suggested solution, but now i think you are having some problems on top of that: system mis-configuration or project setup.
Try to address your problems one by one. Please do not get offended for me saying this, but from what I understand by reading your reply its like saying: "My TV does not work because I cannot start my car".
The reason why I feel this way is that the message "Sorry, a technical error occurred, Please contact a system administrator" does not tell me whether the problem is because rule could not validate/compile or because you have some working environment mis-configuration or problem with project setup. Any exceptions at all?
Try to create simple Java/Drools project outside Eclipse. Create a simple test POJO and try to compile a RuleBase and see what happens.
I am glad you found link I provided useful.
Cheers
Re: Drools - tutorial on writing DSL template
rule 'Refresh scores'
salience 10
dialect "mvel"
when
$rank: Rank( status == Rank.SCORING )
then
System.out.println("Modify Rank >> " + $rank.getDescription() + " --> status = " + $rank.getStatus());
modify ( $rank ) { status = Rank.SCORED };
end
the converted dsl are as follow:
dsl file:
---------
[when]Create rankObject Reference = $rank: Rank( status == Rank.SCORING )
[then]Listing modification = System.out.println("Modify Rank >> " + $rank.getDescription() + " --> status = " + $rank.getStatus());
[then]Changing status = modify($rank){status=Rank.SCORED};
dslr file:
----------
salience 10
dialect "mvel"
when
Create rankObject Reference
then
Listing modification
Changing status
the problem is when viewsource from dslr file it show as follow
rule 'Refresh scores'
salience 10
dialect "mvel"
when
$rank: Rank( status == Rank.SCORING )
then
System.out.println("Modify Rank >> " + $rank.getDescription() + " --> status = " + $rank.getStatus());
Changing status
end
where it not showing the Changing status code, i just try with few alteration but not solve, can any one help me
thanks
Re: Drools - tutorial on writing DSL template
Thnaks for the tutorial, but I'm newbie for the drools, i just started to learn from stract, can anyone help me for students marking system
let say,
when a>0% and a<40%
then b = "Student" + "_fail"
when a>40% and a<50%
then b = "Student" + "_D"
when a>50% and a<60%
then b = "Student" + "_C"
when a>60% and a<70%
then b = "Student" + "_B"
when a>70% and a<100%
then b = "Student" + "_A"
how can implement this in drools concept, if passing values from class for a as 45% and b as Student A
Re: Drools - tutorial on writing DSL template
rule "FindStatus"
when
student: Student ( mark > 0 and mark < 40)
then
status = "student a" + "fail";
when
student: Student ( mark > 41 and mark < 60)
then
status = "student a" + "D";
when
student: Student ( mark > 61 and mark < 70)
then
status = "student a" + "C";
when
student: Student ( mark > 71 and mark < 80)
then
status = "student a" + "B";
when
student: Student ( mark > 81 and mark <= 100)
then
status = "student a" + "A";
end
is above code ok, or can improve in any way
thanks
Re: Drools - tutorial on writing DSL template
hi bro, if with 5 separate rule is below is correct
rule "find_fail"
salience 100
when
student: Student ( mark > 0 and mark < 40)
then
status = "student a" + "fail";
end
rule "find_D"
salience 90
when
student: Student ( mark > 41 and mark < 60)
then
status = "student a" + "D";
end
rule "find_C"
salience 80
when
student: Student ( mark > 61 and mark < 70)
then
status = "student a" + "C";
end
rule "find_B"
salience 70
when
student: Student ( mark > 71 and mark < 80)
then
status = "student a" + "B";
end
rule "find_A"
salience 60
when
student: Student ( mark > 81 and mark <= 100)
then
status = "student a" + "A";
end
how about if using only 1 when & then shall i use if condition, can u give sample for that
thanks
Re: Drools - tutorial on writing DSL template
hi I done i more thing as below,
rule 'Find fail'
salience 100
when
$student : Student()
$student( score > 0 and score < 50)
then
$student.setName( $student.getName + "_fail");
end
is the above ok or can improve in any way, I have create similar code to Find A, B, C, D
Shall I put eval in $student( score > 0 and score < 50) or this already ok
thanks
Re: Drools - tutorial on writing DSL template
Yes, your latest rule code looks ok.
Give it a run test and tell me how it went ...
Also, I would suggest to have a simple test with only one rule. Once you see that it has worked for you, you can complicate things by adding more rules.
Regarding eval, your rules will be matched using the condition in when. So no need for eval, also eval is slow if you concern about performance.
Cheers
Re: Drools - tutorial on writing DSL template
hi bro, I have tested where the code as below:
rule 'test'
dialect 'mvel'
salience 100
when
$student:Student()
$student((score>0) and (score<40))
then
$student.setResult($student.getResult()+ "_fail");
end
I received error as below when validate
[test] [ERR 102] Line 6:34 mismatched input 'and' expecting ')' in rule 'test' in pattern $student
[test] [ERR 101] Line 6:39 no viable alternative at input 'score' in rule 'test'
I know there is something prob at $student((score>0) and (score<40)), I'm trying to rectify but can u help me here
Re: Drools - tutorial on writing DSL template
I need to know how we can validate an XML using rules.
Suppose we have a an XMl say
<class>
<student>
<name>Bala</name>
<age>26</age>
</student>
<student>
<name>Ajay</name>
<age>24</age>
</student>
</class>
How can we pass this xml (not a POJO) to the .drl file and do some validation like if age is less than 24 do something or else do something.
Thanks in advance,
Re: Drools - tutorial on writing DSL template
How can I pass an xml to the drl file and fire rules based on the tag values encountered in the xml.
Suppose we have an xml file like
<class>
<student>
<name>Bala</name>
<age>26</age>
</student>
<student>
<name>Ajay</name>
<age>24</age>
</student>
</class>
I want to fire a rule if the age of the student is greater than 24 or else fire another rule.
How to achieve this?
Thanks in adavnce
Re: Drools - tutorial on writing DSL template
Hm... I never tried to do this. I know it is possible to create KnowledgeSession by loading rules from XML... But instead of POJO an XML?
Honestly, I do not know how... Also, I think they do not support at the moment and asserted fact must be a POJO. But.. I may be wrong here...
In addition, I tried to go through their docs, but I haven't noticed anything that would suggest that it is possible to load XML instead of POJO.
Cheers
Re: Drools - tutorial on writing DSL template
I found out that the earlier versions of Drools used to support direct xml manipulating inside the .drl file by using the SXC (Simple XML Compiler) utilities. Now there is not much development happening on that front and I think that they have moved onto POJO based rule validations.
For my problem, I broke my head for a long time looking at the SXC and the older versions of Drools in vein. Finally, I found out a solution.
I created a XMLHandler class which takes an xml file in its constructor and contains methods to steps through the xml using the javax.xml.xpath.* classes. Pass this XMLHandler class instance to the drl file and achieve what was expected.
Bala
Re: Drools - tutorial on writing DSL template
Let me explain the scenario first A Town is taking a survey of the number of Animals present in their locale.
So I have a Town class which hold the list of animals Then I have the Animal class which has members
type (Dog or Cat or...)
incidents(incidents reported against the animal)
hasOwner (whether the animal has an owner)
Now I create a huge list of dummy animals and set it to the Town's animal list. and set the Town object into ksession and I printed all the animals inside the list using the below rule
rule "Print Animal Type"
when
$town:Town()
Animal($animalType:type) from $town.animalList
then
System.out.println("Type = " + $animalType);
end
Can I write a a complex scenario like,
1.If I find an Animal inside the list to be of type dog
2. Then, take that dog object and fire another rule passing this dog and print whether it has an owner or not
I know how to do it it in one rule at a shot but I just wanted to know whether we can pass arguments to another rule like the case in my scenario?
Thanks in Advance,
Bala
Re: Drools - tutorial on writing DSL template
Hm... Did you try something like that:
rule "Find the dog"
when
//get the dog object from the list: $dog
then
insert( $dog);
end
The insertion will make the engine to be aware of data, so the $dog can be processed against the rules.
Please note - I haven't tested it
Let me know how it went...
Re: Drools - tutorial on writing DSL template
Got a problem
As you suggested, I rewrote the rules like this
rule "Get Zero Incident"
when
$town:Town()
$currentAnimal: Animal($animalType:type, incidents == 0) from $town.animalList
then
insert($currentAnimal);
end
rule "Print Details"
when
$currentAnimal:Animal()
then
System.out.println("Zero incident Animals: " +$currentAnimal.getName());
end
And it prints the name of all animals which has zero incidents against them
If I write another rule to check for Animals for more incidents and use the same $currentAnimal inside that like this
rule "Get More Incident"
when
$town:Town()
$currentAnimal: Animal($animalType:type, incidents == 8) from $town.animalList
then
insert($currentAnimal);
end
rule "Print Details-2"
when
$currentAnimal:Animal()
then
System.out.println("More incident Animals: " +$currentAnimal.getName());
end
It is taking printing the results for both the $currentAnimals in both the rules
In the rule "Get More Incident" why is it not updating the $currentAnimal object we made in the first set of rules?
or is there some way around?
Bala
Re: Drools - tutorial on writing DSL template
When you insert a fact or when you update the fact, the engine first evaluates all the LHS of the rules against that fact. So when you do insert($currentAnimal);, the engine checks which LHS will match.
Note the LHS of your print details rules:
when $currentAnimal:Animal(), it means when true in other words. Therefore, your LHS will always be true both in Print Details and Print Details-2. That is why you see the result printout for both rules. I think this is what happens in your case...
Maybe try to add some extra condition in LHS?
Re: Drools - tutorial on writing DSL template
So you mean to say it can be achieved by rewriting the full set of rules like this
rule "Get Zero Incident"
when
$town:Town()
$currentAnimal: Animal($animalType:type, incidents == 0) from $town.animalList
then
insert($currentAnimal);
end
rule "Print Details"
when
$currentAnimal:Animal($animalType:type, incidents == 0)
then
System.out.println("Zero incident Animals: " +$currentAnimal.getName());
end
rule "Get More Incident"
when
$town:Town()
$currentAnimal: Animal($animalType:type, incidents == 8) from $town.animalList
then
insert($currentAnimal);
end
rule "Print Details-2"
when
$currentAnimal:Animal($animalType:type, incidents == 8)
then
System.out.println("More incident Animals: " +$currentAnimal.getName());
end
No other way we can carry forward the Animal objects selected in "Get Zero Incident" and "Get More Incident" rules to "Print Details" and "Print Details-2" rules respectively?
<em />
Re: Drools - tutorial on writing DSL template
At the moment, I can think of only this way to carry the objects forward...
Just to point something out regarding your LHS:
$currentAnimal:Animal($animalType:type, incidents == 8), why do you have assignment there $animalType:type, you dont use it, do you?
To summarise - at the moment I can think of insert or passing Animal object to some function in RHS instead of to a rule.
Cheers
Re: Drools - tutorial on writing DSL template
Re: Drools - tutorial on writing DSL template
Ok, so you want a temporary place holder for an object until you will
need that object, right?
So just like you described - you want to put aside Pluto temporarily
until it will be needed
Have a look at:
<a target="_blank" href="../../2008/05/11/drools_working_with_stateless_session.html">http://javabeans.asia/2008/05/<wbr></wbr>11/drools_working_with_<wbr></wbr>stateless_session.html
Note the global variable that can play a role of a data holder
is this (for now) answers partially to your question ?
Re: Drools - tutorial on writing DSL template
So according to what you pointed out, I can declared a global map and rewrite my rules like this
global Map animalMap
rule 1
when
Get an Animal (lets name it $selectedAnimal) which has name Pluto
then
Put $selectedAnimal in the global animalMap with a "key "
end
rule 2
when
get the animal object from animalMap corresponding to the "key" and check whether its of type dog
then
print "dog obtained"
end
Am I right?
But I tried to play around with the hashMap inside the drl file and i could achieve what I was supposed to do.
in rule2
How to get the animal object corresponding to a particular key in the HashMap?
$animal: Animal from animalMap["Key"] //is this the way??
I am getting a NullPointer exception when I try to do this.
Please help.
Bala
















