Rule engines in Java: Jboss Drools
Introduction
This article will be about rulebased systems in Java, and about (Jboss) Drools in particular.
First, I will give an introduction on rulebased systems and write about when to use them. If you are just interested in a tutorial on Drools, just skip these two first sections.
In the third section I will give a hands-on tutorial on using Drools, which is easy to follow and reproduce, and for which you only need to know the Java programming language.
At last, I will give another tutorial, on what might be the most popular usage of Drools today: using it for authorization decisions with the Jboss Seam framework. This part is only interesting for people using Seam or people planning to use it.
Rulebased systems
Rulebased systems were the subject of my master thesis in computer science.
In rulebased systems, knowledge is represented in the form of if-then rules. For example, the following rule could be part of such a system:
IF Person wants to buy a house Person does not have enough money for a house THEN Person goes to bank for a loan
To actually trigger this rule, we will need a Person object/fact matching the conditions of the rule. We need to provide our rules with a number of facts where they can work upon.
To build a rulebased system, we use rule engines such as Drools. These rule engines will do the patternmatching of the facts to the rules for us(and do this with very high performance using the Rete algorithm), will decide on the order of execution of the triggered rules and eventually, will execute them. Because of this, developers using a rule engine – who can be a lot less technical than regular software developers, especially in combination with domain specific languages(which are not covered in this article) – can solely focus on making sure all the necessary facts are available and all required rules are added to the rulebase.
Rulebased systems are often called expert systems, because in most cases they try to simulate the behavior of some human expert on a particular domain. My master thesis for example, was about using a rule engine to implement a fully fledged loan advisor. Although the text ended up being a bit academic(it had too..), with a lot of stuff about backward chaining and free variables and such, it also involved interviewing different Belgian mortgage experts in the banking industry and puzzling a rule base on the subject together.
When to use rulebased systems
I kept in touch with one of the experts I regularly interviewed and he told me they are actually using a rulebased system now to help make decisions about granting loans. When I was writing the master thesis, like 5 years ago, rulebased systems were used here and there, but they seem to be more mainstream right now, getting adopted by smaller projects as well.
As a rule of thumb, if you need to make a lot of context dependent decisions, and you would end up with a lot of nested if-then-else statements when writing regular code, you should consider using a rule engine.
For one, the regular if-then-else statements would be a nightmare to maintain and you are likely to forget to treat some use cases anyway. Bugs are likely as well. In code that is barely readable, an && might be swapped for an || unnoticed, and brackets might be forgotten or placed at the wrong spot.
The second most heard argument for using rule engines is about the fact that rules, which would be code in a regular program, can be treated as data now. This means they can be changed without having to change the code and recompile. Moreover, in some cases they can be changed by non-technical users.
There are some other arguments(like the possibility to make decisions with incomplete information), but I think that in general it is usually the combination of the two previous arguments that make people choose for rulebased solutions.
For example, we are currently considering to use Drools on a certain project to process an XML file. Processing this XML would take quite a few if-then statements, but the highest depth of nesting is probably only 2, maybe 3 in some cases. I do believe this would be maintainable(although not the most popular piece of code to work on). But the policy on how to process this XML file changes frequently. Recompiling and redeploying the code everytime the policy is changed is something everyone wants to avoid, ánd there is an expressed need to be able to change the policy without technical guys. Given all that, using Drools seems like the appropriate solution.
“Non-technical people changing rules” should be taken with a grain of salt though, and be thought of before the project. For example, the mortgage expert told me that they could read the rules, with some effort, from the beginning. But to change or add one, they would just email someone at IT, who would do it. Now they have an interface for it, which makes sure they do not violate any syntax, and uses domain specific syntax that translates a line like Person(salary > 1000) to “A person with a salary higher than 1000euro” to them.
For our own idea about the XML file processing, we will probably ask our client some commitment on providing people that can handle some technical stuff. Since writing a domain specific language for a subproject this small would probably be overkill.
Background: Jess vs Drools
You might be wondering why Im just starting out with Drools now. Well, that is because I used Jess, another Java rule engine, to implement the mortgage expert and some other stuff.
Rules in Jess are less typed and look less Javalike because of their prologlike notation, but overall, Jess and Drools are very similar.
Some things have changed though. 5 years ago, I was not working on Java objects directly. Facts in Jess often didnt have a Java object equivalent. You could link a fact to an actual Java object – such linked facts were called “shadow facts” – but it was not so easy to operate on them, since you basically would have two representations of the same thing, one as a Java instance and one as a Jess fact, which you had to keep up to date with each other. Nowadays, Java instances as facts are not only supported by Drools but by Jess as well.
I speeded through the Drools documentation and everything looked quite familiar. I noticed some practical improvements here and there though. For example, there are now some operators like “contains” that make it easy to perform checks on collections. Unless I missed a warning sign, these checks are proper checks that are included in the Rete network(unlike using “eval” in the conditions part of a rule, which basically bypasses the whole Rete network).
I guess the main reason why people choose for Drools is because Jess is commercial/not free to use and Drools is part of the Jboss stack. For example, for people that know Jboss Seam: if you generate a Seam project with seam-gen, Drools comes already configured with the generated project, which makes the barrier to step in pretty low.
Tutorial on Drools
We only need 4 files for this tutorial, which can be downloaded from the link. The 4 required files will be integrally provided below as code snippets as well.
The 2 fact objects
We are going to work with two different Java objects: Person and LoanFormula. These are just regular POJOs, and are only shown here for reference.
Person:
package droolstest; public class Person { private String name; private Long salary; private boolean loyalCustomer; public Person(){} public Person(String name, Long salary, boolean loyalCustomer) { this.name = name; this.salary = salary; this.loyalCustomer = loyalCustomer; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Long getSalary() { return salary; } public void setSalary(Long salary) { this.salary = salary; } public boolean isLoyalCustomer() { return loyalCustomer; } public void setLoyalCustomer(boolean loyalCustomer) { this.loyalCustomer = loyalCustomer; } }
LoanFormula:
package droolstest; public class LoanFormula { private Long minSalary; private String name; public LoanFormula(){} public LoanFormula(Long minSalary, String name) { this.minSalary = minSalary; this.name = name; } public Long getMinSalary() { return minSalary; } public void setMinSalary(Long minSalary) { this.minSalary = minSalary; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
The rules
In Drools, rules are stored in drl files. We will only discuss the two rules in the following drl file. Although they are not really useful, they should be enough to illustrate how Drools works.
package droolstest; rule "loyal_customer_rule" when Person(loyalCustomer == true) $lf: LoanFormula() then System.out.println("Loan "" + $lf.getName() + "" possible because person is a loyal customer."); end rule "salary_rule" when Person($salary: salary) $lf: LoanFormula(minSalary <= $salary) then System.out.println("Loan "" + $lf.getName() + "" possible because person has a satisfactory salary."); end
First, we declare the package. This package works a bit like the package of a Java class. The name of every rule has to be unique within its package.
Then we declare our first rule. We name it “loyal_customer_rule”, and it is supposed to make sure that if a person is a loyal customer, all loan formulas will be available to him.
The two most interesting parts of a rule are obviously the when and the then part. The when part, which is called the left hand side(LHS) of the rule, contains the conditions that need to be fulfilled in order for the then part, called the right hand side(RHS) of the rule, to execute.
It takes a bit of time to get used to the way facts are matched by rules. The “loyal_customer_rule” will match for every possible combination of a person with his property loyalCustomer set to true, and any loanformula.
Note the way we bind variables in the left hand sides of rules. We just put $identifier: in front of a variable, after which the value is bound to that variable, which is further illustrated by our second rule.
The then parts in these two rules do not do anything useful. Usually, you will want to insert, retract or modify facts, or in some cases even call a service. We are just writing to the console to keep the example simple and clear.
We could add all possible loanformulas as facts instead, after which rules like this would weed out the impossible ones:
rule "salary_too_low_rule" when Person($salary: salary, loyalCustomer == false) $lf: LoanFormula(minSalary > $salary) then retract($lf); end
In the end we could then query the collection of facts for all remaining loan formulas.
Executing the rules
So far, we described how it all should work, but we didnt see the rule engine in action so far. The following class will change this:
package droolstest; import org.drools.KnowledgeBase; import org.drools.KnowledgeBaseFactory; import org.drools.builder.KnowledgeBuilder; import org.drools.builder.KnowledgeBuilderFactory; import org.drools.builder.ResourceType; import org.drools.io.ResourceFactory; import org.drools.runtime.StatefulKnowledgeSession; public class DroolsTest { public static final void main(String[] args) { KnowledgeBase knowledgeBase = createKnowledgeBase(); StatefulKnowledgeSession session = knowledgeBase.newStatefulKnowledgeSession(); try { Person person = new Person("Test User",1200l,true); LoanFormula loanFormula1 = new LoanFormula(1000l, "Low salary formula"); LoanFormula loanFormula2 = new LoanFormula(3000l, "High salary formula"); session.insert(person); session.insert(loanFormula1); session.insert(loanFormula2); session.fireAllRules(); } finally { session.dispose(); } } private static KnowledgeBase createKnowledgeBase() { KnowledgeBuilder builder = KnowledgeBuilderFactory.newKnowledgeBuilder(); builder.add(ResourceFactory.newClassPathResource("basic.drl"),ResourceType.DRL); if (builder.hasErrors()) { throw new RuntimeException(builder.getErrors().toString()); } KnowledgeBase knowledgeBase = KnowledgeBaseFactory.newKnowledgeBase(); knowledgeBase.addKnowledgePackages(builder.getKnowledgePackages()); return knowledgeBase; } }
If you run this code, the rule engine comes in action, and you will see the printouts to the console of the rules we discussed in the previous section, proving that the rules are actually executing.
Notice that we first construct our knowledgebase from our basic.drl file. It is at this point the Rete network and everything is constructed. If you use Spring or Seam, this should probably become a managed bean/component. You dont want to instantiate this over and over again(although you might need to rebuild it once in a while when drl files are added or changed).
We then ask our knowledgebase for a session, after which we insert some facts into this session. We then ask to fire all rules, which concludes the tutorial.
Seam and contextual security
The problem
As a web developer, you probably had to add security to a web application more than once. Chances are high that you usually used rolebased authorization. A user logs in, gets his roles assigned, and depending on his roles, he is able to do (or not do) certain things in the application.
But things are not always as simple. Imagine you have some kind of project object, which only admins are allowed to edit, but also regular users who are product owner of the project.
I have to admit we usually implemented these additional checks(e.g. whether the logged in user is the product owner of the project or not) outside of the security framework. We kept it clean, but still, it didnt feel right to shatter security-related code over different places. After all, security is a crosscutting concern, which ideally, we should be able to add, change or remove on the fly.
Rolebased authorization will obviously not suffice for this problem. We need to be able to take context into account. For example, we are deciding on securing a specific project, not determining a policy that is valid for all possible projects.
A rule engine is exactly what we need to solve this problem. And the Seam framework provides us already with exactly what we need to protect our pages and methods using rules.
Enabling Drools in your Seam project
We are going to assume that you are familiar with setting up a Seam project. This is way beyond the scope of this post.
Ofcourse, you will need to include all Drools dependencies in your project.
You should also make sure the following components are added to your components.xml(the drools namespace is http://jboss.com/products/seam/drools):
<drools:rule-base name="securityRules"> <drools:rule-files> <value>/security.drl</value> </drools:rule-files> </drools:rule-base> <security:rule-based-permission-resolver security-rules="#{securityRules}"/>
You will also need to make sure the security.drl file is found by the application. The typical place in your IDE in a Mavenized Seam Project is probably your ejb/src/main/resources source folder. Since we dont have any rules so far, the file can be empty. You could already import the classes you are always going to need later though, ending up with the following security.drl:
package Permissions; import java.security.Principal; import org.jboss.seam.security.Role; import org.jboss.seam.security.permission.PermissionCheck;
The tutorial
I did not include any downloads for this tutorial. If you are following this, I am expecting you to have a Seam project set up, and it shouldnt be hard to follow the tutorial by playing a bit with your own code.
I am going to assume again that you know the basics of Seam security and its rolebased authorization as well. You should be familiar with @Restrict(“#{s:hasRole(…)}”) for example.
If we want to protect a method using rulebased authorization, we will need to guard it with a Restrict annotation like this:
@Restrict(“#{s:hasPermission('projectHome','update', projectHome.instance)}”) public String update(){ return super.update(); }
So instead of hasRole, we are using hasPermission now, which takes 3 arguments.
The first two arguments will be used to created a PermissionCheck(notice the import in our security.drl file above) which will be added to the collection of facts/working memory of our Drools session. Of course, the granted property of this PermissionCheck will initially be false.
The third argument is an object we are passing through, and which will be added to the working memory of our Drools session as well. In this case it is the value of the instance property of our projectHome component, which is an instance of our Project class.
We know now of two objects added to the working memory of our Drools session: a Project instance and a PermissionCheck instance.
Which other facts will we have available to reason about in our rules?
Seam will – if there is any – add the Principal(from its Identity component) as a fact too. If this Principal has any roles, these roles will be added as seperate facts as well. Again, notice the imports above on our security.drl file.
Now, let us define a rule that says the product owner of the project is authorized to update the project:
rule “modifyProjectByProductOwner” when $perm: PermissionCheck(name==”projectHome”, action in (“update”,”remove”), granted == false) Principal($username: name) Project($productOwner: productOwner) ProductOwner(name == $username) from $productOwner then $perm.grant(); end
Note that we could define a catch-all for admins:
rule “allowAdminsEverything” when $perm: PermissionCheck(granted == false) Role(name == “admin”) then $perm.grant(); end
or define that viewing is allowed for everyone, even users that are not logged on:
rule “viewingForEveryone” when $perm: PermissionCheck(action in (“view”), granted == false) then $perm.grant(); end
We end up with a security policy that is easy to extend and modify, and has no problems taking context into account. If you are looking for an opportunity to try Drools: contextual security is probably a requirement in your project too.
Excellent idea for using drools for security. I think I will try it in my current project.
Was this answer helpful?
LikeDislikeHi, thanks for the tutorial. I am just wondering how Drools can be use to make UI decision. It means doing a wizard style UI, where the input of one step can go thru a Rule engine and determine wha the next UI step is.
Thanks
Good one.Recently I have also got chance to work for drool 5 now jboss rules
Well i am also planning to write one article especially DSL supported in drools and how BA’s can modify them in excel files itself.
No doubt drools it best free rule engine available yet it has problems with documentations and there’s no such drl syntax or wiki available one has to learn by trail and error.
Appreciate your initiative writing one poc out here which can help fellow developers to figure it out in short.
Many Thanks
Was this answer helpful?
LikeDislikeVery nice article. Always wanted to see such a simple Getting-Started-Tutorial for Drools. Thanks to you, I am considering to add it to one of our projects.
Was this answer helpful?
LikeDislikeThank you.
Can you tell me 0n which version of java you had done this?
and how can we pass some values to drools from my java application and it should return too?
Regards,
kanhaiya
Was this answer helpful?
LikeDislike