Evaluating Spring EL Expressions Against Multiple Contexts

10 Apr

Any descriptor-(e.g. XML)-driven java API begins with field access. This is a given: you have to provide your users some way to tell you what field(s) on a bean your API should act on. But why settle for simple property access? Especially for APIs that perform content generation, it’s hard to ignore the convenience offered by something like JXLS’ Expression Language.

However, implementing a robust EL evaluation solution for your users can be less than trivial. Spring EL, or “SPEL” as they call it, is a good starting point for an out-of-the box solution. It gives the ability to basically break the actual evaluation of an EL statement into a 4 line call, like in this example that I’ve shamelessly copied from the official documentation:

Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name");
EvaluationContext context = new StandardEvaluationContext(tesla);
String name = (String) exp.getValue(context);

An ExpressionParser converts a string expression to an Expression object. When you get the value of a parsed expression you do so within an EvaluationContext. This EvaluationContext will be the single object from which all properties and variables within your string of EL are accessed.

Keyword: single.

That’s no good, because most likely an API capable of evaluating EL expressions will need to be able to do so against multiple beans. Now, there are a couple official-ish workarounds for this, but they each fall a bit short (tl;drs can skip the next 4 paragraphs for a solution):

Using a map as your evaluation context, you can enable access of multiple contexts by forcing the user to prefix property access with some name that they register each context under. This works since the standard MapAccessor property accessor packaged with SPEL allows access of map values by name. In the above example, if the user had registered the Nikola Tesla inventor bean under the name “tesla”, and you had placed it into a map with that key, and used the map as the EvaluationContext, you could have accessed the same property as the first example by calling “tesla.name”.

But with this solution, just looking up “name” no longer works. That’s unfortunate. Spring Web Flow accomplishes something a bit closer to ideal: you can specify a field in your Flow XML either qualified with a scope, like “flowScope.thePropertyIWant”, or without a prefix, like “thePropertyIWant”. The expected result is that if you provide a scope qualifier, the property will be pulled from that scope, and if you don’t provide a qualfier, it will search scopes in order until the property is found, or return null if not found at all. This is a good pattern to follow, but not trivial in implementation.

Spring Web Flow requires a couple things to make this work. First, they have to consolidate all their contexts onto a single bean to use as the single EvaluationContext. In this case they use their RequestContext interface which hoards pretty much every tidbit of information of or pertaining to a single request. This is where the qualified property access comes from: for example, if you’re looking for “flowScope.thePropertyIWant”, it works because there’s a map-returning getter method called getFlowScope() chilling on that mega bean which (hopefully) contains your “thePropertyIWant” property. The scope searching part (if you didn’t qualify your property name) is handled by their ScopeSearchingELResolver.

That all works fine for Web Flow which has a static number and set of contexts to search through. But when developing your own API, you’re likely going to have multiple contexts registered by the user, so you won’t be able to hard-code ordered access to these. The most obvious solution would be to use implement the context as a map (like previously described), and wire in a single PropertyAccessor that searches through the contexts in the order that they were registered. However, this only works for Web Flow because they knew that all the searchable contexts were maps. Things get more complex when you have to handle searching within context objects that could be of any class. But that’s what PropertyAccessors were for in the first place, so what you need is the ability to both search through all contexts in order, and also utilize all registered PropertyAccessors in determining if a property exists on each context.

The solution turns out to be to handle all EvaluationContext searching within the EvaluationContext itself. To this end, I created a NamedScopeEvaluationContext. The class and accompanying test case can be acquired as a mavenized example project from our SVN here: http://svn.springjutsu.org/examples/multi-scope-spel-example/

Implementing is straight-forward. Instead of using SimpleEvaluationContext when evaluating SPEL, use the provided NamedScopeEvaluationContext implementation. Register any number of contexts to it by calling namedScopeEvaluationContext.addContext(“contextName”, contextObject); You can either qualify property access like “contextName.propertyName” to ensure access from a specific context, or just use “propertyName”, and it will search within the contexts in the order they were registered, utilizing all of the PropertyAccessors that you registered with the context.

Binding enum fields with i8n localization support

29 Mar

It’s easy to argue that enums are a good thing.

Typically, when developing an application, you’re bound to run into a collectible field that has a finite number of selections. Chances are you’re already maintaining a legacy application, or developing for a legacy data model that is chock-full of this type of field; characterized by a cryptic and garbled mess of char(1) values that look like FAVORITE_ICECREAM: 1=”chocolate”, b=”mint chip”, Z=”strawberry”.

But you’re not faced with only the problem of selection and storage, but also comparison: and not just in java code, but likely in the view layer (for example when conditionally rendering portions of a form or view). This is where enums shine: your fellow developers are going to hate you less when they can see “${flavor eq ‘CHOCOLATE’}” rather than “${flavor.charValue == 28}”.

Yet, enums are not without their own headaches. Particularly when trying to find a clean way to expose them to JSP, and bind an enumerated selection to a field with spring. It’s not for lack of trying: spring does have some basic support for binding enum with spring:options. However, the missing piece that throws a wrench in the proverbial gears for many is the lack of support for i8n localization. This is a feature which has been noticeably pushed back from version-to-version for quite a while. That’s not to say there haven’t been some interesting workarounds. But, what I’ve seen so far has been less than clean in implementation.

We’ll look for cleaner solution by sticking to a couple principles. First, our problem is in the view layer, thus our solution should be in the view layer: we don’t want a mess in our application layer to correct our form binding and message display. Second, Spring already provides us with some useful form binding tags. The form:select and form:option tags work fine, and I generally hate form:bind, so we’ll reuse these.

Luckily we can adhere to our zealotry by implementing a pretty simple .tag file. Our goal is very close to a standard spring tag; something that looks like this: <customTags:enumSelector path=”myEnumField”/>
Ideally, this tag should generate a drop down of message-file-translated options, in the order they appear in the enum. When one is selected and the form is submitted, spring should bind the selected enum value into the myEnumField field.

Anticlimactically, here’s the .tag that accomplishes this:

<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://www.springframework.org/tags/form" prefix="form"%>
<%@ taglib uri="http://www.springframework.org/tags" prefix="spring"%>
<%@ attribute name="path" type="java.lang.Object" rtexprvalue="true" required="true" %>

<!-- Identify the enumeration class identified by the subBean path.
Do this using a BeanWrapper, in case the field containing the enum value is null. -->
expression="'${nestedPath}'.substring(0, '${nestedPath}'.length() - 1)"
<spring:eval expression="${basePath}" var="baseBean"/>
<spring:eval expression="new org.springframework.beans.BeanWrapperImpl(baseBean)"
<spring:eval expression="beanWrapper.getPropertyType('${path}')" var="pathClass"/>
<!-- Create a select option for each of the enum constants. -->
<form:select path="${path}">
<c:forEach items="${pathClass.enumConstants}" var="enumConst">
<!-- Value of option is the enum constant name, which spring knows
how to bind using enum.valueOf(). -->
<form:option value="${enumConst}">
<!-- Label is pulled from propfile as simpleName.constant,
e.g Color.RED -->
<spring:message code="${pathClass.simpleName}.${enumConst}"/>

There’s not a lot of magic going on here, so to break it down:

We pass in one attribute: path. It works just like a spring path.
Path on what bean, though? Well, our model object is chilling out there, and luckily spring form has exposed the name of the form model attribute into the attribute named “nestedPath”. We can take a hint from the existing spring form tags and unwrap that variable. The spring:eval tag is especially helpful for this, since it lets us dereference EL and then use that inside of another EL statement, as well as do some substring manipulation to get rid of the unruly trailing period.

Next we need to introspect the enum values from the path. This is trivial once we have the class of the field, because Class.getEnumConstants() will happily give us the whole enumeration. But we have to get the class first…
The catch is that we can’t just do like <spring:eval expression=”${path}.class”/> because the value at the end of path could be null, and checking the class wouldn’t be a null safe operation. But, spring’s beanWrapper is very handy here. We can utilize it to get the property class of the potentially nested field described by “path” with no headaches.

From there, it’s a simple <c:forEach> to write out form:option tags, with some spring:message code format of your choosing, and a bind value of the enum constant’s name, which spring already has a converter for.

Still here? What, you want a mavenized example project?
Sure, that’s right here: http://svn.springjutsu.org/examples/enum-binding-example/