The Spring Framework

Revision History
Revision 1.313 March 2005aps

Table of Contents

1. Introduction to Spring
2. Bean Factories and Application Contexts
3. Spring MVC
3.1. A Larger Spring MVC example
3.2. Controllers
3.2.1. AbstractController
3.2.2. AbstractCommandController
3.2.3. SimpleFormController
3.2.4. AbstractWizardFormController
3.3. Preventing Duplicate Form Submission
3.4. Preventing Hacking of Business Objects
References

1. Introduction to Spring

References for Spring include: [SWS], [HM05], [JH04], [Johnson03], [JHA05], [Raible04] and [WB05].

A project[AppFuse] has been set up to demonstrate how to integrate a number of different technologies, including Spring and Hibernate, into a realistic but simple web application. This is well worth downloading and studying.

Three main concepts underly the Spring framework:

  • Inversion of Control (IoC), also known as Dependency Injection. Implemented via the BeanFactory class hierarchy.

  • Application Context (AC), implemented via the (rather obviously named) ApplicationContext class, which extends BeanFactory.

  • Aspect Oriented Programming (AOP). Implemented by means of various kinds of proxies, reflection and dynamic, runtime byte code generation.

To get you started, I have written a couple of sample applications:

  • SpringSimple.jar: This contains the sources of a console Spring application that demonstrates almost the simplest possible example of IoC, AOP with an ApplicationContext.

  • SpringHibernate.jar: This contains the sources of a console Spring application that uses Hibernate and transactions on the book shop database that we have been working on.

You should download the above applications, get them working and then experiment by trying to add extra use cases (of your own choice) to the SpringHibernate application.

2.  Bean Factories and Application Contexts

A BeanFactory is a Spring class that creates, configures and manages Java objects. The configuration details are typically kept in xml or property files.

An ApplicationContext is a Spring class that extends BeanFactory, and therefore provides all the same functionality, but which also provides extra enterprise level functionality such as internationalisation support, resource access (i.e. files and URLs), Event propagation (i.e. via event listeners) and hierarchical contexts.

The java objects created by a BeanFactory or ApplicationContext are, by default, singletons, i.e. only one such object will be created and used in the whole application, and each request for an object of that type will return the same object — this is, in fact what we usually want for, for example, data sources, data access objects etc. A true BeanFactory will create these objects on demand, whereas an ApplicationContext will create them when it itself is created.

Non-singleton beans (called prototypes can also be created. This can be seen as a more powerful version of the Java new command, because as well as creating the object, the BeanFactory can initialise it and set the objects properties to refer to other beans in the system (this is called wiring it up). However, while the BeanFactory will manage the entire lifecycle of a singleton object, it will not manage that of a prototype bean. Both true BeanFactory and ApplicationContext classes create prototypes only on demand.

The bean configuration is usually specified in an xml file:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
    <bean id="helloWorld" class="helloworld.HelloWorld">
        <property name="message">
            <value>Hello World</value>
        </property>
    </bean>
</beans>
      

The configuration file contains a list of bean elements, each describing a single object configuration. Actually, it can also contain import elements, which reference other bean configuration files that should be included and a description element for documentation purposes. The most common use of the most important elements and attributes of the configuration file are described below. For the rest, see [SRM].

A bean element will normally have a number of attributes:

  • class: the fully qualified class name of the object to be created. This is usually required (exceptions occur only with more advanced configuration specification).

  • id or name: this is the name by which the bean will be referred to both within the configuration file and by code which requests the bean from the BeanFactory or ApplicationContext. You should normally use the id attribute, as this has special significance to XML files (as specified in the DTD file), and the xml parser in Spring is then able to use it to do extra correctness checking of the configuration file. However, if you need to use characters for the name that are not legal in XML id attributes, you can use name instead.

  • singleton: this attribute is optional and, by default is true. Set it to false if you want this bean to be a prototype. Normally one does not need to use prototypes.

  • init-method: optional no-argument method to invoke after setting all the bean's properties for doing some extra initialization beyond what is possible through setting properties alone.

  • destroy-method: optional no-argument method to invoke when the BeanFactory shuts down. Note that this is only invoked for singleton beans, the BeanFactory does not retain any references to prototype beans.

Inside a bean element, we normally specify a sequence of properties of the bean and the values that those properties should be set to when the BeanFactory creates the object.

[To be continued ...]

3.  Spring MVC

Spring provides support for the presentation layer of a web application too. Actually it provides support for many different types of presentation layers, but we will look specifically at its support for the Model-View-Controller pattern in web applications using Java Server Pages. Raw Java Server Pages are rather painful to write so we will allow ourselves a little anaesthetic in the form of the JSP Standard Tag Library (JSTL). Our first simple Spring MVC application can be downloaded from SprMVC.jar. You should download these files and set up a project in your IDE to get them working. Make sure that you can compile the .java files, build a web deployment directory structure, startup tomcat, deploy the application and debug it, all from within Tomcat. Getting this sorted out early on will make future development of web applications much easier.

WEB-INF/jsp/home.jsp:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>    (1)
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>         (2)

<html>
  <head><title>Greeting</title></head>
  <body>
    <h2><c:out value="${message}"/></h2>                             (3)
  </body>
</html>
}
1

The page directive lets one set general page options

2

The taglib directive declares that this JSP can use the tags defined in the specified tag set (in this case the core set of the JSP Standard Tag Library) by prefixing the tag names in the set with "c:" — the usual prefix for this set.

3

The c:out tag is used to output a string. The string, by default, will have any characters special to HTML (such as '<', '>', '&', ' ' ' or ' " ') escaped properly. The "${message}" part is an Expression Language (EL) term. It means: get the value stored in any of the scopes accessible to the application (i.e. page, request, session or application) under the name "message"

src/HomeController.java:

import org.springframework.web.servlet.mvc.Controller;
import org.springframework.web.servlet.ModelAndView;

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

public class HomeController implements Controller                    (1)
{
    private String greeting;

    public ModelAndView handleRequest(HttpServletRequest request,    (2)
                                      HttpServletResponse response)
            throws Exception
    {
        String s = greeting +
                   ": the date and time is now " +
                   Calendar.getInstance().getTime() ;
        return new ModelAndView("home", "message", s);               (3)
    }

    public void setGreeting(String greeting)
    {
        this.greeting = greeting;
    }
}

1

The actual Controller part of the Model-View-Controller pattern is implemented in Spring via a combination of the DispatcherServlet and all the objects of the heirarchy of classes that implement the Controller class. The idea is that the DispatcherServlet fields the incoming request and farms it out to a Controller object. There is typically one controller object for each type of request. In this case our HomeController implements Controller directly. In practice this is not usually a good idea: Spring provides a whole menagerie of different classes that extend Controller and add considerable functionality to it. Thus one can, instead, extend classes that provide extraction of request parameters and/or form data, validation and even full multipage wizard support, and one should normally extend one of those.

2

This method essentially replaces doGet() and doPost() methods of a servlet, and with the same parameters.

3

Here is where the real structure of MVC is imposed: we execute whatever business methods we like, but the end result should be an object of class ModelAndView, which contains the name of the view that should be presented back to the user in response to the request (in this case the name "home") and the model: the object containing the data that should be displayed within that view, in this case the String s. However, there is one other item that needs to be specified: the name under which the view can find the model object ("message" in this case). Since we are using JSP pages for our views, this name will be the name in the request scope under which the model object will be stored.

SprMVC/WEB-INF/controller-servlet.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">

<beans>
    <bean name="/welcome/home.html"                                  (1)
          class="HomeController">
        <property name="greeting">
            <value>
                Welcome to Spring MVC
            </value>
        </property>
    </bean>

    <bean id="viewResolver"                                          (2)
          class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix">
            <value>/WEB-INF/jsp/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>

    </bean>
</beans>

1

The Spring MVC module automatically looks for the application context xml file for a servlet named x in the file named x-servlet.xml. In this file, we have to wire up our HomeController object. In this case we, as usual, give it a name, specify its class and specify the initial values of its properties (which is where the actual welcome string is defined).

The name here is slightly unusual. In other application context xml files, it has usually been a simple identifier. However, there is nothing stopping us using any string we like with any characters we like (although then we must use the name attribute and not the id attribute). By default, using a class called the BeanNameUrlHandlerMapping, Spring MVC takes the name we choose for a bean that extends Controller, as our HomeController does), and uses it, not just as the name of the bean, but also as the path on the web where the corresponding servlet should be installed. in this case, it means that we can access our web application by using the url: http://localhost:8080/welcome/home.html, assuming that we our running our web application on our local machine and we have not changed the default port for the servlet server from 8080.

In practice this is not really a good way to set the web address of a controller as it couples the name of the bean to the location of the controller on the web. Later on we will look at better ways to do this mapping without coupling the name and the web location in this way.

2

This bean works out how to take a view name, as specified in the ModelAndView constructor in our HomeController class, and turn it into the actual JSP page path relative to the root of the web application. The particular ViewResolver class used here is very simple: it takes the name given and adds a prefix and a suffix. Thus the view "home" is turned into the JSP path /WEB-INF/jsp/home.jsp.

[Important]Important

Generally in a JSP based web application, users can not access any resources (html files, jsp files etc.) stored under the /WEB-INF directly: i.e., trying to specify such an address will cause the web application to return an error. For that reason, many programmers put their JSP pages outside /WEB-INF. However, that then allows users to by-pass the controller servlet and go directly to a JSP page without being redirected by the controller, thereby breaking the MVC pattern. A better solution is to always have all your jsp pages stored under /WEB-INF. Your controller can still forward requests on to such JSP pages or access resources there, but no user, by accident or intention, will then be able to by-pass the controller.

WEB-INF/web.xml:

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd">
<web-app>
    <servlet>                          (1)
        <servlet-name>
            controller
        </servlet-name>
        <servlet-class>
            org.springframework.web.servlet.DispatcherServlet
        </servlet-class>
        <load-on-startup>
            1
        </load-on-startup>
    </servlet>

    <servlet-mapping>                  (2)
        <servlet-name>
            controller
        </servlet-name>
        <url-pattern>
            *.html
        </url-pattern>
    </servlet-mapping>
</web-app>
1

We have to tell is to tell the servlet container (Tomcat, in our case) who we are. This entry says that our application has a servlet that we we refer to as "controller", and that the class involved is org.springframework.web.servlet.DispatcherServlet (which dispatches requests to controllers based on the names defined for beans than extend Controller, as our HomeController does.

2

Finally, we have to tell the servlet container what patterns of web addresses should be sent to the controller for processing. As we want all requests to go through the servlet, and we don't particularly want to show the end user what technology we are using, we use the pattern *.html. Thus the user can not automatically tell if a page in our application is a JSP page, a simple HTML page or something else.

Thus, in summary, and although there can be exceptions, we generally have to set up:

  • One DispatcherServlet to farm out requests to our controllers

  • One controller bean for each type of request which processes the request and returns a ModelAndView object

  • One view for each type of response

  • A mechanism for mapping controllers to web addresses: a HandlerMapping class (if you do not specify this you get BeanNameUrlHandlerMapping by default)

  • A mechanism for resolving view names to views: a ViewResolver class

3.1.  A Larger Spring MVC example

For the remainder of this section, we will discuss a larger example that demonstrates different controller types, command and form input and validation. Download the files from SprMVCFull.jar

3.2.  Controllers

We have seen how, in Spring, the Controller part of the Model-View-Controller pattern is actually divided into two parts: a DispatcherServlet that actually fields the incoming requests and classes that implement the Controller interface to process the operation. The following classes and interfaces in the Controller hierarchy are the most used (there are others as well):

Table 1. Spring Controller classes

ClassPurpose
Controller Top level interface of the entire Controller hierarchy. One should normally not implement this controller directly but rather extend a more functional class that implements it.
AbstractController The simplest concrete class that implements Controller. It is relatively unusual to extend this class, but it is used if you want to invoke an operation that takes no parameters at all.
AbstractCommandController Handles simple command style requests but does not provide much in the way of form support: use for simple commands or where command arguments are passed as parameters of the request URL rather than as fields in a form. Not appropriate where the use has to type in parameters but fine if the parameters are hard coded into the web page and chosen, say, by clicking on an appropriate button.
SimpleFormController Handles forms submission: supports specification of the form view to display the form and the success view when the form submission succeeds. The form view is redisplayed if validation fails and the Spring tags support filling the form with the previously entered data and displaying field specific and global errors on the form.
AbstractWizardFormController Handles multi-page wizard forms: supports navigation between the pages, per page and whole form validation and all the features of SimpleFormController as well.
MultiActionController Allows using a single controller to encode multiple similar controller actions (i.e. one method per controller instead of one class per controller).

3.2.1. AbstractController

An AbstractController is a suitable class to extend if you want a controller that does not access any parameters from the request object (i.e., any parameter set on the web page). Of course, one can still access request parameters by querying the request object directly (it is, after all, passed as a parameter to the handleRequest method) but if one needs such parameters, one should use one of the command or form controllers instead. The advantage that AbstractController provides over a simple Controller is that it provides support for synchonising on the session (see Section 3.3, “Preventing Duplicate Form Submission”) and it extends WebContentGenerator, which provides support for cache control of the web pages, requiring a session to be present when the controller is invoked and choosing whether a HTML POST or GET or both are allowed to invoke this controller: see the API documentation for details. This features are normally configured via the Spring application context file.

In our example program, sprMVCFull.controller.HomeController extends AbstractController:

package sprMVCFull.controller;
import org.springframework.web.servlet.mvc.AbstractController;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Calendar;

public class HomeController extends AbstractController
{
    private String greeting;

    protected ModelAndView handleRequestInternal(HttpServletRequest request,
                                                 HttpServletResponse response)
            throws Exception
    {
        String s = greeting +
                   ": the date and time is now " +
                   Calendar.getInstance().getTime() ;
        return new ModelAndView("home", "message", s);
    }

    public void setGreeting(String greeting)
    {
        this.greeting = greeting;
    }
}

As we can see, this controller does not take any data from the input web page (although it is adding some to the output page) so an AbstractController is very appropriate here.

The sprMVCFull.controller.HomeController class is embedded in our web space at /hello.html via setting a property in the controllerMapping bean in the context file, WEB-INF/controller-servlet.xml.

To invoke this controller, we need a link to /hello.html from a web page. We could use a normal html link, a button that uses a little Javascript to go to the address or a submit button in a form. Whatever we do, we should wrap the URL in a c:url JSTL tag to ensure that url rewriting is done correctly in case the user has disabled cookies and we need to preserve the connection between the user's request and a Session object. In WEB-INF/jsp/index.jsp, the following line does this correctly for a simple link:

<a href='<c:url value="/hello.html"/>'>

3.2.2. AbstractCommandController

An AbstractCommandController is intended to add the automatic handling of parameters from a web page request to the functionality of AbstractController. The idea is that one specifies the model class (i.e., the class of the business object) to copy the request parameters into. Within the controller, one can then access those values in a simple manner without having to process the request object directly. The sprMVCFull.controller.PrintParamController class is an example:

package sprMVCFull.controller;
import org.springframework.web.servlet.mvc.AbstractCommandController;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.validation.BindException;

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

import sprMVCFull.model.PrintParamCommand;
public class PrintParamController extends AbstractCommandController
{
    public PrintParamController()
    {
        setCommandClass(PrintParamCommand.class);
    }

    protected ModelAndView handle(HttpServletRequest request,
                                  HttpServletResponse response,
                                  Object command,
                                  BindException errors) throws Exception
    {
        return new ModelAndView("printCommand", "command", ((PrintParamCommand)command).getParamName());
    }

}

We have to specify the class that the parameters should be copied into, in our case the sprMVCFull.model.PrintParamCommand class, and when the controller is invoked, the AbstractCommandController super class methods will ensure that all parameters in the request whose names match properties in the model bean will be copied into the model bean.

In fact, Spring MVC is much more sophisticated that merely to support "copying into the model bean". For any property in the model bean whose type is not String, Spring will try to use one of its built-in PropertyEditors to transform the String obtained from the request and set the appropriate property in the model bean by doing a suitable transformation of the string. Thus for a property of any of a number of numeric types (int, float, Integer, etc.), Spring will invoke its CustomNumberEditor to parse the string into the appropriate numeric type. Similarly there are built-in editors for boolean and for String arrays (the latter transforms a comma-separated string into an array of Strings). There are others as well, including a date editor, although that is not registered by default so must be registered explicitly if required. You can also define your own custom editors and register them with the request parameter binder in order to handle your own classes if you wish.

The sprMVCFull.controller.PrintParamController class is mapped into our web space at /printCommand so again we can use a link, a button with javascript or a button in a form to invoke it. Again we need to wrap the URL in a c:url JST tag to handle URL rewriting. However, we also need to set the parameter, in our case, a property of type String with name "paramName":

  • A link approach; note that the parameter appears as part of the address.

    <a href='<c:url value="/printCommand.html">
        <c:param name='paramName'>
          Link
        </c:param>
      </c:url>'>
      PrintCommand(Link)
    </a>

  • A button with javascript; again the parameter appears as part of the message but also this approach will fail if the user has turned off javascript in his or her browser.

    <button onclick="location.href='<c:url value="/printCommand.html">
        <c:param name='paramName'>
          Button
        </c:param>
      </c:url>'">
        printCommand(Button)
    </button>

  • A form based approach; this is the preferred way to use a button as it does not require javascript to be enabled. Also this does not display the parameter as part of the address as the form method chosen was "post". If you want the parameter to be part of the address (so that, for example, the user can bookmark the resulting page), simply change the method to "get".

    <form action='<c:url value="/printCommand.html"/>' method="post">
      <button name="paramName" value="FormButton" type="submit">printCommand(FormButton)</button>
    </form>

3.2.3. SimpleFormController

If the user explicitly enters input data on a web page, instead of simply clicking on one of the available links, then there is a possibility that the data entered will be invalid for some reason: perhaps he or she has mis-spelled a word or entered a value, or a combination of values, that is not allowed. In this case we want to be able to easily check the data entered and, if there is a problem with it, to re-display the same page back to the user, with the incorrect data still on it, so that the user can correct the data without having to retype everything all over again. Furthermore, we would like to be able to display appropriate error messages on the page to indicate to the user what the problem is.

The key to understanding SimpleFormController is to realise that there are three separate issues involved:

  1. There is a model object associated with the controller, which has to be created (with the default constructor) or initialised (explicitly by the programmer) the first time into the controller but on second and further invocations of the controller during the processing of the form the values for the model object must come from the form.

  2. There are two different ways the controller gets invoked, from pages that do not contain the form but want the form to be displayed, and from the form page itself when the form is submitted.

    There are (usually) two different ways that processing can continue from the controller: if there was an error in the form data, then the form page should be resdisplayed with the incorrect data and appropriate error message, and if the data is correct then the use case should be processed and the next page should be displayed.

First, let us consider the model object associated with the controller. The relevant controller in our example is sprMVCFull.controllerProcessDetailsController. The model object associated with this controller (and form) is sprMVCFull.model.Person. A fairly minimal implementation of the controller is as follows:

package sprMVCFull.controller;
import org.springframework.web.servlet.mvc.SimpleFormController;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.WebUtils;
import org.springframework.validation.BindException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import sprMVCFull.model.Person;

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

public class ProcessDetailsController extends SimpleFormController
{
    public ProcessDetailsController()
    {
        setCommandClass(Person.class);
    }
    
    protected ModelAndView onSubmit(Object command) throws Exception
    {
        Person person = (Person) command ;

        // This debug call represents the real business use case
        logger.debug("got person: " + person) ;
        return new ModelAndView("printCommand", "command", command.toString());
    }
}

In the constructor, we configure the class used as the model object for the form. The only reason we need to do this is so that the framework can create a new model object of the correct type when we first get the form submitted. Of course, we could avoid doing this configuration here and instead do it in the application context file where we wire up all our context beans. However, I have left this configuration here because sometimes we want to replace that configuration with something else: namely, we sometimes want to create not just a default object of the appropriate type, but one which is pre-initialised to some valuesFor our example, we might want the person details shown to be those of the user, so that he or she can then modify and update them. To this, we replace the four lines of the constructor above with the following:

    protected Object formBackingObject(HttpServletRequest request)
        throws Exception
    {
        Person p = new Person() ;
        p.setFirstname("John");
        p.setLastname("Smith");
        p.setEmail("J.Smith@somewhere.co.uk");
        p.setGender("M");
        return p;
    }

Of course, in practice the model object would, most likely, be retrieved from a database.

Now that our model object is taken care of, we can consider how the controller is invoked.

  • The simplest case is from the page containing the form. Here the form data is sent, as parameters in the request, when the user clicks on a submit button in the form. Automatic binding of the data from the form into the model object should occur, validation of the model object can then take place, redirecting back to the form page if there were errors, before handing off to the controller for dealing with the use case.

  • The second case is when another web page, which does not contain the form, wishes to trigger the presentation of the form. In this case, we want that the controller creates a new model object, or obtains an initialised one use the formBackingObject described above. This object should then be used to set the initial values of the form input fields and the form page should be displayed.

The question remains, how does Spring distinguish between the two cases? The default is that if the incoming request used a POST method, then Spring assumes that it is a submission of data from the form. If, however, the request used a GET method, then an initial request is assumed and no attempt is made to extract the form data from the request. In practice, this matches extremely well with the standard use of requests and form page invocation and submission.

Finally, we look at how processing continues out of a form controller. There are usually two cases:

  1. If the incoming request was a GET, or if there was an error with the data from the form, we wish to display the form view. Since we can construct, or already have, the model object, the only extra piece of information required to display the form view is the name of the view itself, Spring takes care of the forwarding to the view so long as we tell it the view name. This is done by setting the formView property of the controller, and this, in turn can be done in the application context file (which is where I did it for the example code).

  2. If the incoming method was a POST, then the request came from the form web page and the model object has passed validation. Now we want to execute the use case and proceed to the next, or success page. We could do this in a similar way to the formView, by setting the successView property and overriding the simpler protected void doSubmitAction(Object command) method of the controller instead of the onSubmit method. the doSubmitAction is simpler because it does not have access to the request or response objects nor does it have to return a ModelAndView: instead the framework ensures that the successView, as set via property of the same name, is presented when doSubmitAction returns. I tend not to use because usually I want the success view to also display some model data as well, and the doSubmitAction does not support that. This may also be a point against setting the formView in the application context file, as it is a questionable practice to set the two views in two different ways in two different files. A solution is to set the formView in the constructor explicitly.

Sometimes there is one other way to proceed from the controller: if you wish to allow the user to cancel the form altogether then you need to shortcut the standard procedure for dealing with the form. Otherwise, when the form data is submitted, validation will take place and if the data is not correct, the user will be returned to the input form until it is filled in correctly. It is not resonable to force a user to correctly fill in a form that he wishes to cancel.

The solution is intercept the request before it goes through model object validation, check if this is a cancel operation, and, if it is not, continue where you left off. If it is a cancel, however, you can redirect to a suitable alternative ModelAndView. This can be done by adding the following code to the controller:

private static final String PARAM_CANCEL = "_cancel" ;                         (1)

protected ModelAndView processFormSubmission(HttpServletRequest request,       (2)
                                             HttpServletResponse response,
                                             Object command,
                                             BindException errors)
        throws Exception
{
    if (WebUtils.hasSubmitParameter(request, PARAM_CANCEL))                    (3)
        return new ModelAndView("printCommand", "command", "form submission cancelled") ; (4)
    return super.processFormSubmission(request, response, command, errors);    (5)
}
1

First we need to set the name of the parameter that will be set when the user presses the cancel button

2

Overriding this method is only necessary if you want to do some processing BEFORE the validation of the model for the form.

3

Since we have intercepted the dispatching to the controller before handling of the model object has taken place, we must search for the parameter directly in the request object. Fortunately, Spring provides this utilities class that makes that search simple.

4

If the operation invoked was a cancel, then we stop further processing of the controller and immediately return the new target ModelAndView

5

If the operation invoked was not a cancel, then we want to continue where we left off; i.e., we want to let the default processFormSubmission method execute as if we had not overridden it at all.

So far, we have discussed the code in the controller class, and we will not discuss the code in the model object as it is a standard java bean with no special or Spring specific aspects at all. However, we have not discussed how validation of the model object works. To do this we add a special validator class which is specific to the controller (and then wire it in via the application context file). The wiring is accomplished, in our case by adding the following lines to the application context file under the bean for our form controller:

        <property name="validator">
            <bean class="sprMVCFull.model.PersonValidator"/>
        </property>

The class referred to must implement the Validator interface to support validation of the model object, and typically the model object itself will do so, but one can, as in the example code, have an independant class provides the validation support:

package sprMVCFull.model;
import org.springframework.validation.Validator;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import java.util.regex.Pattern;

public class PersonValidator implements Validator
{
    public boolean supports(Class clazz)
    {
        return clazz.equals(Person.class);
    }

    public void validate(Object obj, Errors errors)
    {
        Person p = (Person) obj ;

        ValidationUtils.rejectIfEmptyOrWhitespace(errors,  "firstname",
                                                  "required.firstname", "First name is required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors,  "lastname",
                                                  "required.lastname", "Last name is required");
        ValidationUtils.rejectIfEmptyOrWhitespace(errors,  "gender",
                                                  "required.gender", "Gender is required");
        validateEmail(p.getEmail(), errors);
    }

    private static final Pattern EMAIL_PATTERN = Pattern.compile("^\\w+([_\\.-]\\w+)*@(\\w+([_\\.-]\\w+)*)");
    private void validateEmail(String email, Errors errors)
    {
        ValidationUtils.rejectIfEmptyOrWhitespace(errors,  "email",
                                                  "required.email", "Email address is required");
        if (!EMAIL_PATTERN.matcher(email).matches())
        {
            errors.rejectValue("email", "invalid.email", "Email address is invalid") ;
        }
    }
}

In the above code, the Validator requires implementation of two methods: the support method will be used by Spring to check which of a possible collect of Validator methods is suitable for validating objects of a particular class. The second, validate, actually carries out the validation. The first argument will be the model object with its properties set from the corresponding request object parameters but with no validation yet applied. The second is an errors object that essentially contains a collection of errors found so far. If, on return from this method, the error collection is empty, that validation will be considered to have succeeded and processing can continue. Otherwise it will have failed and the form view will be show again.

Error objects are created and added to the errors collection object either by calling the reject or the rejectValue method on the errors object itself, or by using the convenience methods of ValidationUtils. In general, the Value form of the methods are for errors in a specific field or property of the model object whereas the other methods are used to signify global or multi-field problems.

All that is left now is to integrate the error messages and the model object property values into the forms for display to the user. This is done with a small number of Spring tags. Note that we do not need to use these tags in general: without them, we do not get the error messages on the form or the intial values for the input fields, but copying of form input values to the model object on submission of the form would still work.

<<%@ page contentType="text/html;charset=UTF-8" language="java" session="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>   (1)

<html>
  <head>
    <title>Books Rn't Us</title>
  </head>
  <body>
    <h1>
        Test of a SimpleForm Controller
    </h1>
    <p>
       <form action='<c:url value="/processDetails.html"/>' method="post">
          <p>
            <spring:hasBindErrors name="command">                    (2)
              <c:forEach var="err" items="${errors.globalErrors}">
                <span class="error"><c:out value="${err.defaultMessage}"/></span><br />
              </c:forEach>
            </spring:hasBindErrors>

            First name:
            <spring:bind path="command.firstname">                   (3)
                    <input type="text" name="firstname" value="<c:out value="${status.value}"/>">
                    <span class="error"><c:out value="${status.errorMessage}"/></span>
            </spring:bind>
            <br />
            Last name:
            <spring:bind path="command.lastname">
                    <input type="text" name="lastname" value="<c:out value="${status.value}"/>">
                    <span class="error"><c:out value="${status.errorMessage}"/></span>
            </spring:bind>
            <br />
            email:
            <spring:bind path="command.email">
                    <input type="text" name="email" value="<c:out value="${status.value}"/>">
                    <span class="error"><c:out value="${status.errorMessage}"/></span>
            </spring:bind>
            <br />
            <spring:bind path="command.gender">                      (4)
              <input type="radio" name="gender" value="M" <c:if test='${status.value == "M"}'>checked</c:if> > Male
              <span class="error"><c:out value="${status.errorMessage}"/></span>
              <br />
              <input type="radio" name="gender" value="F" <c:if test='${status.value == "F"}'>checked</c:if> > Female
              <br />

            </spring:bind>

          <input type="submit" value="Send"> <INPUT type="Reset"> <input type="submit" value="Cancel" name="_cancel">
          </p>
       </form>
  </body>
</html>
1

Since we are using the Spring tags, we need to declare them to the JSP page.

2

Here we print out the global errors that are not specific to a single property of the model object but are caused by the state of a combination of different properties. Note that each errors object is specific to a single object so here we wrap the error printing code in a spring tag that does nothing if the named object has no errors. Otherwise it sets the errors variable within the spring:hasBindErrors element to refer to the errors object specific to the model object named.

Within the tag then, we can iterate, using the JSTL tags, over the list of global errors in the errors object, printing out the default message of each.

3

For an actual input field, we surround it with a spring:bind tag and specify the "path" to the property we are concerned with, in this case command.firstname, to refer to the firstname property of the model object stored in the request object under the name command. There is a whole expression language for such paths which are more or less compatible with the JSTL EL.

With this tag, we can refer to objects under the name, local to the spring:bind element, of status. In particular, status.value will hold the current value of the corresponding model property and status.errorMessage will hold the error message associated with this field if there is one.

It seems a little inelegant that one has to specify the property associated with the input field twice: once for the path of the spring:bind tag, and once as the name of the input tag. In fact, this is not necessary as there is another property of status available: status.expression returns the expression name of the property, i.e., precisely the string that needs to be passed as the name value of the input tag. Thus we can remove the second specification of the property by writing the whole tag as:

<spring:bind path="command.firstname">                   
    <input type="text" name="<c:out value="${status.expression}"/>"
                      value="<c:out value="${status.value}"/>">
    <span class="error"><c:out value="${status.errorMessage}"/></span>
</spring:bind>

Finally the span element around the printing of the error message is there so that, using CSS, we can style all printing of error messages (for example to set them to print in red).

4

We are faced with a slightly different problem when dealing with radio buttons and option groups. Here each input element is rendered separately but must be bound to the same model property. For a radio button, the input element corresponding to the chosen value must have the property checked, and the others must not. This is easily arranged with the use of the c:if element as shown.

3.2.4. AbstractWizardFormController

A particular situation with respect to forms that frequently arises is where a sequence of forms have to be filled in and each form in the sequence contributes only some properties of the model object. In fact, AbstractWizardFormController extends AbstractFormController, which is almost all of SimpleFormController so most of the discussion of SimpleFormController also applies here. Therefore I will concentrate on the differences. These include

  1. Managing the navigation between pages

  2. Validating the input from a single page instead of the whole model object at once

The model object itself is, as usual, just an ordinary Java bean. For the sake of the example, I assume that it has a number of String properties to represent various fields in a survey.

Unlike in the SimpleFormController case, we do not have a single form view but one for each page of the wizard. We set up the pages in the application context file, WEB-INF/controller-servlet.xml by setting the pages property of the controller bean to a list of views:

<bean id="surveyController" class="sprMVCFull.controller.SurveyController">
    <property name="pages">
        <list>
            <value>survey_colour_food</value>
            <value>survey_country_language</value>
            <value>survey_film_singer</value>
        </list>
    </property>
</bean>

The views in the list are just standard form views. I will show only the last one of the three:

<%@ page contentType="text/html;charset=UTF-8" language="java" session="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jstl/core" %>
<%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %>

<html>
  <head>
    <title>Books Rn't Us</title>
  </head>
  <body>
    <h1>
        Test of a Wizard Controller
    </h1>
    <p>
       <form action='<c:url value="/survey.html"/>' method="post">   (1)
          <p>
            <spring:hasBindErrors name="command">
              <c:forEach var="err" items="${errors.globalErrors}">
                <span class="error"><c:out value="${err.defaultMessage}"/></span><br />
              </c:forEach>

            </spring:hasBindErrors>
            What is your favourite film:
            <spring:bind path="command.film">
                    <input type="text" name="film" value="<c:out value="${status.value}"/>">
                    <span class="error"><c:out value="${status.errorMessage}"/></span>
            </spring:bind>
            <br />
            What is your favourite singer:
            <spring:bind path="command.singer">
                    <input type="text" name="singer" value="<c:out value="${status.value}"/>">
                    <span class="error"><c:out value="${status.errorMessage}"/></span>
            </spring:bind>
            <br />

          <input type="submit" value="Previous" name="_target1">     (2)
          <input type="submit" value="Finish" name="_finish">        (3)
          <input type="Reset">                                       (4)
          <input type="submit" value="Cancel" name="_cancel">        (5)
          </p>
       </form>
  </body>
</html>
1

As with the SimpleFormController, it is important to submit the form using a POST method and not a GET method as Spring, by default, distinguishes between a request to display the initial form and a request to submit data from the form via the choice of submit method.

2

You can have as many wizard page navigation buttons as you like. The name has to be "_targetN", where N is the number of the wizard page you wish to switch to. This number corresponds to the position (starting at 0) in the pages list property of the controller bean in the application context file. It is usual to provide a next and previous button on each page, with previous disabled or missing on the first page and next similarly on the last and with a finish button only on the last page of the wizard, but nothing except taste and good Human Computer Interaction design stops you from making any weird and wonderful wiring together of the pages of the wizard. It is, of course, quite reasonable to put a finish button on each page where suitable defaults exist for the unset input fields of the remaining pages.

3

The "finish" button is a normal submit button which must have the name "_finish". The value of the button is not important to the controller but appears as the button text.

4

The reset button is handled directly by the browser without submitting the form to the server. Note that it does not clear the fields but resets them to the initial values they had when the page was first displayed.

5

The cancel button is handled in exactly the same way as for the SimpleFormController

We have seen how the navigation between pages is specified on each view, on the basis of the list of views set for the controller bean in the application context file. The actual processing of the navigation is done in the controller itself of course. However, the code to do so is in the AbstractWizardFormController superclass, and no code need to be added to your controller to make it work. In effect, navigation between pages is handled invisibly to your code in the same way that, in SimpleFormController, a submission of a form which fails validation gets redirected back to the form view which you having to write explicit code to make it so. You do have to write code to hand the final submission triggered by clicking the finish button however, and there is still the validation to consider (in sprMVCFull.controller.SurveyController):

package sprMVCFull.controller;
import org.springframework.web.servlet.mvc.AbstractWizardFormController;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.validation.BindException;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import sprMVCFull.model.Survey;

public class SurveyController extends AbstractWizardFormController
{
    public SurveyController()
    {
        setCommandClass(Survey.class);
    }

    protected ModelAndView processFinish(HttpServletRequest request, (1)
                                         HttpServletResponse response,
                                         Object command, BindException errors)
            throws Exception
    {
        Survey survey = (Survey) command ;
        return new ModelAndView("printCommand", "command", survey) ;
    }

    protected ModelAndView processCancel(HttpServletRequest request,
                                         HttpServletResponse response,
                                         Object command, BindException errors)
            throws Exception
    {
        return new ModelAndView("printCommand", "command",
                                "survey form submission cancelled") ;

    }

    protected void validatePage(Object command, Errors errors,       (2)
                                int page, boolean finish)
    {
        Survey survey = (Survey) command ;

        switch (page)                                                (3)
        {
        case 0:
            ValidationUtils.rejectIfEmptyOrWhitespace(
                errors,  "colour", "required.colour", "colour is required");
            ValidationUtils.rejectIfEmptyOrWhitespace(
                errors,  "food", "required.food", "food is required");
            break ;
        case 1:
            ValidationUtils.rejectIfEmptyOrWhitespace(
                errors,  "country", "required.country", "country is required");
            ValidationUtils.rejectIfEmptyOrWhitespace(
                errors,  "language", "required.language", "language is required");
            break ;
        case 2:
            ValidationUtils.rejectIfEmptyOrWhitespace(
                errors,  "film", "required.film", "film is required");
            ValidationUtils.rejectIfEmptyOrWhitespace(
                errors,  "singer", "required.singer", "singer is required");
            break ;
        default:
        }
        if (finish)                                                  (4)
        {
            if (!"ireland".equals(survey.getCountry().toLowerCase()))
                errors.reject("invalid.country",
                              "bad choice of favourite country (try \"Ireland\")!") ;
        }
    }
}
1

When the fully completed form is submitted (via the finish button), we handle it like any form submission.

2

In an AbstractWizardFormController, the only method called automatically to validate the model object is this validatePage method.

3

Validation is basically similar to SimpleFormController validation, except that, during the filling in of a wizard form, you only want to validate the data submitted on a page by page basis until the whole form is complete. This is done by switching on the page parameter of the validatePage method. This page parameter contains the page number of the page just submitted. If, on termination of this method, the errors collection is not empty, then the navigation requested will not take place but instead the same page will be redisplayed so that the user can correct his or her errors.

4

If the finish parameter is true, then this submission was due to the wizard being finished, and you now have the opportunity to do some global validation of the submitted model data (the example shown is not very good because it does not involve the contents of multiple input fields from different pages).

3.3. Preventing Duplicate Form Submission

In the (unmodified) SprMVCFull application, try the following: Go to the ProcessDetails form, fill it in and submit it. Then use the browser back button and submit the form a second time. As you can see, the second submit also works. In a real web application, this behaviour could lead to customers unintentionally buying more than one holiday, flight, book or other item. To prevent that we need to be able to detect that the submission is a duplicate submission and we need to be able to take action on that occurrence. In Spring there is support for both these requirements.

[Note]Note

A well designed web front end should prevent duplicate form submission for all those forms where duplicate form submission would be bad. Thus duplicate form submission for confirming an order should be stopped so that a user does not accidentally, for example, book the same flight twice. On the other hand, you should probably not stop duplicate submission of a simple search form: if you have a form used to search a catalog, for example, it is convenient to be able to enter details, execute the search, hit the back button, modify the details and search again. This is duplicate form submission but causes no harm as no important state is being updated on each submission. Therefore, for every form, you should decide whether you need to prevent duplicate submission or not, and have a clear explanation (and documentation) for why.

For the detection part we simply need to store some information across the form view display from the controller and the following request from the user that contains the form data to be submitted. We set up the information before returning the ModelAndView from the controller. When the resulting form is submitted by the user, we discard the information. Thus if a valid (first) form submission arrives, then the stored information will be there, can be discarded, and everything is fine. However, if a form submission comes in for which the stored information has already been discarded, then this must be a duplicate submission. The information stored can be anything so long as it cannot be confused with any other form. Spring simply uses the model itself. The only question remaining is where to store the information. It cannot be stored in the request object as that only lives from an initial request from the user, to the controller and back to the user. However we need the information to live from a response from the controller, to the user and back to the controller: For the first half of that interchange, we have one request object, and for the second one, a totally different, unrelated one. Hence we save the information in the session. This is done by adding the following lines to the controller bean definition in the controller-servlet.xml file:

<property name="sessionForm">                                        (1)
    <value>true</value>
</property>
<property name="synchronizeOnSession">                               (2)
    <value>true</value>
</property>
1

Store the form data object in the session for the duration of the form entry

2

Specify that two parallel submits associated with the same session must synchronize on the session. This stops race conditions in the unlikely situation where a user does a duplicate submission where the second submission, as far as the web application server is concerned, is actually in parallel with the first one.

[Note]Note

The above code is necessary to detect duplicate form submission for controllers that directly extend AbstractFormController or SimpleFormController. However, since wizard controllers, which extend AbstractWizardFormController, already need to use session to store the form object over multiple pages of the wizard, you do not need to set its sessionForm property to true (although setting its synchronizeOnSession property to true is a good idea).

In order to take action on a duplicate submission, we merely have to override the handleInvalidSubmit method of any form controller class. For example:

protected ModelAndView handleInvalidSubmit(HttpServletRequest request,
                                           HttpServletResponse response)
    throws Exception
{
    return new ModelAndView("printCommand",
                            "command",
                            "Warning: Duplicate Survey form submission attempt");
}

Such a handleInvalidSubmit method will work for wizard forms as well as simple forms. If you don't override this method, the default action is to process the duplicate submission as if it were not a duplicate.

3.4.  Preventing Hacking of Business Objects

Typically, one sets a business object as the model for a view to be presented to a user after execution of a controller. That view may contain a form or even just a parameterized command. Frequently, the intention is that only some of the fields of the business object should be updated from the view. For example, parts of a User bean may be presented in a HTML form to the user so that the user can update his or her preferences. If your User bean design includes a privileges property that defines the user's access privileges, then you certainly would not want the user to be able to set such a property from the user preference form. However, a malicious user can easily supply values for fields or properties that do not exist on the form but which he/she guesses or deduces might exist in the business object that is the model for the form. The default strategy for handling forms (in most web frameworks, not just Spring) is to copy all parameters in the request into corresponding properties (including nested properties) in the model bean. If you are not careful, you could end up with a very serious security flaw in your system that could allow a hacker entry into your system or the ability to crash your system at will.

One crude way to avoid this problem is to have a special form or value bean that just has a subset of the properties of your model bean, namely the properties you want to allow to be set, and use that instead as the model bean and, on receipt of this form bean, copy all its properties into corresponding properties of the true model bean. This strategy, however, results in a proliferation of trivial classes which must match their master model classes and have no real purpose other than trivial data transfer: Avoid this antipattern

Spring provides a much better mechanism for handling this problem: within any controller that extends BaseCommandController, and that includes all the command, form and wizard controllers, you can override the initBinder method to apply some initialisation to the Binder object that extracts the parameters from a servlet request and matches them up and assigns them to the corresponding properties of the model bean. This Binder object will be of class ServletRequestDataBinder for JSP/JSTL views, which has a setAllowedFields method which takes an argument which is an array of Strings which are the names of the properties of the model that the Binder is allowed to set.

We can modify our example application to see this in action. First we modify the PrintParamCommand to add an extra property that should not be set from any web form:

package sprMVCFull.model;

public class PrintParamCommand
{
    private String paramName = null;
    private String notAllowed = null; // Only to test setting of dissallowed fields

    public PrintParamCommand()
    {
    }

    public String getParamName()
    {
        return paramName;
    }

    public void setParamName(String paramName)
    {
        this.paramName = paramName;
    }

    public String getNotAllowed()
    {
        return notAllowed;
    }

    public void setNotAllowed(String notAllowed)
    {
        this.notAllowed = notAllowed;
    }

    public String toString()
    {
        return (notAllowed == null) ? paramName: paramName + " " + notAllowed;
    }
}

Next we modify the PrintParamController class to call the modified toString() method so that we can see both properties. We only need to change the line that returns the ModelAndView object to:

return new ModelAndView("printCommand", "command", (PrintParamCommand)command);

Finally we simulate a hacker setting the extra parameter: deploy and start the web application. Click on the PrintCommand(Link) link or the PrintCommand(Button) button. The application should go to the printCommand view and correctly show that only the paramName property has been set. Also, in the address field of the browser, you should see a line that looks something like "http://localhost:8080/printCommand.html?paramName=Button". To the end of that field, add the text (without quotes or extra spaces) "&notAllowed=hacked!" and enter this modified address. The result should show that the notAllowed property has indeed been set even though you had not intended it to be.

To fix it, add the following code to the PrintParamController class:

    private static final String[] ALLOWED_FIELDS = {"paramName"} ;

    protected void initBinder(HttpServletRequest request, ServletRequestDataBinder binder)
            throws Exception
    {
        binder.setAllowedFields(ALLOWED_FIELDS);
    }

Now if you try the same experiment, you should find that the notAllowed property is not set.

[Important]Important

You should always set the allowed fields for a controller with a model object. It might seem unnecessary if all the properties of the model object are supposed to be set by the form. However, at some stage later in the lifetime of the application, that model object may have other properties added to it that should not be vulnerable to setting by the form — and, at that point, it is very easy to overlook the necessity of adding the appropriate restriction.

References

[AppFuse] The AppFuse Project . Matt Raible. https://appfuse.dev.java.net/ .

[SRM] The Spring Reference Manual . http://www.springframework.org/docs/reference/index.html .

[SWS] The Spring Web Site . http://www.springframework.org/ .

[HM05] Rob Harrop and Jan Machacek. Pro Spring . Apress. 2005. 1590594614.

[JH04] Rod Johnson and Juergen Hoeller. J2EE Development without EJB . Wrox. 2004. 0-7645-5831-5.

[Johnson03] Rod Johnson. J2EE Design and Development . Wrox. 2003. 0-7645-4385-7.

[JHA05] Rod Johnson, Juergen Hoeller, Alef Arendsen, Thomas Risberg, and Dmitriy Kopylenko. Professional Java Development with the Spring Framework . Wrox. 2005. 0764574833. Due for publication 31st May 2005 .

[Raible04] Matt Raible. Spring Live . SourceBeat. 2004. 0974884375. The electronic version which can be purchased at http://www.sourcebeat.com/TitleAction.do?id=7 includes updates for a year. .

[WB05] Craig Walls and Ryan Breidenbach. Spring in Action . Manning Publications Co.. 2005. 1-932394-35-4.