Data entry form is the common use case in an interactive application. Application displayes a data entry form and collects user input. If entered data is correct, application accepts is and displays a success message. Otherwise, application redisplays the data entry form along with relevant errors, the user fixes the errors and submits the form again.
In this use case a user is allowed to fill out the form in several attempts, while the form preserves data entered by the user on previous attempts.
Struts framework is built around the Front Controller pattern. This pattern gives no guidelines on how consecutive requests should interact with each other and with business objects, neither does it specify how the application should manage the view state and the state of the business object.
Left to their own devices and constrained with framework limitations, web developers devised the pattern for building interactive forms with Struts. For the lack of a better approach this pattern became a standard practice in Struts community.
An interactive web application operates in two phases. On render phase (or output phase) browser requests a web resource to render itself. On accept phase (or submit phase) browser sends input data to a web resource, usually by submitting an HTML form.
Struts implements this two-phase request/response approach utilizing setup/submit pattern and two types of actions:
Setup/submit action pattern is a standard practice for building interactive application with Struts.
It is common to have several actions of either type for a web resource. For example, if you deal with
Customer web resource, you are likely to define two setup actions:
editCustomer.do and three submit actions:
Setup action loads data from database and queues it into one or more arbitrary objects located in the request or session scope. Submit action processes input data and redisplays the same data entry form if errors has been found in the input. If input does not contain errors, submit action forwards to a success page.
This approach is not perfect:
struts-config.xmlfile as well as with several Java classes.
The remainder of this page shows the ways to improve classic Setup/Submit pattern.
ActionForm was originally designed to collect request data. Despite of that, online poll shows that about 60% of Struts users employ ActionForm as the holder of output data as well. This makes a lot of sense:
Therefore, instead of queueing output data to arbitrary objects in request or session scope, a setup action fills out an ActionForm as the holder of input/output data. Mappings of both setup action and submit action would refer to the same ActionForm in their "name" attribute.
Shared ActionForm makes it easy to preserve incremental changes made by a user in a data entry form and ensures, that all data related to a particular web resourse, is neatly stored in one place.
A submit action should not forward to a success page belonging to another logical web resource. Instead, it should forward (or even better, redirect) to a setup action of the success page. This allows to break a convoluted M:M relationship between actions and pages down to simple and observable 1:M relationship. That is, several pages should correspond to one web resource, not the opposite!
Another benefit of transferring to an action instead of a page is that you don't need to care about what will be displayed on success page. You do not have to select a proper page and you do not need to setup output data for that page. This is the business of the web resource you are transferring to, this is what its setup action should do.
With this simple change an application can be broken into separate independent chunks. There is no need to build inflexible "flow" from one page to another. Proper page is selected and displayed by its respective setup action.
Turn autovalidation off and perform validation manually. This ensures your full control over input data and over the workflow. With autovalidation turned off your action class will always be called, so you can make a better decision what to do in case of error, whether you want to redisplay the same data entry form, shoud you transfer the control to another web resource or maybe you need to modify your business object instead.
Autovalidation is used together with "input" attribute of action mapping in
struts-config.xml file. If
ActionForm.validate returns non-empty error object during autovalidation, Struts forwards to location defined in the "input" attribute. Usually, it is the same data entry form that was just submitted. Therefore, the same form can be represented in the browser with two different URLs: one URL when it is rendered by a setup action, and another URL when it is redisplayed by a submit action. In most cases browser is forwarded to the page, not redirected, so an attempt to refresh a page after it has been redisplayed causes double submit.
These are techniques worth considering when you need to redisplay data entry form:
These techniques (except the first one) can be commonly called as action reloading, because a page is redisplayed using the same setup action that was used for for initial page display. Using redirection instead of forwarding protects from implicit double submit and hides the submit URL from the browser and from the user. This way a page is visible and accessible only with setup (render) URL.
ActionForm should be populated only on submit phase. To protect ActionForm from unintended modification by setup action, do not set "name" attribute in setup action mapping. This will prevent Struts from instantiating and populating the ActionForm. To setup ActionForm data you need to instantiate it yourself and to put in into appropriate scope, either into request or into session. You will also have to refer to the ActionForm explicitly using "name" attribute of your input elements.
A simple web resource like
Customer can have couple of setup action mappings like
editCustomer.do and several submit action mappings like
deleteCustomer.do. Of course, this does not mean that you need to define five corresponding Java classes. It would be great if you could reduce number of action classes down to two: one setup action and one submit action per web resource. It would be also great to reduce number of action mappings in struts-config.xml file.
First, let us deal with setup actions. You can combine two aforementioned mappings into one setupCustomer.do and to differentiate between edit and view modes using a request parameter, like setupCustomer.do?mode=edit . Whether to use this approach or not depends on your idea of "clean URLs" and other factors.
Dealing with submit actions is a more straightforward process, just use one of DispatchAction flavors. Struts core library includes several dispatcher actions: DispatchAction, LookupDispatchAction an MappingDispatchAction. Despite of this variety, all these actions have their deficiences:
Therefore I recommend using EventActionDispatcher to dispatch events to any arbitrary action class.
EventActionDispatcher mentioned in the previous section, is pretty smart. It can detect the logical type of request that it receives: is it a render request or submit request. It does this by looking for event in the request.
If EventActionDispatcher finds event in the request, it calls the corresponding method handler. This is a submit phase. If dispatcher cannot find event in the request, it calls default method. This request is supposed to belong to render phase, so you can prepare the ActionForm and render a page.
As you can see from the picture, the whole web resource is now controlled with only one action class and has only one URL. Neat! A web resource can have several JSP pages corresponding to it, so you can choose a proper one depending on the incoming event and on the state of the resource. For example, if you create a login component, you may decide to have two pages: Login and Logout, and you can display either one depending on user login status.
Remember, the primary object in a web application is a web resource, not a page. A page is just a view; one web resource can have several views. Do not build application around pages, build it around web resources. In Struts, web resources are represented with Action and ActionForm classes.