Java Ninja Chronicles By Norris Shelton

Things I learned in the pursuit of code

Today, we are going to look at an example of Springframework MVC with form validation via JSR-303 annotations. We will be using the hibernate validations implementation of JSR-303.

The pom.xml will look like. The spring specific dependencies are highlighted.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>norris.shelton</groupId>
    <artifactId>spring3MvcValidation</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <name>Spring3 MVC Validation</name>

    <properties>
        <project.build.sourceEncoding>Cp1252</project.build.sourceEncoding>
        <spring.version>3.0.5.RELEASE</spring.version>
        <slf4j.version>1.6.1</slf4j.version>
    </properties>

    <dependencies>
        <!--note Servlet, jsp &amp; jstl - almost all provided-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>jstl</artifactId>
            <version>1.2</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>4.0.2.GA</version>
        </dependency>

        <!--hijack log4j-->
        <!--NOTE Saying it is provided so it will not be packaged in my builds if a dependency is using it-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>log4j-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.16</version>
            <scope>provided</scope>
        </dependency>

        <!--hijack commons logging-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>jcl-over-slf4j</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.1.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>0.9.27</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>${slf4j.version}</version>
        </dependency>
    </dependencies>

    <build>
        <finalName>spring3MvcValidation</finalName>
    </build>
</project>

The web.xml has to have the following to enable the spring dispatcher.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
         version="2.5">
    <display-name>Spring3 MVC Validation</display-name>
    <description>Spring3 MVC Validation</description>

    <!--
        Spring MVC dispatcher - defaults to /WEB-INF/SERVLETNAME-servlet.xml for the spring mvc context

        Central dispatcher for HTTP request handlers/controllers, e.g. for web UI controllers or HTTP-based remote
        service exporters. Dispatches to registered handlers for processing a web request, providing convenient mapping
        and exception handling facilities.
    -->
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <!-- /WEB-INF/springmvc-servlet.xml will be the spring mvc context file -->
        <servlet-name>springmvc</servlet-name>
        <url-pattern>*.do</url-pattern>
    </servlet-mapping>
</web-app>

The spring web context is in springmvc-servlet.xml (per the name of the spring mvc dispatcher in the web.xml). Nothing special here. It scans for spring components, says it is annotation driven. There is also a view resolver.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
                           http://www.springframework.org/schema/mvc     http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">

    <!--
        Scans the classpath for annotated components that will be auto-registered as Spring beans. By default, the
        Spring-provided @Component, @Repository, @Service, and @Controller stereotypes will be detected.

        This tag implies the effects of the 'annotation-config' tag, activating @Required, @Autowired, @PostConstruct,
        @PreDestroy, @Resource, @PersistenceContext and @PersistenceUnit annotations in the component classes, which is
        usually desired for autodetected components (without external configuration).

        A component scan results in new bean definition being registered; Spring's PropertyPlaceholderConfigurer will
        apply to those bean definitions just like to regular bean definitions, but it won't apply to the component scan
        settings themselves.
    -->
    <context:component-scan base-package="norris.shelton.spring3.mvc.validation"/>

    <!--
        This tag registers the DefaultAnnotationHandlerMapping and AnnotationMethodHandlerAdapter beans that are
        required for Spring MVC to dispatch requests to @Controllers.
    -->
    <mvc:annotation-driven/>

    <!--
        Resolves symbolic view names to URLs, without explicit mapping definition. This is useful if your symbolic names
        match the names of your view resources in a straightforward manner (i.e. the symbolic name is the unique part
        of the resource's filename), without the need for a dedicated mapping to be defined for each view.
        TODO: Why doesn't this get created and defaulted with these default values by <mvc:annotation-driven/>
    -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:viewClass="org.springframework.web.servlet.view.JstlView"
          p:prefix="/WEB-INF/jsp/" 
          p:suffix=".jsp"/>
</beans>

The controller is a fairly normal spring mvc annotation driven controller.

package norris.shelton.spring3.mvc.validation;


import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.validation.Valid;


/**
 * Validating Spring MVC controller.
 * @author Norris Shelton
 */
@Controller
public class NameController {

    /**
     * Handles gets of the first name form.
     * @param model spring-provided model object
     * @return the name of the jsp page to resolve
     */
    @RequestMapping(value = "/firstName", method = RequestMethod.GET)
    public String getFirstName(Model model) {
        model.addAttribute("firstNameModel", new FirstNameModel());
        return "firstNameForm";
    }

    /**
     * @param firstNameModel         first name data model
     * @param firstNameBindingResult validator binding result for the first name model
     * @return model and view object
     */
    @RequestMapping(value = "/firstName", method = RequestMethod.POST)
    public String postFirstName(@Valid FirstNameModel firstNameModel, BindingResult firstNameBindingResult) {
        if (firstNameBindingResult.hasErrors()) {
            return "firstNameForm";
        } else {
            return "redirect:/lastName.do";
        }
    }

    /**
     * Handles gets of the last name form.
     * @param model spring-provided model object
     * @return the name of the jsp page to resolve
     */
    @RequestMapping(value = "/lastName", method = RequestMethod.GET)
    public String getLastName(Model model) {
        model.addAttribute("lastNameModel", new LastNameModel());
        return "lastNameForm";
    }

    /**
     * @param lastNameModel         first name data model
     * @param lastNameBindingResult validator binding result for the first name model
     * @return model and view object
     */
    @RequestMapping(value = "/lastName", method = RequestMethod.POST)
    public String postLastName(@Valid LastNameModel lastNameModel, BindingResult lastNameBindingResult) {
        if (lastNameBindingResult.hasErrors()) {
            return "lastNameForm";
        } else {
            return "redirect:/firstName.do";
        }
    }
}

I have two controller methods per form. The first method I use is the getter method. Basically, it says that if the form is requested via a GET, then the form is being requested for the first time. I want to create the model object, then display the view. This is necessary because the spring form tags have to be told the name of the model class. This model has to exist when the form is rendered.

    @RequestMapping(value = "/firstName", method = RequestMethod.GET)
    public String getFirstName(Model model) {
        model.addAttribute("firstNameModel", new FirstNameModel());
        return "firstNameForm";
    }

The second method I use is when the form is posted. This is where the data on the form is sent. This is what triggers the validation. If the binding result object has errors, then go back to the previous form to display the errors, else redirect to the next url. I had to redirect because I had trouble getting it to re-enter the controller.

    @RequestMapping(value = "/firstName", method = RequestMethod.POST)
    public String postFirstName(@Valid FirstNameModel firstNameModel, BindingResult firstNameBindingResult) {
        if (firstNameBindingResult.hasErrors()) {
            return "firstNameForm";
        } else {
            return "redirect:/lastName.do";
        }
    }

@Valid indicates that this model needs validation. Also, there BindingResult needs to be directly after the model validated. This is because if you have multiple models being validated, then you could access multiple binding result objects. In this case, I don’t care what the messages are.

Still very simple and straight forward. Here is what one of the models looks like. The only things out of the ordinary are the annotations on line 11 and 12. I say that the property is required and it must have a length between 2 and 64.

package norris.shelton.spring3.mvc.validation;


import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;


/** @author Norris Shelton */
public class FirstNameModel {

    @NotNull
    @Size(min = 2, max = 64)
    private String firstName;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }
}

The view for this is also very simple. It uses 3 spring form tags. The label tag generates a HTML label. The input field creates an input form. The “firstName” property is linked to this vield in the “firstNameModel”. The errors tag displays any errors for the firstName property in the firstNameModel. You can also say path=”*” to display all error messages.

<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<form:form action="firstName.do" method="post" modelAttribute="firstNameModel">
    <form:label path="firstName">
        First Name:<form:input path="firstName"/> <form:errors path="firstName" cssStyle="color:red;"/>
    </form:label>
    <input type="submit" value="Submit"/>
</form:form>

The entire source code is available here.

February 28th, 2011

Posted In: Spring

2 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

WP to LinkedIn Auto Publish Powered By : XYZScripts.com