The Source for Java Technology Collaboration
User: Password:



Jacob Hookom's Blog

November 2006 Archives


Extending EL Syntax

Posted by jhook on November 27, 2006 at 02:08 AM | Permalink | Comments (6)

I've been working a bit on extending EL, but found that the API is missing a few features desired to fully make the language pluggable by third parties.

First is method invocations. Caching reflected data is so difficult to do without causing some kind of memory leak. I really wish JSE had some kind of ClassLoader listener API as a safer alternative to statically scoped data in web containers. Maybe others have more experience in this area? Anyways, performance of method invocation is pretty fast, it takes the same time to do a.b as to do a.getB()-- averaging 0.0016 ms on my laptop for both.

To be safe, I did add some logic for finding the best match if there are multiple methods with the same name. Tis better than nothing :-)

The more important addition is projections: a.{x|x.b} which means iterate over the Collection (a), for each, get (b).

Projections are useful in the presentation tier for binding sets of properties instead of requiring two parts: items="#{a}" var="v", value="#{v.b}". Actually, it may be more performant with the equivalent projection if you were to do: items="#{a.{x|x.b}}" var="v", value="#{v}". Anyways, there's a subtle difference in my implementation of projections as opposed to others.

Here's our model: A company has many departments, which have many employees. If I wanted to generate a list of employee names, I would do something like this:

#{company.departments.{x|x.employees}.{x|x.name}}

The OGNL equivalent is a little lighter in syntax:

company.departments.{employees.{name}}

..but produces a List of a List of Strings:

[['Bob','John','Sally'],['Jason','Marge','Kelly']]

That's not what we wanted! With the EL implementation, you instead get:

['Bob','John','Sally','Jason','Marge','Kelly']

Which seems more correct to me. The main reason is that EL is supposed to represent pointers to instances, so if I take the pointer (ValueExpression) and call the setValue on it-- I would expect that my target (each employee's name) would be set. So why would I return a List of Lists then?

Another cool feature of projections under the EL-API is the use of MethodExpressions. MethodExpressions work perfectly for hooking into Framework APIs as JSF has with generic actions, validators, etc. So lets say your backing model had a collection of Validators, but you didn't want to express each one separately?

<h:inputText value="#{bean.name}" validator="#{bean.validators.{x|x.validate}}"/>

What I'd really like to see added to the EL-API though is pluggable type conversion and method invocation. Right now you can plug in your own property resolution, but the strange thing is that if you take the expression #{a.b} as a value, you can customize the resolution of (b), but if you take it as a method, then you cannot customize the resolution of (b), so the method must always exist on that instance in accordance with the EL implementation's rules.

To correct these two missing pieces, I would like to see two methods added to the ELResolver API which allow near everything to be customized with:

public Object convertType(ELContext ctx, Object in, Class type)

public Object invoke(ELContext ctx, Object base, Object method, Object[] params)

public MethodInfo getMethodInfo(ELContext ctx, Object base, Object method, Object[] params)

The execution of these methods would work the same as the other ELResolver methods, requiring the propertyResolved to be set to 'true'. It'd be great then to allow customized extensions of Iterable/Collections/Maps such that one could do:

#{company.departments.asc{x|x.employees.{x|x.name}}}

Which would call into the ELResolver chain with the following:

public Object invoke(ctx, Collection, "asc", new Object[] { closure })

This would allow you to treat closures as value-types for some pretty extreme customization with mixing your own Java code into the execution of 'inlined' EL without requiring full API changes within the JSE.

Basically, these are just some future ideas for EL, whenever it becomes it's own JSR. With Facelets and custom EL-API implementations, such as this one, you can just set one property on the compiler and away you go with the added features.

(I have to start releasing stuff again)



Trying out NetBeans 5.5

Posted by jhook on November 26, 2006 at 02:49 PM | Permalink | Comments (11)

I use Eclipse both at home and at work, but finally decided to give NetBeans a spin. Overall, the IDE is solid, but some of the features seem too 'literal'. For lack of a better word, let me use an example instead. When I'm writing test cases, I'll constantly comment and uncomment lines. Instead of providing one comment toggle control, NetBeans has two different ones:

  • ctrl+shift+T to comment
  • ctrl+shift+D to uncomment

Two things: does T and D have anything to do with the noun 'comment', and why isn't the IDE smart enough to do this with one command? Consequently, running the comment command multiple times will keep on adding more '//' to the beginning of your lines. Great.

Hotkeys aside, I'll always try to right click to find associated features. This is fairly confusing in NetBeans for common tasks such as implementing abstract methods or generating bean accessors. When you are writing APIs and deal with inner classes, this only adds additional confusion with the IDE.

Right-clicking on a Class, under the Refactor sub-menu, you are presented with a list of options for all Java types-- including: Extract Method and Change Method Parameter types-- what does that have to do with a Class declaration? Feel free to pick these options, but you will be presented with an error.

If I'm declaring a new class, or add an additional abstract method, why can't I right-click and implement those methods. Yes, the option is on the top menu, under 'Source', but I'm concerned with the context of my cursor in the code-- may it be a method or class file, not 'mousing' away from it to the top menu under 'Source'.

Finally, many of the options around refactoring are always forced into a review dialog before committing the changes. If I want to generate getters and setters, and select the fields I want, why always force me to review in a second dialog? I've gotten caught off guard so many times because that bottom pane has everything loaded into it for output, junit results, etc. Clicking on 'next' in the pop-up dialogs, closes the dialog, then adds yet another tabbed panel to the bottom with the next step to confirm what I just said I wanted to do. It seems unecessary and should be an optional flow-- and even if I wanted confirmation, why close the dialog and put the next step in another panel?

Overall, NetBeans isn't bad. It's just not as intuitive as others I've used. It's still never obvious to me with what classes have errors and which ones don't without hitting the recompile button.



EL Comparisons

Posted by jhook on November 16, 2006 at 12:28 PM | Permalink | Comments (12)

Updated 11/17/2006 with JEXL and upgraded MVEL and added Serialized size

I decided to test the following EL libraries available today:

  • Commons EL (http://jakarta.apache.org/commons/el/)
    Implementation of the JSP EL API that's existed forever. This library can be found in many JSP containers (Tomcat for example) or used as a foundation for within many vendor's J2EE servers.
  • OGNL (http://www.ognl.org/)
    One of the most expressive ELs available today and widely used with WebWork (Struts 2) and Tapestry.
  • MVEL (http://wiki.mvflex.org/index.php?title=MVFLEX_Expression_Language)
    A newcomer to EL which is part of the MVFlex/Valhalla project. Features look more in line with OGNL's offering with method invocation and some interesting regular expression support.
  • EL-API (http://jcp.org/en/jsr/detail?id=245)
    Sun's reference implementation of the new EL-API which is part of JEE 5 and available with Glassfish. Syntax features are basically the same as the EL from JSP, but with additinal API features.
  • JEXL (http://jakarta.apache.org/commons/jexl/)
    An implementation based on Velocity's parser. Because of this, it acts more like a limited templating solution with things like method invocation.

Here's a table of features I thought were pertinent:

Library A.B Accessors Math Method Invocation Projections Regular Expressions Coercion Conversion Pluggable Property Resolution Pluggable Method Resolution Serializable Factory Caching
Commons-EL Yes Yes No (functions) No No Yes No No No No Yes
OGNL Yes Yes Yes Yes No Yes Yes Yes Yes Yes No
MVEL Yes Yes Yes No Yes Yes No No No ? No
EL-RI Yes Yes No (functions) No No Yes No Yes No Yes Yes
JEXL Yes Yes Yes No No Yes No No No No No

Pretty obvious things that stand out are that OGNL and MVEL have a lot more syntax features compared to the standardized EL. I think this is largely impart to the fact that EL was only intended for simple notation within the View counterparts and not as a scripting language. I will say that there's absolutely no reason why these other syntax features shouldn't be added to future releases.

Pertaining to pluggability of syntax evaluation, OGNL is the most expressive with Method, Property, Index, etc. The new EL-API has similar capabilities for Property and Index Accessors with chainable ELResolvers.

*Factory Cached means the API's factory method inherently cache expressions to some degree and do not re-parse on every call.

API Use:

Commons-EL
ExpressionFactory fact = new ExpressionFactoryImpl();
VariableResolver vars = new VariableResolverImpl(..);
Expression expr = fact.parseExpression(str, Object.class, null);
Object value = expr.evaluate(vars);
OGNL
Map vars = new HashMap();
Object expr = Ognl.parseExpression(str);
Object valu = Ognl.getValue(expr, vars);
MVEL
Map vars = new HashMap();
Object value = Interpreter.eval(str, vars);
EL-RI
ExpressionFactory fact = new ExpressionFactoryImpl();
ELContext ctx = new ELContext(...);
ValueExpression ve = fact.createValueExpression(ctx, str);
Object value = ve.getValue(ctx);
JEXL
Expression e = ExpressionFactory.createExpression( jexlExp );
JexlContext jc = JexlHelper.createContext();
Object value = e.evaluate(jc);

Finally, I have some performance numbers at 10,000 iteration, average time in milliseconds:

Library test.fubar != null test.num + 10 - 1
Commons-EL 0.00249 0.00343
OGNL 0.20751 0.23535
MVEL 0.02032 0.03775
EL-RI 0.00714 0.00715
JEXL 0.34199 0.21907
Commons-EL (re-use) 0.00597 0.01462
OGNL (re-use) 0.00342 0.00712
EL-RI (re-use) 0.00394 0.00767
JEXL (re-use) 0.04946 0.01123

*re-use means take the Expression returned from the factory and keep evaluating that instance instead of going back to the factory again. This didn't show much of a difference for those EL libraries that cache within their factories, but it did show a big improvement with OGNL and JEXL.

Serialization can be important for some libraries and the EL-RI was optimized for use with JSF's state-saving architecture. I'm sure other frameworks may have similar concerns. Here are the sizes in bytes for the couple libraries that were actually Serializable.

Library test.fubar != null test.num + 10 - 1
OGNL 594 764
EL-RI 170 169

Synchronization was another thing I wanted to test, but haven't yet. Supposedly OGNL has some unwanted synchronization blocks and JEXL has not been optimized at the Factory level at all-- putting a synchronization block around expression creation, with calls to commons-logging.

In summary, here's just my observations from an afternoon of testing. While I'm not an expert on all of the libraries listed, feel free to comment on where I might have missed a point or gotten things wrong.





Powered by
Movable Type 3.01D
 Feed java.net RSS Feeds