Tuesday, June 29, 2010

Working with Struts 2

So as I mentioned in a previous post, I've had some time to work with a real Struts 2 app. While most of my initial impression stands, some of my feelings about it have changed slightly. I can say there are certainly some features I've warmed up to yet there are others I feel are still in need of improvement.



AJAX

For starters, I had the opportunity to replace the DOJO tags already in use by the application with the JQuery plugin. While the functionality offered by the plugin was pretty cool, it did suffer from a few quirks. The first issue that presented itself was that the plugin always fetches a value from the server when the component loads on the page. Being AJAX, this is done in a separate request from the page itself. I supposed what I would have liked to see was the initial loading of the component from the same action which loads the page. This would be less requests against the server initially if you have a bunch AJAX components. Having said that, I realize this isn't always possible as sometimes the action which returns the AJAX content (be it a div, a select box, or what have you) may need to be different from the action which loads the page.

One of the features of the tags is that you can "chain" invocations by publishing and subscribing to topics. For example, say you have multiple select boxes whose values depend on one another. To do this, you set up your components such that the first select box publishes to a topic that the second subscribes to. When the value changes in the first one, the second will reload as a result of the message on the topic. This is kind of neat, but we run into problems again on initial load. I had one situation in which a page that had three inter-dependent select boxes required SEVEN requests to the server on initial load: one (1) for the page, three (3) to load the initial values, two (2) to refresh the second and third once the first one completed loading, and a one (1) to refresh the third once the second one had completed loading. I managed to fix this by introducing a "deferred loading" patch to the jQuery plugin source - which essentially suppresses the initial load of the component's values for those tags which you specify the deferredLoading attribute. This brought the number of requests from seven down to four which is a bit more sane.

In hindsight, I wish I had come up with a little more general-purpose patch which also addressed the first issue and would have resulted in only one server request on initial load. But this will do for now.

Annotations

I've gotten pretty used to writing annotations to declare Struts configuration. Annotations used in this way really provide contextual configuration. There's no need to declare what class you're working with as you would in a centralized configuration file. You simply declare the namespace, your results, and optionally whether or not the class is an action. (Struts has a multitude of ways to figure out if a class is an action, the annotation is just one way.) The advantage is that you don't have to remember to edit a configuration file.

Now in my opinion one of the prime advantages of using Struts over a model-1 approach was the fact that configuration was central. What I liked about the central config was it gave you a bird's-eye view of the whole configuration, and you could essentially map out a graph of the whole application without delving into individual class files. The price to pay for this was in terms of overhead; the action classes and the config always need to remain in sync with one another. Somewhere down the line this was heralded as an inconvenience and annotations were embraced as the solution. Not to dis annotations as they do play a major role in providing contextual configuration and therefore reducing development time (how would you envision JAXB without any annotations? 'nuff said). But it almost seems to me there should be two views of the configuration: a per-class view for those doing the developing and a centralized view for those who need a high level perspective of the application. This might be role that could be filled by an IDE plugin.

Validators

While I initially glossed over it when I read the book, I had an opportunity to work with the Validator framework. I had never used it before, but I admit this is a pretty powerful feature and saves a lot of coding time. What's nice is that in Struts 2 you can throw your validations into annotations right above the execute function they pertain to, so you don't need to keep a separate validation XML file in sync with your page as you did in Struts 1.

Having said that it seems there are some minor caveats. For one, it works well with simple validations. While you can express conditional validations, you end up repeating yourself quite a bit if several input fields rely on those conditions - and even more if said conditions are complex. There are ways to ease the pain a bit, such as adding convenience getters to the action which match the condition.

Another issue I noticed relates to how Struts has field-level validators and global validators. Field validators are nice since you can show the error associated with the field right next to the field on the page. However you cannot use field validators if your edits are conditional. These must be expressed using global validators. It would be nice if Struts' validation framework gave you the choice of how to categorize (and hence where to display) each type of error.

OGNL & the Value Stack

Working with OGNL and the Value Stack wasn't as confusing as I initially expected, although it wasn't without error. For the most part I wrote my JSP pages using Struts tags and OGNL and was able to avoid the use of EL. In those cases where I did need both I ran into issues where I needed to transfer a value from OGNL to EL and vice-versa, and there are some not-quite-so-obvious ways to do that. At first, you might think the answer would look something like this:

JSP to Struts: <s:set var="strutsVar">${jspVar}</s:set>
Struts to JSP: <c:set var="jspVar"><s:property value="%{#strutsVar}"/></c:set>

While this might work okay in some situations, it would be an incorrect general solution. Both of these are converting their values to strings before setting the variable. So if I had, say, a bean it would set my variable with whatever it's toString() returns. The actual way to do conversions is as follows:

JSP to Struts: <s:property value="%{#attr.jspVar}" />
Struts to JSP: <s:set var="jspVar" value="%{#strutsVar}" />

You can access variables in any EL-accessible scope (page, request, etc) by using the #attr variable in OGNL. Likewise, to access any OGNL variable, use <s:set>. This tag actually does two things: creates a page-scoped JSP variable AND creates an OGNL variable. You would do the same thing to transfer a value from the ValueStack to a JSP variable.

I have to admit that OGNL is fairly powerful. There is a lot more you can do with it than EL - invoke functions, create arrays in-line, and access static properties among other features. It's powerful enough that I think it's somewhat unfortunate that it's not the default language used by JSP. And, I'm starting to wonder where exactly EL and JSP are headed, considering Sun (*ahem* Oracle) seems to be backing JSF/Facelets more so than JSP. But I digress; maybe I'll post more on that topic in another post...

No comments:

Post a Comment