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

------------------------------------------------------------------------------
- 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.
+ The subject of this page is separation of input from output when using Struts 
actions. It has two aspects: 
+    * separating input data from output data
+    * separating input phase from output phase
  
- == Traditional Struts request/response cycle ==
+ This page is relevant to StrutsMultipleActionForms topic, which you may want 
to look at as well.
  
- First, let us recall the traditional way of processing input and generating 
output in Struts application.
+ === Separating input data from output data ===
  
- 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.
+ As StrutsMultipleActionForms states, "the most common use case is when we go 
from one page to another page on the browser, sending the server certain 
request values from the first page and getting from the server certain response 
values on the second page. Most pages have both input data and output data." 
The reasonable choice would be to correlate each web page with an action form. 
In this case "the obvious answer would be to have an Action process data from 
!FirstPageActionForm and setup data for !SecondPageActionForm. This would allow 
us to have a page oriented !ActionForm, which makes sense, without having to 
use page-centric solutions. But, we cannot, because action mapping allows us to 
have only one !ActionForm per Action."
  
- attachment:actioncombo01.gif
+ What are the possible choices for storing and accessing input and output data 
from an action class?
  
+ === Not using action forms ===
- 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.
+ This is the radical choice. Proponents of this choice do not see added value 
of action forms. They can obtain input data directly from the request object, 
and they can display information directly from the business object or another 
bean. They prefer to validate input data by explicitly calling the validator, 
or they have their own validation techniques.
  
- attachment:actioncombo02.gif
+ Another reason for not using action forms is using presentation engine 
different from JSP. Quoting Don Brown: "My primary app uses stxx 
(http://stxx.sf.net) so without taglibs, there isn't much left to !ActionForms. 
 My forms are XML based, so I use commons-validator with custom XML validators, 
and JXPath to populate (treats the form element names as xpath expressions). My 
wizards that use Struts Flow use the same population and validation methods."
  
- 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:noform.gif
  
- attachment:actioncombo03.gif
+ This approach is used by 22% of developers, who participated in the quick 
poll: http://marc.theaimsgroup.com/?l=struts-user&m=111584899632546&w=2 You can 
still vote either right in the mailing list, or on my web page: 
http://www.superinterface.com/projects.htm
  
- 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.
+ === Using action form for input only ===
  
- attachment:actioncombo04.gif
+ Struts populates form bean with input data from the request. After input is 
processed and domain model is updated, output data is generated manually and 
pushed into request or session scope, either field by field or packaged into a 
bean. This approach makes sense if a page is rendered with non-JSP engine, too.
  
- But these approaches are quite exotic. What if we just have two actions? 
+ attachment:forminput.gif
  
- == Two actions and two forms ==
+ Used by 11% of respondents.
  
- 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.
+ === Using action form for output only ===
  
- attachment:actioncombo05.gif
+ This is quite an exotic choice. To make this work, action form should not 
have any setters, since Struts uses setters to populate action form bean. 
  
+ attachment:formoutput.gif
+ 
+ No one who responded to the poll, uses this approach.
+ 
+ === Using same action form for both input and output data ===
+ 
+ This choice turned out to be quite popular, 50% usage out of all respondents. 
Here is an outline:
+ 
+    * HTML FORM is submitted from the input page, usually using POST request 
method.
+    * Struts populates form bean with request data.
+    * 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.
+    * execute() usually performs model update, then fills out form bean with 
output values, and forwards to JSP page, which displays output data.
+ 
+ attachment:forminputoutput.gif
+ 
+ === Using input form and output form ===
+ 
+ Action mapping does not allow to assign more than one action form to an 
action class declaratively. If you want to use action form for both input and 
output, but do not want to keep the data in the single form, then you can use 
two action classes, each with its own action form.
+ 
+ attachment:twoforms.gif
+ 
- Now, this approach can work. In this sheme each action and form bean performs 
its own specific task. 
+ In this scheme each action class 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.
+    * Input Form bean validates input and if something wrong, it generates 
error messages.
+    * If validate() returns errors, Struts forwards to location, which is 
defined in "input" property of <action> element.
+    * If input data is correct, Struts calls execute() method of the input 
action class.
-    * Then input action class forwards to output action. This is basically a 
simple action chaining.
+    * Input action class forwards to output action mapping. 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.
+ Struts will try 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 usually differ from fields on the input form.
  
- 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.
+ Now we have clean separation of input and output data, and each class is 
doing a small but specific task. About 17% of respondents use separate input 
and output form beans.
  
- The solution is quite simple: to chain actions using redirect instead of 
forward.
+ === Separating input phase from output phase ===
  
- attachment:actioncombo07.gif
+ Separating data may be important for a web developer, but it is invisible for 
a browser. The only thing that browser knows, is that to obtain a certain page 
it has to issue a certain request. Putting it differently: each page has a 
unique location. What does it really mean?
  
+ When a browser submits data, it sends a request to the server. By default, 
when browser submits an HTML form, the request has POST type. Server 
application processes input data, updates domain objects, and responds with 
result page. From browser's point of view, the result page has an address, 
which is a combination of URL and browser input data. To reload the result page 
the browser needs to send to the server exactly the same address. Thus, when a 
user clicks Reload button, browser resends to the server information that has 
been already submitted.
- 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.
+ HTTP specification distinguishes between POST and GET request types. GET 
should be used for requests, which do not produce side effects when repeated. 
POST should be used primarily for changing server state. Because of that, when 
the same POST is resent to the server, browser is obliged to ask a user for 
confirmation. Obviously, if application does not check for duplicate input 
data, the same server activity can be performed twice, say, a user credit card 
can be charged once again.
  
- 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.
+ This situation is known as "postdata situation" or "double submit problem". 
Struts provides a special object, token, to detect that a particular request is 
resubmitted. While this helps to fight with the double submit, what if we can 
avoid double submits altogether?
  
+ To accomplish that, we need to ensure that when a user clicks Reload, browser 
does not send another POST request to the browser. That means that result page 
must have a different address. That, in turn, means that result page cannot be 
generated in response to POST request. Instead, it should be generated in 
response to GET request. This means, that we need to make two requests out of 
one, and the second response must have GET type.
- attachment:actioncombo08.gif
- ==== Follows the example from a working CRUD application. ====
- {{{
- <!--
-   Create Item. Creates new object with random ID, temporarily
-   saves it in the session, attaches item ID to redirected URL
-   and redirects to editing.
-   Input: none
-   validation: none
- -->
- <action path  = "/createItem"
-   type  = "com.superinterface.items.CreateItemAction"
-   validate = "false">
-   <forward name="itemCreated" path="/editItem.do" redirect="true"/>
- </action>
  
+ Combining this information together, we can come up with a logical solution:
+    * Browser submits input data using POST request.
+    * Server application processes input data and updates domain model.
+    * Server redirects to the address of result page. Redirection codes 302 
and 303 produce GET request. 302 code is automatically generated with Java 
!HttpServletResponse.sendRedirect method.
+    * Browser uses new address to load the result page.
- <!--
-   Edit Item. Presents new or existing item for editing.
-   Item is looked up by ID. 
-   Input: item id
-   validation: item must exist in the item list
- -->
- <action path  = "/editItem"
-   type  = "org.apache.struts.actions.ForwardAction"
-   name  = "itemFormOutput"
-   input = "itemError"
-   parameter = "/WEB-INF/items/editItem.jsp">
-   <forward name="itemError" path="/WEB-INF/items/error.jsp"/>
- </action>
  
+ Now, when a user clicks Reload button, browser uses address from GET request 
to reload a page. Thus, no input data is resubmitted to the server. Also, 
because this request has GET type, browser does not show warning dialog window.
- <!--
-   Store Item. Persists item in the storage. If item has "New" status,
-   it is persisted, if item has "Stored" status, it is updated.
-   On success redirects to home page, on error returns to editing.
-   Input: item id, item value
-   validation: input form fields are validated
- -->
- <action path  = "/storeItem"
-   type  = "com.superinterface.items.StoreItemAction"
-   name  = "itemFormInput">
-   <forward name="itemStored" path="/itemList.do" redirect="true"/>
-   <forward name="storeError" path="/editItem.do" redirect="true"/>
- </action>
- }}}
  
- Let's check out the output action first, editItem. Notice, that it does not 
care where it was called from and was it forwarded to or redirected to. All it 
knows, that it recieves object id in the ID property of its form bean. Well, I 
cheated a little, using the same form bean for input and for output in this 
case. 
+ === Remove "input" property and use redirect ===
  
- If item is not found, action forward to error page. If a user reloads error 
page, editItem action would try to locate the item again, which does not change 
server state, but can improve situation if the item is found. On the other 
hand, it would be cleaner to use redirection to error page, so that database 
would not be bothered if error page is reloaded. If item is found, editItem 
shows it.
+ Usually, when form bean is used for input, errors are forwarded to location, 
defined in "input" property. By default, "input" property does not allow 
redirection. Redirection can be enabled, but for the whole application. Because 
most applications forward to input page instead of redirecting, page is sent 
back to browser immediately in response to POST request. This results in 
POSTDATA situation when a user tries to reload input page afterwards.
  
- Updated item is submitted to storeItem action. It is an input action and uses 
form bean to collect browser data. If data is incorrect, errors are generated 
and saved in the session, then control is redirected back to output action, 
editItem, which redisplays the item along with the errors. If data is correct, 
item is stored in the database and control is redirected to the home page. Home 
page can be reloaded, this will not incur item resubmit.
+ So, as a first step of splitting one request into two I suggest either to get 
rid of "input" property and automatic validation, or to set controller 
property, that allows redirection for "input" location.
  
- createItem creates new item. This would be an input action, but it has no 
input parameters. It does not have output data either, since it redirects to 
editItem which is output action for createItem.
+ Then we need to use redirection instead of forwarding between the action 
mappings, and this is it! 
+ 
+ attachment:twoformsredirect.gif
+ 
+ Well, we are almost there, but not quite. Because now we need to find a 
place, where to store intermediate data and error messages between requests. 
This data can either be passed in the redirected request, or saved in the 
session. The choice is yours. I prefer to store most of the data in the 
session, and to pass only object ID as the request parameter. I will discuss 
the details of redirecting from POST to GET on a separate page.
+ 
+ === Dialog Action ===
+ 
+ What if we need to use one form? Say, we have a wizard or a dialog, which 
shares data between pages. No problem, just use one form bean with session 
scope instead of two, and it will retain all input data between requests. You 
can use one action class as well. To avoid endless loop, you need to check the 
request method. It it is POST, then you redirect to the same action mapping 
again. If it is GET, you display the result page.
+ 
+ attachment:twoformsdialog.gif
+ 
+ Separation between input and output phases using redirection provides freedom 
of browsing. Now we can reload result page without risking to resubmit input 
data. And we can go back and forward again without invoking POST requests.
  
  See also: StrutsMultipleActionForms
  
  == Comments ==
  
-  * [http://www.mail-archive.com/user%40struts.apache.org/msg26454.html Thread 
on Struts user list] 
+    * [http://www.mail-archive.com/user%40struts.apache.org/msg26454.html 
Thread on Struts user list] 
+    * This page was heavily updated since after the comments on Struts user 
list
  

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to