Controlling duplicate form submissions


In chapter 4, you looked at how duplicate form submission can be handled effectively at individual form level. Here is a recap.

  • The isTokenValid() is invoked in the execute() method (or one its derivatives).

  • If the page is the last in a multi-page form, the token is reset.

  • After processing, the user is forwarded or redirected to the next page.

  • If the next page thus shown also has a form with sensitive submission, the saveToken() is called to set the token in session just before forwarding to the page.

Page after page, the logic remains the same as above with two blanks to be filled. They are:

  1. Should the execute() (or any equivalent method in Action) method check if the submission was valid for the current page? (through the isTokenValid() method)?

  2. Does the page rendered after processing (in execute() or any equivalent method in Action) has sensitive submissions needs?

Two approaches emerge to fill in the blanks. The first is to use Template Method pattern and encapsulate the logic of handling sensitive resubmissions in the base class and delegate the task of filling the two blanks to the child classes by declaring two abstract methods in the base Action. While this sounds as the logical thing to do, there is an even better way. You got it ‚ customizing Struts.

For a moment consider what would the two methods do if you chose the former option? The first method would simply provide a boolean value (without any logic) indicating whether the page should handle duplicate submission or not. The second method would decide (a simple if logic) whether the next view rendered needs the token in session. This information is best provided as configuration information and that is exactly what the forthcoming customization does.

Listing 10.6 shows all the customizations needed to achieve what is needed. The application flow that is used is the same flow as before: CustomerForm is a single page form. On submit, a success page is shown. On Cancel, cancel.jsp is shown. However the only twist is that success.jsp is treated as a JSP with a form that needs to avoid duplicate submission (For simplicity purposes, we are not showing the redirect=true setting).

Listing 10.6: struts-config.xml for duplicate form submission handling
 <action-mappings  type="mybank.struts.MyActionMapping"  >   <action   path="/submitCustomerForm"               type="mybank.app1.CustomerAction"               name="CustomerForm"               scope="request"               validate="true"               input="CustomerDetails.jsp">   <  set-property property="validateToken" value="true" /  >   <  set-property property="resetToken" value="true" /  >   <  forward name="success"   className="mybank.struts.MyActionForward"   path="success.jsp"  >   <  set-property property="setToken" value="true" /  >  /forward  >      <forward name="success"  path="cancel.jsp"  />   </action>   </action-mappings> 
 

The action mapping in listing 10.6 provides all the information needed for sensitive resubmission logic to be retrieved in a generic manner in the base Action class. Before looking at the actual code in MybankBaseAction , let us look at what Listing 10.6 conveys. It has two new < set-property > elements. The first setting, validateToken is used to determine if token validation is necessary on entering the execute() . The second setting, resetToken is useful for the multi-page form scenario when the token has to be reset only on the final page (See chapter 4 for more information). These two settings fill in the first blank.

Next, there is a new kind of ActionForward called mybank.struts.MyActionForward . This is an example of extending the ActionForward class to add custom settings. The < forward > itself now contains a < set-property > for a JavaBeans property called setToken on MyActionForward . This setting fills the second blank.

Now, let us look at the actual code that handles form submission. This code goes into the base Action class and is shown in Listing 10.7. The new code is shown in bold. In addition, the listing includes all the useful code discussed so far that should make into the base Action (except page flow Controller). You can use this Listing as the template base Action for real world applications.

Listing 10.7: The complete base Action class
 public class MybankBaseAction extends Action {   public ActionForward execute(ActionMapping mapping,              ActionForm form, HttpServletRequest request,             HttpServletResponse response) throws Exception   {     // Add centralized logging here (Entry point audit)             // Check here if the user has rights to this application           // or retrieve app specific profile for the user           ActionForward forward = null;      MybankBaseForm myForm = (MybankBaseForm) form;           // Set common MybankBaseForm variables using request &           // session attributes for type-safe access in subclasses.           // For e.g. myForm.setUserProfile(//                 request.getAttribute("profile"));           MyActionMapping myMapping = (MyActionMapping) mapping;     String selectedButton =                 getSelectedButton(myForm, myMapping);     boolean validationReqd =                   myMapping.isValidationRequired(buttons[i]);     if (validationReqd) {       ActionErrors errors = myForm.validate(myMapping, request);       if (errors != null && ! errors.isEmpty()) {         saveErrors(request, errors);         return myMapping.getInput();       }     } //if there are errors through form validation,        //return immediately     //Check if token is valid, but dont reset token  boolean tokenIsValid = true;   if (myMapping.getValidateToken()) { // validate token   tokenIsValid = isTokenValid(request);   }   if (tokenIsValid) {  preprocess(myMapping, myForm, request, response);             forward = process(myMapping, myForm, request, response);             postprocess(myMapping, myForm, request, response);  }   else { //duplicate submission   //Adopt a strategy to handle duplicate submissions   //This is up to you and unique to your business   }   if (forward.getClass().equals(   mybank.struts.MyActionForward.class) {   MyActionForward myForward = (MyActionForward) forward;   /* Reset the token if there are no errors and   resetToken atteibute in ActionMapping is true   Note that in multipage scenarios, resetToken is   false in the ActionMapping   */   if (!hasErrors(request)  &&  myMapping.getResetToken())   {   resetToken(request);   }     /* If there are no errors and next page requires   a new token, set it   The setToken is false in the ActionForwards   for that ActionMapping. Hence a multipage   form flow has a single token  a unique identifier   for the business transaction   */   if(myForward.getSetToken()  &&  !hasErrors(request)) {   // next page is a form with sensitive resubmission   saveToken(request);   }   }  // Add centralized logging here (Exit point audit)             return forward;   }  } 
 

The getValidateToken() method retrieves the validateToken (< set-property >) from MyActionMapping . This setting tells the framework whether to check for sensitive resubmissions on the current page. After the check is done, duplicate form submissions need to be handled as prescribed by your business. For regular submissions, retrieve the ActionForward for the next page. If the next page happens to be one of those requiring a token in the Http Session, saveToken() is invoked and then the ActionForward is returned.




Struts Survival Guide. Basics to Best Practices
Struts Survival Guide: Basics to Best Practices (J2ee Survival Series)
ISBN: 0974848808
EAN: 2147483647
Year: 2004
Pages: 96

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net