Enter Hours Screen: A Form Controller Example


The Timesheet List screen example we just looked at demonstrated how to develop a simple no-form controller. Now let's look at a slightly more complex example using the Enter Hours screen shown in Figure 7.6.

As you can see from Figure 7.6, the Enter Hours screen enables users to enter their hours and select the department these hours should be charged to (using a drop-down list). This functionality will require us to get a list of department names, bind the HTML form fields to a Java object, validate data entered on the screen, and display error/status messages on the screen.

Step-by-Step Configuration

The following are steps required to configure the Enter Hours screen in our timex-servlet.xml file. For the sake of brevity, I will not provide detailed explanations for the same steps we covered previously for the Timesheet List screen.

Map Handler

The following line provides the mapping for the Enter Hours view to the controller class:

<prop key="/enterhours.htm">enterHoursController</prop>


Define Controller and Associated Classes

The configuration for the Enter Hours controller is a bit more involved than the Timesheet List controller, so let's take a closer look at it.

First, you will notice that we have two model classes and one security-related (utility) class; these are required for the Enter Hours screen to function, which are configured as follows:

<property name="timesheetManager">     <ref bean="timesheetManager" /> </property> <property name="departmentManager">     <ref bean="departmentManager" /> </property> <property name="applicationSecurityManager">     <ref bean="applicationSecurityManager" /> </property>


The following lines configure the command class for the EnterHoursController:

<property name="commandClass">     <value>com.visualpatterns.timex.model. Timesheet</value> </property>


The remainder of the configuration for this controller is Spring specific. For example, you will notice the validator property, which is an optional configuration but one we will use to validate the input data from the screen. The formView is the name of the actual form view and successView is the view you want Spring to redirect to upon a successful form submittal. The sessionForm property allows us to keep the same instance of the command object in the session versus creating a new one each time.

<property name="formView">     <value>enterhours</value> </property> <property name="successView">     <value>redirect:timesheetlist.htm</value> </property> <property name="validator">     <ref bean="enterHoursValidator" /> </property>


ResourceBundle

One other configuration item we should look at is related to externalizing string messages for internationalization and other purposes, as shown here:

<bean      >     <property name="basenames">         <list>             <value>messages</value>         </list>     </property> </bean>


The ResourceBundleMessageSource Spring class relies on JDK's java.util.ResourceBundle class; we will use this to externalize our error and status messages in a file called messages.properties (placed in our timex/src/conf directory), which contains the following messages:

typeMismatch.int=Invalid number specified in a numeric field error.enterhours.missingdepartment=Please select a department error.login.invalid=Invalid employee id or password message.enterhours.savesuccess=Timesheet saved successfully


Alternatively, Spring also provides a class named ReloadableResourceBundleMessageSource, which can be used to reload the properties periodically using its cacheSeconds parameter setting. This can come in handy during development, when the messages file can change often.

Step-by-Step Coding

The following is Spring-related Java code we need to write for our form controller. By the end of this Enter Hours example, we will end up with the following files (under our timex/src directory):

  • conf/messages.properties

  • java/com/visualpatterns/timex/controller/EnterHoursController.java

  • java/com/visualpatterns/timex/controller/EnterHoursValidator.java

  • java/com/visualpatterns/timex/controller/MinutesPropertyEditor.java

  • java/com/visualpatterns/timex/view/enterhours.jsp

Controller Code

Let's start by developing the controller. For starters, notice that instead of implementing the org.springframework.web.servlet.mvc.Controller interface as we did for the TimesheetListController, we are extending Spring's org.springframework.web.servlet.mvc.SimpleFormController (concrete) class.

public class EnterHoursController extends SimpleFormController


SimpleFormController implements the Controller interface but also is part of a hierarchy of various abstract controller-related classes (as we saw in Figure 7.3). It can also automatically redirect the user to the default form view in case of errors and to a different (or same) view if the form submission is successful; this is controlled using the successView and formView properties we set in our timex-servlet.xml for the enterHoursController Spring bean, as we saw earlier.

Let's take a look at the various Spring-related methods for form processing. However, before looking at each method, let's look at the order in which these methods are called.

Figure 7.8 shows three boxes: the first box is essentially when the user first enters the screen; the second box is when the user submits the form with invalid fields (that is, validation fails), and the third/last box shows which methods are called when the validation is successful. Now let's review the type of code that goes into each of these methods.

Figure 7.8. Life cycle of EnterHoursController.


The first method I will discuss is the formBackingObject, which returns a command object that is used to hold the input data from the HTML form fields. Notice that we fetch an existing Timesheet record from the database if parameters are passed into the controller, indicating it is an edit operation versus an add operation, in which case, we construct a new command object (which, incidentally, is a Time Expression domain/business object).

protected Object formBackingObject(HttpServletRequest request) {     if (request.getParameter(TID) != null             && request.getParameter(TID).trim().length() > 0)         return timesheetManager.getTimesheet(Integer.parseInt(request                 .getParameter(TID)), false);     Timesheet timesheet = new Timesheet();     Employee employee = (Employee) applicationSecurityManager             .getEmployee(request);     timesheet.setEmployeeId(employee.getEmployeeId());     timesheet.setStatusCode("P");     timesheet.setPeriodEndingDate(DateUtil.getCurrentPeriodEndingDate());     return timesheet; }


Binding Directly to Domain (Business) Objects

One vital benefit of Spring MVC is the capability to bind the form fields directly to a domain object (Timesheet, for example)! This is one of the things that separates Spring from many other web frameworks.


Next up is the initBinder method, which provides a good place to register custom property editors (discussed shortly), as shown here:

binder.registerCustomEditor(int.class, new MinutesPropertyEditor());


The referenceData method is a good place to return read-only data for forms, typically for drop-down lists on the screen, as we have done by returning a list of departments for the Enter Hours screen:

model.put("departments", departmentManager.getDepartments());


Last, but not least, let's look at one of the most important methods in our controller class, the onSubmit method, shown next. As we saw in Figure 7.8, this method is called only after all validations have passed through successfully:

protected ModelAndView onSubmit(         HttpServletRequest request,         HttpServletResponse response,         Object command,         BindException errors) {     Timesheet timesheet = (Timesheet) command;     timesheetManager.saveTimesheet(timesheet);     request.getSession().setAttribute(             "message",             getMessageSourceAccessor().getMessage(                     "message.enterhours.savesuccess"));     return new ModelAndView(getSuccessView()); }


Also, notice the following code in the onSubmit method, which returns a successful message via the HTTP session. This message is extracted from the messages.properties file (using the message.enterhours.savesuccess key) and displayed on the Timesheet List screen.

This is about all we will cover for the controller class. Now, let's look at the other related classes used by this controller.

Custom Property Editor

As I mentioned earlier in this chapter, Spring makes heavy use of JavaBean style property editors (that is, java.beans.PropertyEditorSupport).

We will write a custom property editor class, MinutesPropertyEditor, to convert the hours entered on the screen to minutes because that is how our database is designed. The code for this class should be fairly straightforward because it performs the conversion from minutes to hours and vice versa (that is, multiplying or dividing by 60 minutes).

Validation

Our validation example is very also fairly straightforward. The main code really is in the validate method of this class, as shown in the following code excerpt:

Timesheet timesheet = (Timesheet)command; if (timesheet.getDepartmentCode() == null ||     timesheet.getDepartmentCode().trim().length() < 1)     errors.reject("error.enterhours.missingdepartment");


The error variable shown here is of type org.springframework.validation. Errors, which provides several reject methods. The example I have shown here is useful for displaying global messages for the entire screen; I tend to use this method rather than the field-specific ones. For example, one of the field-specific reject methods has the following signature: rejectValue(String field, String errorCode).

Also, you might have noticed an onBindAndValidate method in Figure 7.8. This method has the following signature:

onBindAndValidate(HttpServletRequest request,                   Object command,                   BindException errors)


This method is called by Spring automatically after the Validator object has been invoked. This is a great place to do additional validationsfor example, validations based on parameters sent in via HTTP request or database validations using one of the injected model classes, perhaps to check for duplicate records in the database.

View/JSP Code

Now that we are done looking at Java classes for the Enter Hours screen, we can look at the corresponding view code, located in our enterhours.jsp file. We will inspect a few excerpts here.

The first interesting block of code in our view is the displaying of error messages set in our EnterHoursValidator class, as shown here:

<spring:bind path="command.*">     <c:if test="${not empty status.errorMessages}">       <c:forEach var="error" items="${status.errorMessages}">                     <font color="red"><c:out value="${error}" escapeXml="false"/></font><br/>       </c:forEach>     </c:if> </spring:bind>


This is the first time we are seeing the spring:bind tag, so let me explain a few things about it.

The key class behind the spring bind tag library is org.springframework.web.servlet.support.BindStatus. This tag enables you to bind the HTML form fields to the command object (Timesheet, in our case). However, it also provides access to a special variable named status. The status object contains some of the following attributes, which can be used in the JSP code:

  • status.value The value of a given attribute in the command object

  • status.expression The name of a given attribute in the command object

  • status.error A Boolean flag indicating whether an error exists

  • status.errorMessage A field-specific error message

  • status.errorMessages Global error messages for the view

  • status.displayValue Get a string value suitable for display using toString

Now let's look at how fields are bound. The following code shows how the departmentCode JSP/HTML variable is bound to the matching variable in our Command object (that is, Timesheet.departmentCode).

<spring:bind path="command.departmentCode">


That is really all there is to enterhours.jsp; some of the code I have not explained here is because we already covered similar code for the Timesheet List screen example earlier in this chapter (such as looping through code using the JSTL forEach tag).

I wish I could tell you there is more to Spring's bind tag library, but as I mentioned earlier, this library is fairly simple; but what you can do with it is quite powerful.

Binding to Custom (Nonbusiness) Command Objects

One of the key benefits of Spring MVC is that it enables you to bind HTML form fields directly to your domain object. Spring refers to these objects as command objects, perhaps based on the "Command" design pattern, which basically involves encapsulation of a request in an object. Another way to view the concept of a command object is to view it as our form object because it can hold all the values entered on the HTML form. However, because we can bind our HTML form fields directly to our business objects or have other data stored in this object, the term command is more appropriate.

For the Time Expression screens, we bind directly to Timesheet, our domain object. However, you always have the option to create a custom Command class, which could, for example, extend or contain the Timesheet class and provide some additional methods. For instance, I worked on a project where I need to assemble and disassemble a java.util.Date object because the HTML form had separate drop-downs for month, date, and year. In that case, I used methods such as assembleDate and disassembleDate in a custom command class.

There are a couple of ways you can approach a custom command class. For example, we could have done something like the following:

public class TimesheetCommand extends Timesheet


By doing this, you can still bind directly to the setter/getter methods of our business object, but also extend it by adding additional methods, as needed. Also, to construct a custom command class, you would need to specify it in the timex-servlet.xml file and also construct/return an object of this type in the formBackingObject method.

The other approach is to have the TimesheetCommand class contain a reference to the Timesheet object. For example, this class could have a constructor as follows:

public TimesheetCommand(Timesheet timesheet) {...}


Using this approach, you would bind the HTML form fields to the Timesheet object using a notation similar to this:

command.timesheet.minutesMon


The one problem you run into with this approach is related to JavaScript validation checking because JavaScript gets confused with the dots in HTML field names. For example, command.timesheet.minutesMon would translate into timesheet.minutesMon for the HTML input text field name if we used ${status.expression} to fill in the name of this input field.

DateUtil.java

The one other notable file is DateUtil.java; this file provides some utility type date methods. For example, our EnterHoursController class uses one of these methods in its formBackingObject method:

timesheet.setPeriodEndingDate(DateUtil.getCurrentPeriodEndingDate());


JSP Taglib Directives

The one thing I haven't pointed out explicitly until now are the following lines of code you might have noticed in our JSP files:

<%@ taglib prefix="c"      uri="http://java.sun.com/jsp/jstl/core" %> <%@ taglib prefix="fmt"    uri="http://java.sun.com/jsp/jstl/fmt" %> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>


These directives are required before using a JSP tag library. More information on this and other JSP features can be found on the java.sun.com website.



Agile Java Development with Spring, Hibernate and Eclipse
Agile Java Development with Spring, Hibernate and Eclipse
ISBN: 0672328968
EAN: 2147483647
Year: 2006
Pages: 219

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