A long time ago (or so almost everything seems nowadays), I worked with Webwork 1 for a while. One thing I liked about it was its ActionSupport class, where you could set it up to accept certain request parameters by setting up the setters in this class, and specifying what to do with the data in its execute() or doDefault() method. Something like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public class FooAction extends ActionSupport {
private String bar;
public void setBar(String bar) { this.bar = bar; }
public String getBar() { return bar; }
private String baz;
public void setBaz(String baz) { this.baz = baz; }
public String getBaz() { return baz; }
public void doDefault() {
// ... do something with bar and baz, these are automatically
// available to the view through the OGNL stack
return SUCCESS;
}
}
|
I like the way that one can "set" in the code what parameters are going to be used. With Spring Controllers, so far, I had been using something like this:
1 2 3 4 5 6 7 8 9 10 | public class FooController implements Controller {
public ModelAndView handle(HttpServletRequest request, HttpServletResponse response) {
String bar = ServletRequestUtils.getStringParameter(request, "bar");
String baz = ServletRequestUtils.getStringParameter(request, "baz");
// ... do something with bar and baz
ModelAndView mav = new ModelAndView();
// ... populate the ModelAndView
return mav;
}
}
|
What I like about the above approach is its relative simplicity. However, the problem with this approach is that if you have multiple controllers which work off the same base set of parameters (which is true in most applications), the code is repeated (or moved out to a helper method). Also, if there is going to be significant computation based on the values of multiple parameters, then this logic will probably have to be repeated or put in a helper method as well. Putting the code into a helper method is preferable for maintenance but makes the code less readable.
In my case, I intended to use request parameters to define a very lightweight query language. A combination of certain values would trigger off certain actions and so on. Multiple controllers would have to work with the same basic set of request parameters. Also, the parameters in my case would have more numerous and have more interdependencies, since the parameters were meant to be set by human users rather than a computer program. So clearly, the Webwork style approach of being able to declaratively set parameters would be preferable to using Spring's ServletRequestUtils.getRequestParameter() calls.
One seemingly obvious way would have been to have setters in the Spring Controller implementation, and then use reflection to access them in the handle() method. However, as obvious as this sounds, this is a bad idea; since that the controller itself is a singleton by default, this is not thread-safe.
Looking a little further, however, I found Spring's AbstractCommandController, which provides pretty much the functionality I am looking for. To keep things thread-safe, AbstractCommandController exposes a setCommandClass() method, which allows you to set a bean class. Your controller class, which extends AbstractCommandController, will instantiate the bean once per request, and bind the request variables (parameters and attributes at least, AFAIK) to the instantiated bean using its exposed setters. In code, thus, for the above example, this is what our bean would look like:
1 2 3 4 5 6 7 8 9 | public class Parameter {
private String bar;
public void setBar(String bar) { this.bar = bar; }
public String getBar() { return bar; }
private String baz;
public void setBaz(String baz) { this.baz = baz; }
public String getBaz() { return baz; }
}
|
Our controller class will look something like that shown below, and uses a different signature for its handle method as shown below:
1 2 3 4 5 6 7 8 9 10 | public class FooController extends AbstractCommandController {
protected ModelAndView handle(HttpServletRequest request, HttpServletResponse response,
Object command, BindException errors) throws Exception {
Parameter param = (Parameter) command;
// ... request parameters are all available in param, do something with them
ModelAndView mav = new ModelAndView();
// ... populate the ModelAndView and return it
return mav;
}
}
|
The controller will be configured with its command object in configuration like so:
1 2 3 4 | <bean id="fooController" class="com.mycompany.myapp.controllers.FooController">
<property name="commandClass" value="com.mycompany.myapp.beans.Parameter"/>
...
</bean>
|
So this way we get the best of both worlds. In my case, there were situations where I would let the client use a set of input parameters and based on that, derive some other parameter, or alternatively let the client specify this derived parameter directly. In these situations, the controller would call a computeXXX() method in the Parameter class, rather than put logic to handle these cases in the getters for the parameters. I thought I'd share this, since my first approach was to modify the getters, but then I ran into a race condition with Spring first calling the getters inside its framework code. So I figured that its best to have default functionality on the getters and setters. That way, Spring, which assumes default semantics, can use it safely, and so can other application code. If application code needs values other than the default semantics, it can call the appropriate computeXXX() method instead.
This is probably old hat to a lot of you Spring MVC folks out there, but I had never needed this particular functionality so far, so just implementing Controller and grabbing the parameters with ServletRequestUtils worked fine for me. This was one case where this approach would have gotten too complicated, and happily Spring had something that worked out better.
Hot on the heels of my successful use of AbstractCommandController, and in the general spirit of learning new things, I have also been looking at the SimpleFormController for another part of the same application. SimpleFormController seems to be quite powerful but is anything but simple. However, its pretty easy to use once you know how...
By the way, if you would like to know more about Webwork 1, check out this nice tutorial on Joe Ottinger's blog, which by the way is an interesting read in itself.