Recipe5.6.Populating Value Objects from ActionForms


Recipe 5.6. Populating Value Objects from ActionForms

Problem

You don't want to have to write numerous getters and setters to pass data from your action forms to your business objects.

Solution

Use the introspection utilities provided by the Jakarta Commons BeanUtils package in your Action.execute( ) method:

import org.apache.commons.beanutils.*; // other imports omitted   public ActionForward execute(ActionMapping mapping,       ActionForm form,       HttpServletRequest request,       HttpServletResponse response) throws Exception {     BusinessBean businessBean = new BusinessBean( );     BeanUtils.copyProperties(businessBean, form);     // ... rest of the Action

Discussion

A significant portion of the development effort for a web application is spent moving data to and from the different system tiers. Along the way, the data may be transformed in one way or another, yet many of these transformations are required because the tier to which the data is moving requires the information to be represented in a different way.

Data sent in the HTTP request is represented as simple text. For some data types, the value can be represented as a String object throughout the application. However, many data types should be represented in a different format in the business layer than on the view. Date fields provide the classic example. A date field is retrieved from a form's input field as a String. Then it must be converted to a java.util.Date in the model. Furthermore, when the value is persisted, it's usually transformed again, this time to a java.sql.Timestamp. Numeric fields require similar transformations.

The Jakarta Commons BeanUtils package supplied with the Struts distribution provides some great utilities automating the movement and conversion of data between objects. These utilities use JavaBean property names to match the source property to the destination property for data transfer. To leverage these utilities, ensure you give your properties consistent, meaningful names. For example, to represent an employee ID number, you may decide to use the property name employeeId. In all classes that contain an employee ID, you should use that name. Using empId in one class and employeeIdentifier in another will only lead to confusion among your developers and will render the BeanUtils facilities useless.

The entire conversion and copying of properties from ActionForm to business object can be performed with one static method call:

BeanUtils.copyProperties( businessBean ,  form );

This copyProperties( ) method attempts to copy each JavaBean property in form to the property with the same name in businessBean. If a property in form doesn't have a matching property in businessBean, that property is silently ignored. If the data types of the matched properties are different, BeanUtils will attempt to convert the value to the type expected. BeanUtils provides converters from Strings to the following types:

  • java.lang.BigDecimal

  • java.lang.BigInteger

  • boolean and java.lang.Boolean

  • byte and java.lang.Byte

  • char and java.lang.Character

  • java.lang.Class

  • double and java.lang.Double

  • float and java.lang.Float

  • int and java.lang.Integer

  • long and java.lang.Long

  • short and java.lang.Short

  • java.lang.String

  • java.sql.Date

  • java.sql.Time

  • java.sql.Timestamp

While the conversions to character-based and numeric types should cover most of your needs, date type fields (as shown in Recipe 3-13) can be problematic. A good solution suggested by Ted Husted is to implement transformation getter and setter methods in the business object that convert from the native type (e.g. java.util.Date) to a String and back again.

Because BeanUtils knows how to handle DynaBeans and the DynaActionForm implements DynaBean, the Solution will work unchanged for DynaActionForms and normal ActionForms.


As an example, suppose you want to collect information about an employee for a human resources application. Data to be gathered includes the employee ID, name, salary, marital status, and hire date. Example 5-9 shows the Employee business object. Most of the methods of this class are getters and setters; for the hireDate property, however, helper methods are provided that get and set the value from a String.

Example 5-9. Employee business object
package com.oreilly.strutsckbk.ch05; import java.math.BigDecimal; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; public class Employee {          private String employeeId;     private String firstName;     private String lastName;         private Date hireDate;     private boolean married;     private BigDecimal salary;     public BigDecimal getSalary( ) {         return salary;     }     public void setSalary(BigDecimal salary) {         this.salary = salary;     }     public String getEmployeeId( ) {         return employeeId;     }     public void setEmployeeId(String employeeId) {         this.employeeId = employeeId;     }     public String getFirstName( ) {         return firstName;     }     public void setFirstName(String firstName) {         this.firstName = firstName;     }     public String getLastName( ) {         return lastName;     }     public void setLastName(String lastName) {         this.lastName = lastName;     }     public boolean isMarried( ) {         return married;     }     public void setMarried(boolean married) {         this.married = married;     }     public Date getHireDate( ) {         return hireDate;     }     public void setHireDate(Date HireDate) {         this.hireDate = HireDate;     }     public String getHireDateDisplay( ) {         if (hireDate == null)             return "";         else              return dateFormatter.format(hireDate);     }     public void setHireDateDisplay(String hireDateDisplay) {         if (hireDateDisplay == null)              hireDate = null;         else {             try {                 hireDate = dateFormatter.parse(hireDateDisplay);             } catch (ParseException e) {                 e.printStackTrace( );             }         }     }     private DateFormat dateFormatter = new SimpleDateFormat("mm/DD/yy"); }

Example 5-10 shows the corresponding ActionForm that will retrieve the data from the HTML form. The hire date is represented in the ActionForm as a String property, hireDateDisplay. The salary property is a java.lang.String, not a java.math.BigDecimal, as in the Employee object of Example 5-9.

Example 5-10. Employee ActionForm
package com.oreilly.strutsckbk.ch05; import java.math.BigDecimal; import org.apache.struts.action.ActionForm; public class EmployeeForm extends ActionForm {          private String firstName;     private String lastName;         private String hireDateDisplay;     private String salary;     private boolean married;     public String getEmployeeId( ) {         return employeeId;     }     public void setEmployeeId(String employeeId) {         this.employeeId = employeeId;     }     public String getFirstName( ) {         return firstName;     }     public void setFirstName(String firstName) {         this.firstName = firstName;     }     public String getLastName( ) {         return lastName;     }     public void setLastName(String lastName) {         this.lastName = lastName;     }     public boolean isMarried( ) {         return married;     }     public void setMarried(boolean married) {         this.married = married;     }     public String getHireDateDisplay( ) {         return hireDateDisplay;     }     public void setHireDateDisplay(String hireDate) {         this.hireDateDisplay = hireDate;     }     public String getSalary( ) {         return salary;     }     public void setSalary(String salary) {         this.salary = salary;     } }

If you wanted to use a DynaActionForm, you would configure it identically as the EmployeeForm class. The form-bean declarations from the struts-config.xml file show the declarations for the EmployeeForm and a functionally identical DynaActionForm:

<form-bean name="EmployeeForm"             type="com.oreilly.strutsckbk.ch05.EmployeeForm"/> <form-bean name="EmployeeDynaForm"             type="org.apache.struts.action.DynaActionForm">     <form-property name="employeeId" type="java.lang.String"/>     <form-property name="firstName" type="java.lang.String"/>     <form-property name="lastName" type="java.lang.String"/>     <form-property name="salary" type="java.lang.String"/>     <form-property name="married" type="java.lang.Boolean"/>     <form-property name="hireDateDisplay" type="java.lang.String"/> </form-bean>

The following is the action mapping that processes the form. In this case, the name attribute refers to the handcoded EmployeeForm. You could, however, change this to use the EmployeeDynaForm without requiring any modifications to the SaveEmployeeAction or the view_emp.jsp JSP page:

<action    path="/SaveEmployee"            name="EmployeeForm"            scope="request"            type="com.oreilly.strutsckbk.ch05.SaveEmployeeAction">     <forward name="success" path="/view_emp.jsp"/> </action>

The data is converted and copied from the form to the business object in the SaveEmployeeAction shown in Example 5-11.

Example 5-11. Action to save employee data
package com.oreilly.strutsckbk.ch05; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.commons.beanutils.BeanUtils; import org.apache.struts.action.Action; import org.apache.struts.action.ActionForm; import org.apache.struts.action.ActionForward; import org.apache.struts.action.ActionMapping; public class SaveEmployeeAction extends Action {     public ActionForward execute(ActionMapping mapping,                                   ActionForm form,                                  HttpServletRequest request,                                  HttpServletResponse response)                           throws Exception {         Employee emp = new Employee( );                  // Copy to business object from ActionForm         BeanUtils.copyProperties( emp, form );         request.setAttribute("employee", emp);                 return mapping.findForward("success");              } }

Finally, two JSP pages complete the example. The JSP of Example 5-12 (edit_emp.jsp) renders the HTML form to retrieve the data.

Example 5-12. Form for editing employee data
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix=   "bean" %> <%@ taglib uri="http://jakarta.apache.org/struts/tags-html" prefix=   "html" %> <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <html> <head>   <title>Struts Cookbook - Chapter 5 : Add Employee</title> </head> <body>   <h2>Edit Employee</h2>   <html:form action="/SaveEmployee">     Employee ID: <html:text property="employeeId"/><br />     First Name: <html:text property="firstName"/><br />     Last Name: <html:text property="lastName"/><br />     Married? <html:checkbox property="married"/><br />     Hired on Date: <html:text property="hireDateDisplay"/><br />     Salary: <html:text property="salary"/><br />     <html:submit/>   </html:form> </body> </html>

The JSP in Example 5-13 (view_emp.jsp) displays the results. This page is rendering data from the business object, and not an ActionForm. This is acceptable since the data on this page is for display purposes only. This approach allows for the formatting of data, (salary and hireDate) to be different than the format in which the values were entered.

Example 5-13. View of submitted employee data
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <%@ taglib uri="http://jakarta.apache.org/struts/tags-bean" prefix=   "bean" %> <%@ taglib uri="http://java.sun.com/jstl/core" prefix="c" %> <html> <head>   <title>Struts Cookbook - Chapter 5 : View Employee</title> </head> <body>   <h2>View Employee</h2>     Employee ID: <bean:write name="employee" property="employeeId"/><br />     First Name: <bean:write name="employee" property="firstName"/><br />     Last Name: <bean:write name="employee" property="lastName"/><br />     Married? <bean:write name="employee" property="married"/><br />     Hired on Date: <bean:write name="employee" property="hireDate"                               format="MMMMM dd, yyyy"/><br />     Salary: <bean:write name="employee" property="salary" format="$##0.00"/     ><br /> </body> </html>

When you work with this example, swap out the handcoded form for the DyanActionForm to see how cleanly BeanUtils works. When you consider how many files need to be changed for one additional form input, the use of BeanUtils in conjunction with DynaActionForms becomes obvious.

See Also

Recipe 3.13 discusses additional considerations when working with Date fields specifically. Complete documentation on the BeanUtils package can be found at http://jakarta.apache.org/commons/beanutils/api/org/apache/commons/beanutils/BeanUtils.html.

DynaActionForms are discussed in Recipe 5.1.



    Jakarta Struts Cookbook
    Jakarta Struts Cookbook
    ISBN: 059600771X
    EAN: 2147483647
    Year: 2005
    Pages: 200

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