succeeding with struts

27 404 0
succeeding with struts

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

Succeeding With Struts: Indexed Properties and Beans as Properties By James M. Turner In last month's article we looked at how to use DynaForms to replace standard form beans with dynamically created ones. This month we can take it one step further and examine how to implement master-detail records, something that occurs frequently in web applications. For example, suppose you're implementing a purchase-order form. You have 4 rows, each one of which has multiple columns (part number, quantity, price, etc.) Using a brute for approach, you could implement it like so: struts-config.xml <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd"> <struts-config> <form-beans> <form-bean name="purchaseOrderForm" type="org.apache.struts.action.DynaActionForm"> <form-property name="partNumber1" type="java.lang.String"/> <form-property name="quantity1" type="java.lang.String"/> <form-property name="price1" type="java.lang.String"/> <form-property name="partNumber2" type="java.lang.String"/> <form-property name="quantity2" type="java.lang.String"/> <form-property name="price2" type="java.lang.String"/> <form-property name="partNumber3" type="java.lang.String"/> <form-property name="quantity3" type="java.lang.String"/> <form-property name="price3" type="java.lang.String"/> <form-property name="partNumber4" type="java.lang.String"/> <form-property name="quantity4" type="java.lang.String"/> <form-property name="price4" type="java.lang.String"/> </form-bean> </form-beans> <action-mappings> <action path="/generatePO" type="article2.GeneratePO" name="purchaseOrderForm" scope="request" input="/purchaseOrder.jsp"> <forward name="success" path="/displayPurchaseOrder.jsp" redirect="false" /> </action> </action-mappings> <message-resources parameter="ApplicationResources" /> <plug-in className="org.apache.struts.validator.ValidatorPlugIn"> <set-property value="/WEB-INF/validator-rules.xml" property="pathnames" /> </plug-in> </struts-config> This would tie into a JSP page, which needs to explicitly call out each field: purchaseOrder.jsp <%@ taglib uri="/WEB-INF/c.tld" prefix="c" %> <%@ taglib prefix="fmt" uri="/WEB-INF/fmt.tld" %> <%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <head> <title>Example of a Brute Force Purchase Order</title> </head> <h1>Example of a Brute Force Purchase Order</h1> <html:errors/> <html:form action="/generatePO"> <TABLE WIDTH="80%" BORDER="1"> <TR><TH align="left">Part Number</TH> <TH align="left">Quantity</TH> <TH align="left">Price</TH></TR> <TR><TD><html:text property="partNumber1"/></TD> <TD><html:text property="quantity1"/></TD> <TD><html:text property="price1"/></TD></TR> <TR><TD><html:text property="partNumber2"/></TD> <TD><html:text property="quantity2"/></TD> <TD><html:text property="price2"/></TD></TR> <TR><TD><html:text property="partNumber3"/></TD> <TD><html:text property="quantity3"/></TD> <TD><html:text property="price3"/></TD></TR> <TR><TD><html:text property="partNumber4"/></TD> <TD><html:text property="quantity4"/></TD> <TD><html:text property="price4"/></TD></TR> </table> <html:submit/> </html:form> And, of course, the appropriate Action to process the form: GeneratePO.java package article2; import org.apache.struts.action.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.ServletException; import java.io.IOException; import article2.Utils; public class GeneratePO extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException{ DynaActionForm poForm = (DynaActionForm) form; if (!Utils.nullOrBlank((String)poForm.get("partNumber1"))) { request.setAttribute("total1", computeTotal(poForm, "price1", "quantity1")); } if (!Utils.nullOrBlank((String)poForm.get("partNumber2"))) { request.setAttribute("total2", computeTotal(poForm, "price2", "quantity2")); } if (!Utils.nullOrBlank((String)poForm.get("partNumber3"))) { request.setAttribute("total3", computeTotal(poForm, "price3", "quantity3")); } if (!Utils.nullOrBlank((String)poForm.get("partNumber4"))) { request.setAttribute("total4", computeTotal(poForm, "price4", "quantity4")); } return mapping.findForward("success"); } private Double computeTotal (DynaActionForm form, String price, String quantity) { return new Double(article2.Utils.parseFormFieldToInt(form, quantity) * article2.Utils.parseFormFieldToDouble(form, price)); } } This approach, however, is both inelegant and inflexible, in order to add another line you'd need to manually add three more form-properties, and change both your JSP form and Action to know about them. Luckily, Struts 1.1 offers a convenient way to both deal with this type of form, and to expand it dynamically (or even at runtime) without having to change any code or JSP. To begin, we define a simple Java Bean that holds the data on one line of the form: POLine.java package article2; public class POLine { private String partNumber; private String quantity; private String price; private double total; public String getPartNumber() { return partNumber; } public void setPartNumber(String partNumber) { this.partNumber = partNumber; } public String getQuantity() { return quantity; } public void setQuantity(String quantity) { this.quantity = quantity; } public String getPrice() { return price; } public void setPrice(String price) { this.price = price; } public double getTotal() { return total; } public void setTotal(double total) { this.total = total; } } With this in place, we change our struts-config.xml so that the form uses an array of these beans, rather than explicit values for each field. struts-config.xml version 2 <struts-config> <form-beans> <form-bean name="purchaseOrderBeanForm" type="org.apache.struts.action.DynaActionForm"> <form-property name="lines" type="article2.POLine[]" size="4" /> </form-bean> </form-beans> <action-mappings> <action path="/generateBeanPOForm" forward="/purchaseOrderBean.jsp" name="purchaseOrderBeanForm" scope="request"/> <action path="/generateBeanPO" type="article2.GenerateBeanPO" name="purchaseOrderBeanForm" scope="request" input="/purchaseOrderBean.jsp"> <forward name="success" path="/displayPurchaseOrderBean.jsp" redirect="false"/> </action> </action-mappings> <message-resources parameter="ApplicationResources"/> </struts-config> A few notes are required here in explanation. The size parameter is late addition to Struts 1.1, it works like this. If the type parameter of a form- property is an array, and the size parameter is present, Struts will construct an array of that size, and use the zero-argument instatiator of the class to populate the array. In this case, it will create a 4-element array of type POLine and use the POLine() instantiator to create values for each slot. If you don't specify a size parameter, no array will be created (useful for dynamically generated forms, which will be discussed later in this article.) In order to have the array available for the form, we need to precreate it before the JSP page is displayed. To do this, we create an action called /geneateBeanPOForm which doesn't actually call an Action class. Instead, it instantiates the form in request scope and then immediately forwards to purchaseOrderBean.jsp. purchaseOrderBean.jsp <%@ taglib uri="/WEB-INF/c.tld" prefix="c" %> <%@ taglib prefix="fmt" uri="/WEB-INF/fmt.tld" %> <%@ taglib uri="/WEB-INF/struts-tiles.tld" prefix="tiles" %> <%@ taglib uri="/WEB-INF/struts-html.tld" prefix="html" %> <head> <title>Example of a Bean Based Purchase Order</title> </head> <h1>Example of a Bean Based Purchase Order</h1> <html:form action="/generateBeanPO"> <TABLE WIDTH="80%" BORDER="1"> <TR><TH align="left">Part Number</TH> <TH align="left">Quantity</TH> <TH align="left">Price</TH></TR> <c:forEach var="lines" items="${purchaseOrderBeanForm.map.lines}" > <TR><TD><html:text indexed="true" name="lines" property="partNumber"/></TD> <TD><html:text indexed="true" name="lines" property="quantity"/></TD> <TD><html:text indexed="true" name="lines" property="price"/></TD></TR> </c:forEach> </table> <html:submit/> </html:form> Notice how much cleaner this code is. We use the JSTL forEach iterator to get a handle on each element of the POLine array, and bind it to the variable lines. Then we can use the indexed parameter of the html:text tag to indicate that each field is actually being indexed by the surrounding forEach loop. A quirk of the html:text tag is that the name parameter (and by extension, the variable you use in the var field of the forEach) must match the name of the form-property. When this is executed, the resulting HTML looks like: <head> <title>Example of a Bean BasedPurchase Order</title> </head> <h1>Example of a Bean Bease Purchase Order</h1> <form name="purchaseOrderBeanForm" method="post" action="/jdj/generateBeanPO.do"> <TABLE WIDTH="80%" BORDER="1"> <TR><TH align="left">Part Number</TH> <TH align="left">Quantity</TH> <TH align="left">Price</TH></TR> <TR><TD><input type="text" name="lines[0].partNumber" value=""></TD> <TD><input type="text" name="lines[0].quantity" value=""></TD> <TD><input type="text" name="lines[0].price" value=""></TD></TR> <TR><TD><input type="text" name="lines[1].partNumber" value=""></TD> <TD><input type="text" name="lines[1].quantity" value=""></TD> <TD><input type="text" name="lines[1].price" value=""></TD></TR> <TR><TD><input type="text" name="lines[2].partNumber" value=""></TD> <TD><input type="text" name="lines[2].quantity" value=""></TD> <TD><input type="text" name="lines[2].price" value=""></TD></TR> <TR><TD><input type="text" name="lines[3].partNumber" value=""></TD> <TD><input type="text" name="lines[3].quantity" value=""></TD> <TD><input type="text" name="lines[3].price" value=""></TD></TR> </table> <input type="submit" value="Submit"> </form> Notice that nowhere in the JSP do we specify the number of lines, the forEach loop figures it out automatically. This means that if you want to add more lines, you just change the size parameter, and it all updates automatically. Finishing things out, here's the Action to process the form submission: GenerateBeanPO.java package article2; import org.apache.struts.action.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.ServletException; import java.io.IOException; import article2.Utils; public class GenerateBeanPO extends Action { public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { DynaActionForm poForm = (DynaActionForm) form; POLine[] lines = (POLine[]) poForm.get("lines"); for (int i = 0; i < lines.length; i++) { if (!Utils.nullOrBlank(lines[i].getPartNumber())) { try { double total = Integer.parseInt(lines[i].getQuantity()) * Double.parseDouble(lines[i].getPrice()); lines[i].setTotal(total); } catch (NumberFormatException ex) { } } } return mapping.findForward("success"); } } Once again, the code is totally general, there's no need to specify how many lines to iterate over. You can even dynamically specify the size of the form (and prepopulate it) at run time. I've recently used this technique with an insurance application where the user has to choose various parameters such as copays and deductables for a claim modeling system. by using an Action instead of a forward to precreate the form-property value, and leaving the size parameter out. The actual can then create an array of the appropriate size and prepopulate values. The only trick is that the form must be placed in session scope, because it needs to be available and of the right size to accept the submitted values. In the next edition of Succeeding with Struts, we'll tie this all together by looking at how to use the Validator Framework to handle both simple and bean-based validation, and take a look at the new validWhen validation which has just been added to Struts. About the author James Turner is the Director of Software Development for Benefit Systems, Inc. He is also a contributor to the Apache Struts project. He has two books out on web-facing Java technologies, MySQL and JSP Web Applications, and Struts Kick Start. His third book, Java Server Faces Kick Start, will be published by Sams in the 4th quarter of 2003. Succeeding With Struts: Dynamically Sized Forms By James M. Turner Go to page: 1 2 Next In the previous installment of Succeeding with Struts, I alluded to the ability of DynaForms to dynamically size forms at run time. In other words, the ability to have a form that could be 5 rows long, or 10 rows, or 15 rows as needed. Perhaps a bit unwisely, I let the actual implementation of such a strategy as an exercise to the reader. In the following months, I've received dozens of inquires from readers looking for the dirty details, so this month I'll demonstrate not one, but two seperate ways to implement dynamically sizable forms. The first method is the one I mentioned in passing in the previous column, leaving the size parameter off the form-property attribute of a DynaForm To see how this works, let's look at a very simple application that lets the user add comments about various Star Wars actors. The key fact in this application of interest to us is that the number of actors is dynamically set during the form setup, rather than in the struts-config.xml file. First, let's look at the struts-config.xml file itself: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.1//EN" "http://jakarta.apache.org/struts/dtds/struts- config_1_1.dtd"> <struts-config> <form-beans> <form-bean name="dynamicArrayForm" type="org.apache.struts.validator.DynaValidatorForm"> <form-property name="people" type="demo.Person[]"/> </form-bean> </form-beans> <action-mappings> <action path="/setupForm" type="demo.SetupFormAction" name="dynamicArrayForm" scope="session" validate="false"> <forward name="success" path="/displayForm.jsp"/> </action> <action path="/processActorComments" type="demo.ProcessFormAction" name="dynamicArrayForm" scope="session" validate="false"> <forward name="success" path="/displayForm.jsp"/> </action> </action-mappings> </struts-config> As you can see, this is a fairly simple configuration file with one form and two actions defined. The first Action, /setupForm, is used to set up the form before the initial display, and the other action, /processActorComments is what processes the comments entered by the user. There are two important things to notice in this file that are critical to making things work: 1. The people form property is defined as type demo.Person[] (meaning an array of demo.Person), but no size parameter is given. This will result in a placeholder for an array being created, but no actual array being instantiated. 2. The two actions define the form as session scoped. This is critical because when the user submits the form after filling in values, the values are populated back into the form before the action is run. This means that there is no opportunity to manually create the array with the proper number of slots, as you will see is done in the SetupFormAction class before the form is displayed. In other words, when the form is submitted, there must already be the appropriate slots to accept the form values, and the only way to ensure this is to have the form already instantiated in session scope. There's basically no value in looking at the Person bean, it's just a vanilla bean with lastName, firstName, dateOfBirth, gender and comment fields. The source is included in the WAR file. Now let's take a look at the SetupFormAction class, which is called before the form is first displayed. package demo; /** * Copyright 2004, James M. Turner. * All Rights Reserved * * A Struts action that sets up a DynaForm which is globally scoped */ import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.*; import org.apache.struts.action.*; import org.apache.struts.validator.DynaValidatorForm; [...]... struts- config.xml< /struts- config> ... DynaBeans (and the Struts implementation, Dynaforms) work, let's begin with a simple Struts Form that records name, address, and telephone Here's how it's currently implemented using ActionForm article1.CustomerForm package article1; import import import import import org.apache .struts. action.ActionForm; org.apache .struts. action.ActionErrors; org.apache .struts. action.ActionMapping; org.apache .struts. action.ActionError;... found at http://www.blackbear.com /struts. war About the author James Turner is the Director of Software Development for Benefit Systems, Inc He is also a contributor to the Apache Struts project He has two books out on web-facing Java technologies, MySQL and JSP Web Applications, and Struts Kick Start Succeeding With Struts: Dynaforms By James M Turner Once you've been using Struts for a while, you'll begin... far, this is vanilla Struts that everyone should be familiar with But, by using some of the new features of Struts 1.1, you can drastically reduce the amount of code you need to write For example, we can begin be using the Dynaform extension to eliminate the need for an ActionForm class To do this, we change the definition of customerForm in the struts- config.xml to use the org.apache .struts. action.DynaActionForm... System.out.println("postalCode = " + custForm.getPostalCode()); System.out.println("phone = " + custForm.getPhone()); } return mapping.findForward("success"); } This is all tied together, as always with Struts, in the struts- config.xml file: uri="/WEB-INF /struts- html.tld" prefix="html"... standard output (which will place them in the Catalina log file): article1.AddCustomerAction package article1; import import import import org.apache .struts. action.Action; org.apache .struts. action.ActionMapping; org.apache .struts. action.ActionForward; org.apache .struts. action.ActionForm; import import import import javax.servlet.http.HttpServletRequest; javax.servlet.http.HttpServletResponse; javax.servlet.ServletException;... notice that you spend a lot of time creating ActionForm classes While these classes are critical to the MVC architecture of Struts (as they implement the view portion), they are usually simply a collection of bean properties and a validate method (also sometimes a reset method.) With the Struts 1.1 release, developers have a new set of options to create their view objects, based around DynaBeans DynaBeans... processes the results: package demo; /** * Copyright 2004, James M Turner * All Rights Reserved * * A Struts action that sends the new comments to the console */ import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.*; import org.apache .struts. action.*; import org.apache .struts. validator.DynaValidatorForm; public class ProcessFormAction extends Action { public ActionForward... data: package demo; /** * Copyright 2004, James M Turner * All Rights Reserved * * A Struts action that sets up a DynaForm which is globally scoped */ import import import import java.io.IOException; java.util.HashMap; javax.servlet.ServletException; javax.servlet.http.*; import org.apache .struts. action.*; import org.apache .struts. validator.DynaValidatorForm; public class SetupHashFormAction extends Action . technologies, MySQL and JSP Web Applications, and Struts Kick Start. Succeeding With Struts: Dynaforms By James M. Turner Once you've been using Struts for a while, you'll begin to notice. "http://jakarta.apache.org /struts/ dtds /struts- config_1_1.dtd"> < ;struts- config> <form-beans> <form-bean name="purchaseOrderForm" type="org.apache .struts. action.DynaActionForm"> . total; } } With this in place, we change our struts- config.xml so that the form uses an array of these beans, rather than explicit values for each field. struts- config.xml version 2 < ;struts- config>

Ngày đăng: 29/04/2014, 15:51

Mục lục

    Start Wars Actor Fact Page

    Example of a Standard Customer Form

    Example of a Standard Customer Form

Tài liệu cùng người dùng

Tài liệu liên quan