September 22, 2014
Hot Topics:
RSS RSS feed Download our iPhone app

Succeeding with Struts: Dynamically Allocated Forms

  • December 5, 2005
  • By James Turner
  • Send Email »
  • More Articles »

So far, so good. You have a display action that creates an array of products, sets the initially null formItems property to the newly submitted array, and a JSP page to render the form. But what happens when the form is submitted? The first thing that the request processor is going to do is to try to take all those formItems[34].productNumber fields and populate them with the parameters from the request. Only, because the form is in request scope, the one you used to render the form has traveled to the bit bucket, and a new one with a nulled out formItems is going to be used to accept the values. This, of course, is not going to work, and will lead to our yold friend, the Null Pointer Exception.

To make this work, you need to look at the incoming request, figure out the number index value for formItems you will have to deal with, and create an array to handle it. As I mentioned, the place to do this is in the reset method of the form, which is called each time before form population from the request (unless the form is in session scope).

Why not put it in the form's instantiator? Two reasons. First, because Struts is allowed to pool form objects. So, the form you get may not be newly instantiated; it may have been allocated from the pool, which means the instatiantor won't be run. Also, the instantiator doesn't take any arguments, so there's no good handle on the request to fetch the parameters from. On the other hand, the reset method does get handed the request as one of the arguments, so it's readily available. Here's your reset method:

/**
 * Pattern to match request parameters
 */
private Pattern itemPattern =
   Pattern.compile("formItems\[(\d+)\].*");

/** 
 * Method reset
 * Dynamically creates the appropriate product array based on the
 * request
 * 
 * @param mapping    The Struts Action mapping
 * @param request    The incoming request
 */
public void reset(ActionMapping mapping,
                  HttpServletRequest request) {

   Enumeration paramNames = request.getParameterNames();
   int maxSize = 0;
   while (paramNames.hasMoreElements())
   {
      String paramName = (String) paramNames.nextElement();
      Matcher itemMatcher = itemPattern.matcher(paramName);
      if (itemMatcher.matches())
      {
         String index = itemMatcher.group(1);
         if (Integer.parseInt(index) > maxSize)
         {
            maxSize = Integer.parseInt(index);
         }
      }
  }
  formItems = new Product[maxSize + 1];
  for (int i = 0; i <= maxSize; i++)
  {
      formItems[i] = new Product();
   }

}

A fairly simple piece of code: The reset method iterates over the parameters in the request looking for ones that match the pattern, which only matches against formItems[nn] parameters. The matcher extracts the index values. You look to see whether the value is greater than then largest value you've seen so far, and if so remember it. After you find the largest index value, you use it to create an array of products and populate the array with newly created Product objects.

By using this reset method, you're assured that you'll always have enough (but no more than you need) of the Product array slots. This is much superior to the "let's just create an array of 100; no one will ever need more than that" approach, which only assures that some day, at 3 AM, when you're vacationing in Mexico, someone will try to enter 101 items.

Finally, your process action can just do the trivial subtotaling:

package com.blackbear.action;

import java.util.Vector;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;

import com.blackbear.forms.ListOfProductsForm;
import com.blackbear.forms.Product;

public class ProcessRandomListOfProductsAction extends Action {

   /** 
    * Subtotal the list of products
    *
    * @param mapping           The Struts Action Mapping
    * @param form              The Struts Form (ListOfProductsForm)
    * @param request           The request
    * @param response          The response
    * @return ActionForward    The resulting page
    */

   public ActionForward execute(
      ActionMapping mapping,
      ActionForm form,
      HttpServletRequest request,
      HttpServletResponse response) {
      ListOfProductsForm listOfProductsForm =
         (ListOfProductsForm) form;

      Vector newProducts = new Vector();
      for (int i = 0;
           i < listOfProductsForm.getFormItems().length; i++)
      {
         Product p = listOfProductsForm.getFormItems()[i];
         if ((p.getProductQuantity() != null) &&
             (p.getProductQuantity().length() > 0))
         {
            int quantity = Integer.parseInt(p.getProductQuantity());
            if (quantity < 1)
            {
               continue;
            }
            float total =
               quantity * Float.parseFloat(p.getProductPrice());
            p.setTotal(String.valueOf(total));
            newProducts.add(p);
         }
      }
      listOfProductsForm.setFormItems((Product[])
         newProducts.toArray(new Product[0]));
      return mapping.findForward("success");
   }

}
Product Number Product Price QuantityTotal
12877 9.66 1 9.66
17443 84.55 2 169.1
1501 57.3 4 229.2
17076 57.97 3 173.91
15176 68.84 4 275.36
13281 66.03 1 66.03

Download

Click here for a jar file with the complete sources to this sample application.

About the Author

James Turner is a Senior Developer at Axis Technology, LLC. He is also a freelance technology journalist who has written two books on Open Source Java development (MySQL and JSP Web Applications and Struts Kick Start, both from SAMS) and currently serves as Products Editor for Linux Journal.





Page 2 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Sitemap | Contact Us

Rocket Fuel