Sat, 11 Jun 2005
Introducing Huts 1.0'Huts' is mash up of Hibernate and Struts. It consists of a couple of classes that will ease development when using both together. The name was chosen for it's non-existence in the Java space and the dream of a future filled with 'Java the Huts'-references. The ideas for it come from discussions with Gregory Vandenbroucke, Kwinten Tobback and Julien Herfurth on the zen of web development, and why it has to be this hard. It logs using, shrug, commons-logging because Struts and Hibernate do so. A public CVS is not yet available, just the binary and source jars and some javadoc. I am getting it registered at dev.java.net but that seems to be a more difficult process then it used to be...
So let's go over the classes:
HutsFilter
This is a common implementation of the one-Session-per-thread pattern that exists already for a long time and I already talked about over here. In this version it is now safe to run multiple instances of in the same ClassLoader. It has some easy extension points to override its' behaviour when opening and closing Sessions. It has 2 modes of operation: in and out of a servlet container. When instantiating one with the constructor that takes a SessionFactory as a parameter, you can start it to be used in a test or any other POJO-like environment. Of course the basic use is to use it as a Filter in your webapp, where it takes an init-param called 'configuration' and the location of the Hibernate configuration to use as value. When it is 'active' you can call HutsFilter.getSession() and HutsFilter.getSessionFactory() pretty safely thoughout your code. When something out of the ordinary happens, like a DataSource that is closed, an unchecked CreateSessionException is thrown. This class is not bound at all to Struts so it can be used in any webapp context without any problems. It does rely on commons-beanutils though, a behaviour you can avoid by overriding and emptying registerHutsConverters() in a HutsFilter implementation.
HutsConverter
This is a Converter for use with the commons-beanutils environment and needs HutsFilter to be active. When the static method register(SessionFactory) is called, which is done by default the HutsFilter when being used as a Filter, it will register itself as the converter for all of the mapped classes in the given SessionFactory. When asked to convert a String to an object for one of these classes, it will look upon the String as the Stringified identifier of the mapped class and load, and return, the mapped object identified by it, connected to the current Session. This is a long explanation for being able to do:
<html:text property="bridge" value="${myActionForm.bridge.id}"/>
Which will, when submitted, load up the Bridge object identified by the id and set it as a Bridge on the 'bridge'-property of the ActionForm. Note that you have to specify the value of the property in the Struts tag as it will not call this converter when writing out the value of the Bridge, although this functionality is written as well, Struts will just do a toString(). Also note that this should work with any beanutils based web framework, like Spring MVC, but I haven't tried it yet.
The class is a small hack with huge consequences, especially is used in parallel with:
HutsRequestProcessor
This is the most controversial, wink to Kwinten, of the set, and at the same time one of the most powerful. It assumes a development structure where Hibernate mapped classes extend ActionForm and are used as thus in some Struts actions. This custom RequestProcessor, when registered as 'processorClass' in your Struts configuration, will detect these kinds special ActionForms and try to load them in the Hibernate Session, before their properties are being set by Struts. It will listen to a request parameter with the same name as the Hibernate identifier and use its' value to find the appropriate object. It allows you to make CRUD actions very easily: you only have to create the JSP's and a SaveAction that calls HutsFilter.getSession().saveOrUpdate(actionForm), all the rest is done automagically.
An example: imagine you have a Hibernate mapped bean called Bridge that has an identfier called 'id' and a property called 'name'. In the detail-action of this bean you just have to make a JSP like:
...
<html:form action="/bridge/save.do">
<c:id test="${bridge.id != null}">
Id:
And a Struts configuration like:
...
<form-bean name="bridge" type="org.futurestreet.huts.examples.Bridge"/>
...
<action name="bridge" path="/bridge/detail" scope="request" forward="/bridge/detail.jsp"/>
<action name="bridge" path="/bridge/save" validate="true" input="/bridge/detail.do" scope="request" type="org.futurestreet.huts.examples.SaveAction">
<forward name="success" path="/bridge/detail.do"/>
</action>
...
<controller processorClass="org.futurestreet.huts.HutsRequestProcessor"/>
...
When an 'id' parameter is given as a GET parameter, the HutsRequestProcessor will load the matching bean and fill in the name box. When saving, it will do the same and then set the 'name' with the value given in the request, after which, in your own, and very likely generic, SaveAction it will persist the change. Right now it can only be used correctly in actions with request-scope, something I want to fix as soon as possible.
PS: Commentators, especially the anonymous ones, I don't want to get in discussions about dirtying up your precious Hibernate POJO object model with ActionForm extensions. For a lot of projects, you shouldn't care: the coupling is just a couple of characters, 'extends ActionForm', get over it. For the people who are now all red-eyed of rage, I am planning a decoupled version as well...
11:32 PDT | /Java | permanent link