You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@struts.apache.org by Apache Wiki <wi...@apache.org> on 2005/05/07 08:26:21 UTC

[Struts Wiki] Update of "StrutsCatalogInputOutputSeparation" by MichaelJouravlev

Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Struts Wiki" for change notification.

The following page has been changed by MichaelJouravlev:
http://wiki.apache.org/struts/StrutsCatalogInputOutputSeparation

New page:
This information is covers almost the same topic, as StrutsMultipleActionForms, but I made it a separate page for clarity. The problem we are trying to solve is how to process input and output data, having only one form bean per action class.

== Traditional Struts request/response cycle ==

First, let us recall the traditional way of processing input and generating output in Struts application.

HTML FORM is submitted from the input page, usually using POST request method. Struts populates form bean (marked with [F]) with request data. Then form bean validates input and if something wrong, it generates error messages. If validate() returns errors, Struts does not bother to call action class. Instead, it forwards to location, which is defined in "input" property of <action> element. If, on the other hand, input data is correct, Struts calls execute() method of the action class. It usually performs model update, then fills out form bean with output values, and forwards to JSP page, which displays output data.

attachment:action_combo_01.gif

What is wrong with this scheme? Number of things.
   * Form bean is used both for input and for output. If input and output page uses same fields, this is OK, but usually this is not the case. So, one form has to combine input and output fields.
   * On error action class is not called. This may be useful in some cases, but not always.
   * On error control is forwarded to location, defined in "input" property. This property itself is a source of misunderstanding for Struts newbies, and makes clean request->processing->response sequence a little fuzzy.
   * By default, "input" property allows only forwarding, but not redirection. Redirection can be enabled, but for the whole application. 
   * Because in case of error most applications forward to input page instead of redirecting, page is sent back to broswser immediately in response to POST request. This produces POSTDATA effect on reload, which results in double sumbit.

So, as a first step of cleaning up the input/output mess I would suggest getting rid of "input" property and automatic validation. This makes request processing more clear and linear, and allows to have several error pages instead of only one. Also, now you are free to redirect to error page instead of just forwarding to it.

attachment:action_combo_02.gif

Next step is to separate input and output data. This is not an easy task considering that a form bean should handle both. First, let us consider two exotic choices. First, is to use form bean for input only. Output data will be generated manually and pushed into request object field by field on in a non-Struts bean. 

attachment:action_combo_03.gif

Another choice is to use form bean for output only. In this case action class processes input data directly from request object and fills out form bean with output data. Oh, right, form bean is called first by Struts. This is not a problem, because Struts uses getters and setters to access form bean. So, if you define fields with package scope, and set them directly, you would not need setter. Thus, Struts would not be able to set their values during populate phase.

attachment:action_combo_04.gif

But these approaches are quite exotic. What if we just have two actions? 

== Two actions and two forms ==

In this case we can assign a very own form bean for each action class. We would have one input form bean coupled with input action, and one output form bean coupled with output action.

attachment:action_combo_05.gif

Now, this approach can work. In this sheme each action and form bean performs its own specific task. 
   * Input form bean is populated with input data.
   * Input action class explicitly calls validate() and updates the domain model.
   * Then input action class forwards to output action. This is basically a simple action chaining.
   * Struts would want to populate output form bean, but here is the trick: you do not need to define setters for properties in the output form. Also, the field on the output form are usually different from fields on the input form.
   * Output form does not define validate() method because there is nothing to validate.
   * Struts calls output action class, which fills out output form bean with data.

Now we have clean input/output separation and each class is doing a small but specific task. This scheme is more maintainable.

But, even with this cleaner model we still have POSTDATA and double submit problem, because all processing is performed during a single request/response cycle. From browser's point of view, server receives POST request and responds with result page. When a user wants to refresh the result page, browser has to resend its address, that is, the POST request. This produces POSTDATA situation.

The solution is quite simple: to chain actions using redirect instead of forward.

attachment:action_combo_07.gif

In this case, after input data is processed and domain model is updated, input action redirects to output action. What is special about redirection?
   * The request has to make a roundrip through browser.
   * The request has GET type
   * No data from previous request is included in redirected request.
Thus, from server's point of view, it receives completely separate request for output action. Alas, because there is no input data in the request, we do not know what to display. Usually, we do not need much, just a primary key or object ID. We can pass this ID through the session, or we can append it to the redirected request as query parameter. When redirected request comes to the server, Struts would pull object ID out of the request, and load business object from database. Then it would fill output form bean and voila!

Redirection provides freedom of browsing. Now we can reload result page without risking to re-submit input data. And we can go back and forward again without invoking POST requests.

What if we need to use one form? Say, we have a wizard or a dialog, which shares data. No problem, this even easier than to have two forms. Just define the form with session scope, and it will retain all input data between requests.

attachment:action_combo_08.gif

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@struts.apache.org
For additional commands, e-mail: dev-help@struts.apache.org