To evaluate expressions in Java, at first we used GroovyShell, but noticed abysmal performance. Based on this post, we retooled, and went with the Java Scripting engine (JSR 223) , and used LuaJava as the engine. It performed much more reasonably. Since Lua doesn’t have a regular expression parser, we fall back to using BeanShell when we need to do regex comparisons.
(Image courtesy of CajuScript on Google Code)
But, let’s go back. This became a problem that required a scripting engine when I realized I didn’t want to be building a nested expression based on the result set, one row at a time. I’d rather compile a query, fill in the appropriate values at run time and evaluate it.
Again, here is the UI. This is what we need our engine to be able to model and act on:
Sidenote: I later heard about JSR-94 the Java Rules Engine spec, and implementations like Drools and JESS. I think our solution is better for our application, as we have build the front end with tools that our support team understands. The only part of those tools we’d use is the evaluator, which JSR-223 accomplishes.
Other Sidenote: The same guy who told me about JSR-94 also called said this engine is “hyper generic.” He’s a guy I respect, so there’s probably some merit to considering a rules engine where things are more static.
The Process
Getting back to our engine, the back end process is like this:
- A query executes to retrieve a result set with criteria and corresponding actions, and returns a set of DAOs
- An evaluator class, which does the actual JSR-223 stuff, is passed this set of DAO’s as well as a list of candidate objects to be evaluated
- Each object is evaluated against each set of criteria, and when the result is true, the actions associated with that criteria are applied.
Object Representation
So, think of the map as a triangle: at the base vertices, you have criteria and action groups. The top vertex is the record linking the criteria group to the action group. If the criteria evaluates to true, you execute the actions.
There is some complexity around the criteria grouping because of the nesting levels. I went with a tree that represented the data according to the adjacency list. The process of constructing the tree from the results set involves (as you’d imagine) lots of recursion, but it is much easier than the persistence issues involved in the PHP backend.
The code that deals with the actions will be based upon your application but we made use of reflection and tried to keep things simple but flexible. As performance isn’t an issue with actions nearly as much as criteria (the actions are only executed when the statement evaluates to true), reflections are worth the performance hit for us.
Code
This is the top of the “triangle”:
Since the criteria group can have nesting levels, we use a tree. For this to make sense, you need to see the criteria group table.
And for the creation of an expression to be parsed we use recursion (Javadoc is pretty helpful here):
Leave a Reply