WebWork Tip #1 - Handling null properties
Almost a month ago I promised I'd start to document on my blog a tip per day about WebWork. I feel like a sleezy bastard for having failed to such a thing, but I'm finally making up for it. Disclaimer: this won't be completely daily, only because I'll be out of town for New Years, but once I get back it should be just about every day. Hopefully someone can roll this in to a decent set of documents (*hint*hint*)
OK, so today's tip is:
Handling null properties
Suppose you have an object, User, that you want to populate based on various form fields entered from the web. The most common practice is to create a UserFormBean (Struts) or a bunch of String properties in your action (WebWork) to set these values. Recently Matt Raible stirred up a bit of a discussion about using your domain objects (User) as the immediate input for your data.
Not letting this turn in to another one of those discussions, a common desire when writing applications this way is to make form input fields named things like "user.name" and "user.password". By doing this, a call to getUser().setName() is made, and your domain object is automatically populated, letting you no longer have to write the code to go from FormBean->DO yourself.
However, what if you have a more complex object? Suppose you want to do "user.shippingAddress.city". A call to getUser().getShippingAddress().setCity() would be made. Normally this will only work if getShippingAddress() returns a non-null object, meaning that your domain objects must now provide default instances for all their various properties. This just won't do!
So, thanks to my constant perstering of Drew Davidson (Ognl guru), a NullHandler was added to Ognl. This now allows for you to not have to create default properties in effort to avoid the "null problem" described above. Rather, WebWork is now configured to automatically detect a null reference and then create a new instance (only zero-arg constructors need apply) for you. The end result is that your domain objects remain clean and your webapps can still remain blissfully simple.
A word of caution: there is a small bit of overhead here, of course, so don't depend on it all the time. Also, dealing with lists becomes a bit more complex. Imagine a property "user.address[0].city" -- in order for the List property "address" to be properly returning an empty Address object when get(0) is called, more information is needed than is known by just looking at the property and the domain object. This will be addressed in the final version of 2.0, maybe in the form of providing a hidden form field such as "type:user.address:com.acme.Address" (huge security problem here) or in the form of another property file (not _another_ config item). Any suggestions would be very welcome.
And there you have it folks. Tip #1 -- please feel free to request tip topics, pimp alternative technologies (especially ones that are conceptually different, like Tapestry), or explain to me why Fresno Taco Bells don't serve the cheesy gordita crunch.