March 2005
Discussion
Velocity: A template engine OR A Rule engine OR Both?
Most of the developers must be familiar with Velocity as a great open source template engine and I don’t think I need to say much about its uses and features as a template engine. This paper compiles its features as a rule engine.
I have been working with java for the past 6 years and recently, I got an opportunity to design and develop an Entitlement Application.
An entitlement is the result of the execution of a formula, or rule that specifies what must be considered when deciding whether a given user has rights to manipulate or use a given resource (or object). This rule may be composed of one or more conditions that might only be evaluated with application context specific data. In addition, a rule evaluation may result in more than simple yes/no answers and contain additional information that would be required by an application to now perform the requested access to the resource. These are called obligations. Therefore an Entitlement Service can be used both as a rule evaluator and a privilege data service.
In its simplest form an Entitlement Service performs standard authorization calls. For example, can a user perform a simple action against a named resource? In its more complicated form an entitlement may be a rule on how an object such as a financial transaction may be manipulated. In this rule the specific parameters of the transaction may be compared against set limits or current financial data. The rule in this case may be a representation of business logic.
There are four types of entitlement:
Coarse-grained entitlement - The control of access to broad categories of resources, for example, an entire application, an entire subsystem, or access to specific web pages.
Fine-grained entitlement - The granting or withholding of individual resources like buttons, links, menu choices on a page or screen, or functions within a program.
Transactional entitlement - An entitlement that checks privilege to manipulate a resource by examining very specific parameters about a ‘transaction’ upon that resource. The characteristics of or relationships between these parameters could be expressed numerically (<, >, =, …), using date and time information, or even as part of a formula (*,%,/,-,+).
Dynamic entitlement - Where a parameter of a ‘transaction’ represents information that must be calculated or retrieved from an external source. This information may be retrieved by a call to an application specific service.
So according to the requirements, this application has to have:
Some logic to return the stored data (Users, User Groups, Resources, Resource Groups, Permissions etc) based on some filters. For example return me all the users who have an access on this resource. Logic to check the permission. Permissions are Boolean in nature and don’t require any complex logic to process. Although, permission are very simple they are quiet powerful for building an entitlement application and cater to most of the cases of Coarse-grained and Fine-grained entitlements. A rule based engine to handle Transactional and Dynamic entitlements.
Now, we will directly focus on the uses of Velocity as a rule engine as this article is not about developing an entitlement application.
So, instead of developing my own rule engine I started looking around to find out a decent rule engine that can fulfill my requirements. And I began to explore rule engines like:
JEL ( http://galaxy.fzu.cz/JEL ), Bean Shell (http://www.beanshell.org/home.html), EL ( http://java.sun.com/j2ee/1.4/docs/tutorial/doc/JSPIntro7.html )
and many more, especially the ones with open sources because my management did not want to spend too much money on this application.
What I was looking for was an engine with the following features:
Ease of use in development. Should reduce my development time. Easy and flexible in terms of configuration. It should have very easy and familiar language to write rules especially for a non-technical admin to maintain. Easily extendable. Clear separation between admin (Rule author) and developer. Tested and reliable. Great community support. Nobody is perfect and everybody needs Help :)
But I couldn’t find anything good enough out there. Suddenly, Velocity got my attention. I have already used Velocity to generate my GUI. However, this time, I was looking at it with a different point of view. I was compiling its features in terms of a rule engine and it seemed to me a considerable fit for this new framework. Then it became a perfect fit when I realized its uses of toolbox concept as well. The toolbox concept provides the power to rule an author to access any EIS or application (almost anything) at runtime and with a complete transparency to the author.
Now lets go over a real life example and use Velocity as rule engine. Before we go ahead, lets configure Velocity.
Velocity can be used as a servlet in a web application or as a utility in a standalone application. In our case, we will use it as a standalone application. Now, as a standalone application, you can use it in two ways. One is singleton another is separate runtime instance. I used it as singleton. Following is the code sample:
public class RuleEngineVelocityImpl implements RuleEngine{public RuleEngineVelocityImpl{ try { // Initialize Velocity.Velocity.init(); }catch(Exception e){ //Do something. }}public String execute(String ruleId ,Map params){// get the velocity template from some storage.String template = getFromSomewhere(ruleId );// Create a Context object.VelocityContext context = new VelocityContext( params );// Get a template as stream.StringWriter writer = new StringWriter(); StringReader reader = new StringReader( template );// ask Velocity to evaluate it.boolean result = Velocity.evaluate( context, writer, ruleId, reader ); String strResult = null; if( result ){ strResult = writer.getBuffer().toString(); } return strResult;}public static void main( String[ ] args){// add the variable and their values which is being referred in// template/rule.Map params = new HashMap();params.put("Anykey", AnyObject );// Initialize Rule Engine and execute the rule.RuleEngine engine= new RuleEngineVelocityImpl ();String result = engine.execute( "MyRule", params);System.out.println("Result:"+ result);}}
Now, we will write a rule to check a credit limit for some user. A VTL template/rule for this may look like this:
#if( $creditLimit > 1000 ) Over limit. You can't complete this transaction.##OR just a Boolean value like false###else #set ( $creditLimit = 1000 - $creditLimit ) Now, your balance is ${creditLimit }.##OR just a Boolean value like true###end
You can store the rules in a file or in a database or in any another data storage. In my case, I stored these rules/templates in a RDBMS and I provided an Admin GUI to maintain them. Lets assume database table, ‘Rule’ with two columns, RuleId and Template. I insert a rule with RuleId “Chk_Credit_Limit”.
Now, lets modify the main method of “RuleEngineVelocityImpl “ class.
public static void main( String[ ] args){Map params = new HashMap();params.put("creditLimit", new Integer(200));// Initialize Rule Engine and execute the rule.RuleEngine engine= new RuleEngineVelocityImpl ();String result = engine.execute( "Chk_Credit_Limit", params);System.out.println("Result:"+ result);}
The output will be:
Now, your balance is 800.
Doesn’t it sounds very familiar and easy? It has to be. It is velocity, dude!!!
Now, lets see the power of toolbox concept.
Naturally, Velocity only supports integer but Velocity tools project adds the power to Velocity to do any kind of Math, Date processing and formatting which you will need to use for the rule engine.
Since a toolbox is extendable and it’s very easy to write a tool (almost any class with public method can be used as tool) you have unlimited opportunities to write N numbers of tools corresponding to your application/requirements context and all will be available to rule author for writing the smart rules. For example, lets say an airline company has come up with a business rule for year 2005 that states:
If booking date is on a special day list then get the discounted rate, otherwise use the normal fare.
Lets assume this discounted rate and special day list is stored in some database and both are being maintained by some other application. This special rate is valid only for year 2005 and it may change or can be completely eliminated in year 2006. In such case it will not be wise to build the application to cater to the above business requirements and change it later.
So lets address this problem with the rules. Before we write a rule we need two values available at a runtime. One is a special day list and the other is the discount rate. Lets write a Tool class (com.RateHelper) similar to what we have already in Velocity tools: Math, Date etc. The signature of this class would be as follows:
public Double getRate() ;public boolean isInSpecialDayList( String day );
Any class with public method can behave like a tool in Velocity. That’s the magic of introspection :)
After writing this class lets add it as a tool in Velocity engine. We already have a beauty called “XMLToolboxManager” to add N number of tools in Velocity automatically. We just need to specify our tools in an xml file called toolbox.xml.
<toolbox><tool> <key> rateHelper </key> <scope>application</scope> <class>com.RateHelper</class></tool></toolbox
Now, lets modify our RuleEngineVelocityImpl class to load this toolbox by adding the below line in the constructor after initializing Velocity.
XMLToolboxManager toolboxManager = new XMLToolboxManager();toolboxManager.load(this.getClass().getClassLoader().getResourceAsStream("toolbox.xml") );toolboxContext = toolboxManager.getToolboxContext(null);
And now replace the below line in execute method:
VelocityContext context = new VelocityContext( params );
with the this line:
VelocityContext context = new VelocityContext( toolboxContext, params );
This is context chaining. Please refer the apache documentation for more information on it.
Okay, we are all set. Lets write the business rule (using our custom tool) and store it in our database with RuleId “get_price”
#if( $rateHelper.isInSpecialDayList( $day ) ) $rateHelper.getRate()#else 2500 ##Regular price###end
Now, lets modify the main method again to execute our new rule.
public static void main( String[ ] args){Map params = new HashMap();params.put("day","02/28/05");// Initialize Rule Engine and execute the rule.RuleEngine engine= new RuleEngineVelocityImpl ();String result = engine.execute( "get_price", params);System.out.println("Result:"+ result);}
The output will be (if 02/28/05 is not in special day list): 2500
Isn’t it easy and powerful?
In addition to toolbox, I associated properties (key-value pair) to objects like User, User Group, Resource, Resource Group which I made available to rule author. This gives the author more flexibility to write smarts rules. For example, there is a user group in a company where some of the users can only “buy” the product and some of them can only “sell” the product and the rest of them can do both. We can associate a property called “ActionDirection” with every user of the group and the possible values for the property will be like “BUY”, “SELL” and “BOTH”. A rule author can use it freely in his rules to distinguish among the user actions.
Velocity was intensely written as a template engine and it’s one of pioneers in this area but you can see that it is also a great Rule engine. Combination of properties, toolbox and parameters makes it more powerful and perfectly suitable for this task. And most importantly, somebody has used it successfully as rule engine.
Reference:
1)http://jakarta.apache.org/velocity/index.html
Thanks to:
-Marc and Norm who brought me into this project.
-Margaret for her editing. :)
origional link: Velocity: A template engine OR A Rule engine OR Both?
Velocity A template engine OR A Rule engine OR Both
80酷酷网 80kuku.com