Tuesday, June 05, 2007

Rant: JSP + OGNL + Collections == Train Wreck

The Story So Far

In my day job I'm using Struts 2, with JSP as the view templating mechanism. I have a collection whose size I'd like to report on the page. Unfortunately for my sanity, I have recent experience with Ruby on Rails, in which such a thing is as simple as:

<%= @myCollection.size %>

But no.

Problem #1

Struts 2 exposes properties on the Action class via OGNL, which has a nice, clean, property-based syntax, much like RHTML (which is what Rails uses for its view templates). So I should be able to ask for something like this:

${myCollection.size}

Assuming, that is, that I have a method getMyCollection() defined on my action. Which I do.

The problem here is that I also have to have a method called getSize() defined on whatever getMyCollection() returns. Which is a java.util.Set. Which, for some reason, does not have a getSize() method. It has a size() method instead. Apparently the designers of the Java 2 Collections API were feeling a mite saucy when they went a-designin', and were daring their overlords to punish them for ignoring the Java Beans method naming convention that, as it turns out, OGNL relies on heavily.

D'oh!

No problem, though. OGNL doesn't require method names to be bean-compliant, it just prefers it in its chain of figuring out what the heck you're asking for. You can invoke any method directly, as in:

${myCollection.size()}

Problem solved! Let's save everything and reload:

Struts Problem Report
Struts has detected an unhandled exception:
Messages:
view.jsp(40,109) The function size must be used with a prefix when a default namespace is not specified
org.apache.jasper.JasperException: view.jsp(40,109) The function size must be used with a prefix when a default namespace is not specified


What the...?! Oh. The JasperException must mean that the ${...} expression is being interpreted as JSP EL instead of OGNL. Bummer. Oh well. I'll just let the EL engine handle it.

Which brings me to:

Problem #2

The JSP EL engine can't handle it. Neither the property syntax nor the method syntax works. The property syntax wants to use the (non-existent) getSize() method too (go figure). And the method syntax doesn't exist.

So. Since it looks like JSP EL trumps OGNL when the page is rendered, I'll just turn off JSP EL evaluation (in web.xml) and let OGNL handle everything.

Or not. That results in a ton of errors from pages in my app that rely on JSP EL.

Solution

I'll try to wrap this up. It turns out that the answer is JSP functions. "All" I have to do to get the number of items in my collection is write a public function class and implement a static method -- which I get to name anything I want! Just like the Collections API designers! -- that takes the collection as a parameter and returns its size.

Oh, and I have to write a snippet of XML in the form of a .tld file that tells the JSP where to find this function.

Oh, and I have to declare the .tld as a taglib at the top of the JSP. And that's all I have to do.

Sheesh.

Conclusion

Fortunately, it turns out that the hardest part of this work has been done for me, in the form of the JSTL implementation (documented here) of several useful JSP functions, including length().

It doesn't help my mood any that in Ruby -- if the problem existed to begin with, which it doesn't -- I could have solved it simply by adding a getSize() method, aliased to the size() method of the Set class, and everyone would be happy.

Grumble.