One very nice thing about WebWork and presumably Struts 2.x is the OGNL form data scraping. RoR takes a similar approach. Though I realize many will push back at the idea, I wrote a fairly straightforward way to accomplish putting the form information conveniently in the Action class using XWork's Ognl capabilities. I also realize that, at the moment, this doesn't provide any form validation which means it isn't for every situation.
For complex and dynamic forms, you don't get any sort of ActionForm validation/handling anyway. If a product can have any number of descriptions keyed off a string, there is no good way to scrape that data from your form so you can then validate it - maybe with complicated indirecting using map-backed forms. I feel that the complication you incur outweighs the "gain" of validation you achieve by using them and therefore not worth it. OGNL is a very powerful, expressive way of traversing object graphs and setting data. XWork has some good things going for it here: if an element is null as you traverse it, you can optionally plug in a NullHandler. They also provide a InstatiationNullHandler which has a reflective ObjectFactory to create an instance using the default constructor. The solution consists of an Annotation, a common Action base class (we had one anyway), and 2 or 3 lines of OGNL context/runtime configuration. Though we don't use it, the configuration and stuff can easily done in Spring, Pico or whatever IoC container you like. We have our own configuration object hierarchy because we were oblivious to IoC containers at the time we started and "we are going to refactor that part." The context is just a Map of properties. One, really. The property that says to instantiate null-ness if you'd like. The runtime is simply declaring which objects you want the OGNL processor to handle. We registered our common base Action class, so that all Action classes annotated are OGNL-enabled. My action class is annotated with OgnlForm: @Retention( RetentionPolicy.RUNTIME ) @Target( ElementType.TYPE ) public @interface OgnlForm {} My action class can define: ... private Product product; public get/setProduct()... Then my form can say: <textarea name="product.descriptions['LONG'].text">This is my long description.</textarea> <textarea name="product.descriptions['my favorite'].text">I had to say a few words about this product.</textarea> Our base class implements execute and then calls work() on its subclasses. execute() just says: if( this.getClass().isAnnotationPresent( OgnlForm.class ) ) { OgnlUtil.setProperties( request.getParameterMap(), this, this.webappConfiguration.getOgnlContext( this ) ); } return work(); OgnlForm is our annotation. OgnlUtils takes a map of name/value pairs, the object to populate and an optional context (auto nullness). The names are valid OGNL expressions, the object to populate is this - the action with product locally defined and the webappConfiguration is our IoC configuration garbage accessed from the session, etc... By the time work() is called on my action, I have a product instance locally to my action populated with all the necessary data - in this case at least two descriptions. You can see using a freemarker (or velocity or whatever) template to generate the form based on the contents of the Product, its Skus, descriptions, pricing, inventory, whatever. The InstantiationNullHandler can handle Collections, Arrays, Maps as well as any object with a default (empty) constructor. For Collections and Lists, it uses ArrayList and for Maps it uses HashMap. An alternative is to have input elements that tell me which descriptions I need to go after and iteratively scraping the request myself looking for meaningful data. This is complicated and annoying to me. Another alternative is to call OgnlUtils.setProperties() on your Struts ActionForm. You then have two frameworks manipulating one form and we opted to dodge the complexity. You could also process any value object you want, really. We just decided that for our webapp considerations, the Action is as good as any candidate. Whatever you feel best about. We are considering reworking the Annotation to take a parameter, such that if the Action is annotated with OgnlForm we'd put the action itself in the request, turning it into our value object for display purposes. Again, I realize that there are many who will violenty oppose this (please don't flame), but very respectable frameworks such as WebWork (which IS Struts 2.x), Ruby On Rails (RoR) and others find it appropriate too, so you'll have a large body of very smart men and women to contend with. I will say that having your form data, action, and view value object all rolled into one is very succinct. Struts-config.xml slims down considerably. We are also tossing around the idea that one of the parameters on our OgnlForm would me method or command or something, so that once our work() (execute()) is called, we can switch on the command. We could then map many actions (*.do) to one Action(? extends OurAction). A single action could provide a multitude of related functionality in one class. We spent a couple hours digging through XWork's and OGNL's source code, both written/maintained by OpenSymphony, a VERY good bunch of folks. Integrating wasn't too bad at all. WebWork/XWork is very well layered and factored out so we could use exactly what we wanted/needed. In fact, we aren't using any WebWork anything. It is all XWork - the underbelly of WebWork. For complicated and/or dynamic forms, I really like the WebWork/OGNL approach. It is interesting to evaluate what other frameworks and technologies do as well as why they do them. For us, we can't abandon the investment we've made in Struts, as I'm sure many out there can't. We can, however empower our framework innovateively to maximize our ROI - both on Struts and our developers. If any are interested, we could put together a small patch jar that could be committed or posted or something. If you aren't using java 1.5, I suppose you could do something similar with empty interfaces to achieve the same net result.