Best way to validate decimal / double values with Spring

Spring’s validation support is widely used to validate objects. For example, it can validate models that are coming from user submitted forms. Validation is happening based on annotations that you can put on the fields in your model. These annotations are not part of Spring, external libraries must be used. The most common is the javax.validation package with the hibernate validator implementation.

The values to be validated could be decimal values. These can be represented by a number of different types. Probably the two most common types that pop into your mind are float and double. These could work okay, but because they are not exact numbers, just approximations, depending on the validation implementation, there could be some false results.

The best idea for validation is to use the BigDecimal type with the @DecimalMin and @DecimalMax validation annotations.

@DecimalMin(value = "0.1", inclusive = true)
@DecimalMax(value = "9.9", inclusive = true)
private BigDecimal measurement;

It might seem like a hassle to deal with values of this type, but actually it’s pretty straightforward. You just have to use method calls instead of numeric operators to add / subtract / etc. values.

 

How to create a custom Spring form errors tag

Spring has a tag library that helps you in building forms. It also has a tag called errors that lets you output validation errors in a formatted way.  However, this tag has some limitations. When multiple errors are displayed at the same time, you can only control the styling of the outside container and not the styling of each of the errors. So for example it is not possible to display the error messages as an unordered list.

This tutorial describes how to create a custom errors tag that has some extra customization attibutes compared to the basic Spring form errors tag.

The steps we will take include the following:

  • Creating a Tag Library with our custom tag definition in it.
  • Creating a custom class that specifies how our new tag will work.
  • Include the tag library in a sample JSP and use it to display errors.

Creating the Tag Library

A tag library definition is contained in a .tld file and also each tag has a class which contains the logic that results in some kind of output when we use the tag. We are using the basic Spring error tag’s definition as a starting point, but add two extra attributes to it. The added attributes are highlighted in the code below.

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
    <tlib-version>1.0</tlib-version>
    <short-name>Jtuts common tags</short-name>
    <uri>http://jtuts.com/taglib/tags</uri>
    <tag>
          <description>Renders field errors wrapped in a specified tag.</description>
          <name>errors</name>
          <tag-class>jtuts.tag.CustomSpringFormErrorsTag</tag-class>
          <body-content>JSP</body-content>
          <variable>
               <name-given>messages</name-given>
               <variable-class>java.util.List</variable-class>
          </variable>
          <attribute>
               <description>Path to errors object for data binding</description>
               <name>path</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Standard Attribute</description>
               <name>id</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>Enable/disable HTML escaping of rendered values.</description>
               <name>htmlEscape</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>Specifies the HTML element that is used to render the individual errors.</description>
               <name>innerElement</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
               <attribute>
               <description>Specifies the CSS class that the element has that is used to render the individual errors.</description>
               <name>innerElementCssClass</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>Delimiter for displaying multiple error messages. Defaults to the br tag.</description>
               <name>delimiter</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>Equivalent to "class" - HTML Optional Attribute</description>
               <name>cssClass</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>Equivalent to "style" - HTML Optional Attribute</description>
               <name>cssStyle</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Standard Attribute</description>
               <name>lang</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Standard Attribute</description>
               <name>title</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Standard Attribute</description>
               <name>dir</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Standard Attribute</description>
               <name>tabindex</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onclick</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>ondblclick</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onmousedown</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onmouseup</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onmouseover</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onmousemove</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onmouseout</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onkeypress</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onkeyup</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>HTML Event Attribute</description>
               <name>onkeydown</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <attribute>
               <description>Specifies the HTML element that is used to render the enclosing errors.</description>
               <name>element</name>
               <required>false</required>
               <rtexprvalue>true</rtexprvalue>
          </attribute>
          <dynamic-attributes>true</dynamic-attributes>
     </tag>
</taglib>

One of the new attributes is innerElement. It can be used to specify an HTML element that will be used to wrap every single error message individually. The other new attribute is the innerElementCssClass. This specifies the CSS class or classes that will be applied to each of the inner wrapping elements.

The other attributes are just copied from the original Spring form errors tag.

Creating the tag class

The tag class specifies what kind of output will the tag produce based on it’s attribute values. Here is the full class for the new tag.

package jtuts.tag;

import javax.servlet.jsp.JspException;

import org.springframework.util.ObjectUtils;
import org.springframework.web.servlet.tags.form.ErrorsTag;
import org.springframework.web.servlet.tags.form.TagWriter;


/**
 * The Spring form:errors tag extended with attributes which allow to use an inner element with a
 * css class which wraps every individual error item.
 */
public class CustomSpringFormErrorsTag extends ErrorsTag {

    private static final long serialVersionUID = 24723583545L;

    private String innerElement;
    private String innerElementCssClass;

    @Override
    protected void renderDefaultContent(TagWriter tagWriter) throws JspException {
        tagWriter.startTag(getElement());
        writeDefaultAttributes(tagWriter);
        String delimiter = ObjectUtils.getDisplayString(evaluate("delimiter", getDelimiter()));
        String[] errorMessages = getBindStatus().getErrorMessages();

        for (int i = 0; i < errorMessages.length; i++) {
            String errorMessage = errorMessages[i];
            if (i > 0) {
                tagWriter.appendValue(delimiter);
            }

            if (innerElement != null)
                tagWriter.appendValue(startTagForInnerElement());

            tagWriter.appendValue(getDisplayString(errorMessage));

            if (innerElement != null)
                tagWriter.appendValue(endTagForInnerElement());
        }
        tagWriter.endTag();
    }

    private String startTagForInnerElement() {
        StringBuilder stringBuilder = new StringBuilder("<" + innerElement);

        if (innerElementCssClass != null)
            stringBuilder.append(" class=\"" + innerElementCssClass + "\"");

        stringBuilder.append(">");

        return stringBuilder.toString();
    }

    private String endTagForInnerElement() {
        return "</" + innerElement + ">";
    }

    public String getInnerElement() {
        return innerElement;
    }

    public void setInnerElement(String innerElement) {
        this.innerElement = innerElement;
    }

    public String getInnerElementCssClass() {
        return innerElementCssClass;
    }

    public void setInnerElementCssClass(String innerElementCssClass) {
        this.innerElementCssClass = innerElementCssClass;
    }

}

You can see that it extends the basic error tag (org.springframework.web.servlet.tags.form.ErrorsTag).  We override the renderDefaultContent method that creates the output of the tag. Anything that you pass to this method will be appended to the output produced when someone uses this tag.

As you can see the extra logic that we have added is pretty simple. We just print an opening and closing tag around each error if the user provided the innerElement attribute and we also add the CSS classes to the opening tag if the innerElementCssClass attribute is specified.

We have also set up some extra methods, but they are just there to simplify the renderDefaultContent method.

The outer element that encloses all the errors is just displayed the same way as it was in the super implementation (startTag, endTag, writeDefaultAttributes calls).

Using the tag

For demonstration purposes I have created a very simple registration form that uses the newly created tag.

<%@ page contentType="text/html; charset=UTF-8" %>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib prefix="jtuts" uri="/WEB-INF/tags/jtuts.tld" %>

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>Registration form with a custom errors tag.</title>
    <style>
        .error-message { color: #CC3344; }
    </style>
</head>
<body>
    <h1>Register</h1>
    <form:form commandName="userForm" action="${pageContext.request.contextPath}/register" method="post">
        <jtuts:errors path="*" element="ul" innerElement="li" delimiter="" cssClass="error-message" />
        <table>
            <tr>
                <td>First Name:</td>
                <td><form:input path="firstName" /></td>
            </tr>
            <tr>
                <td>Last Name:</td>
                <td><form:input path="lastName" /></td>
            </tr>
            <tr>
                <td>User Name:</td>
                <td><form:input path="userName" /></td>
            </tr>
            <tr>
                <td>Email Address:</td>
                <td><form:input path="emailAddress" /></td>
            </tr>
            <tr>
                <td colspan="2">
                    <input type="submit" value="Save User" />
                </td>
            </tr>
        </table>
    </form:form>
</body>
</html>

Notice the taglib definition with the taglib directive and the usage of our new jtuts:errors tag.

The POM, the Controller, the form class and validation

For the sake of completeness I also show you the remaining parts of the sample application.

The pom.xml looks like this:

<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>how-to-create-a-custom-spring-form-errors-tag-20150126</groupId>
    <artifactId>how-to-create-a-custom-spring-form-errors-tag-20150126</artifactId>
    <packaging>war</packaging>
    <version>0.0.1-SNAPSHOT</version>
    <name>how-to-create-a-custom-spring-form-errors-tag-20150126 Maven Webapp</name>
    <url>http://maven.apache.org</url>

    <properties>
        <spring.version>4.1.4.RELEASE</spring.version>
        <javax.validation.validation-api.version>1.1.0.Final</javax.validation.validation-api.version>
        <hibernate-validator.version>5.1.3.Final</hibernate-validator.version>
        <servlet-api.version>2.5</servlet-api.version>
        <jsp-api.version>2.2</jsp-api.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>${javax.validation.validation-api.version}</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>${hibernate-validator.version}</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>${servlet-api.version}</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>${jsp-api.version}</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <path>/jtuts</path>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.1</version>
                <configuration>
                    <source>1.7</source>
                    <target>1.7</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

I just included the minimum dependencies required for this project. I also used the Tomcat Maven plugin to be able to deploy the app easily.

The Controller class:

package jtuts.controller;

import javax.validation.Valid;

import jtuts.form.UserForm;

import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;


@Controller
public class RegisterController {
 
    @RequestMapping(value="register", method = RequestMethod.GET) 
    public ModelAndView registerGet() {
        ModelAndView model = new ModelAndView();
        
        model.setViewName("register");
        model.addObject("userForm", new UserForm());
        
        return model;
    }

    @RequestMapping(value="register", method = RequestMethod.POST) 
    public ModelAndView registerPost(@Valid @ModelAttribute("userForm") UserForm userForm, BindingResult result) {
        ModelAndView model = new ModelAndView();
        
        model.setViewName("register");
        
        return model;
    }
}

In the controller we are just running the form through some basic validation. The validation rules are set up in the form class which looks like this:

package jtuts.form;

import org.hibernate.validator.constraints.NotEmpty;

public class UserForm {

    @NotEmpty(message="The first name may not be empty.")
    private String firstName;
    
    @NotEmpty(message="The last name may not be empty.")
    private String lastName;
    
    @NotEmpty(message="The user name may not be empty.")
    private String userName;
    
    @NotEmpty(message="The email address may not be empty.")
    private String emailAddress;

    
    public String getFirstName() {
        return firstName;
    }

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

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getEmailAddress() {
        return emailAddress;
    }

    public void setEmailAddress(String emailAddress) {
        this.emailAddress = emailAddress;
    }
}

The Spring configuration for the validator to work:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
         
    <context:component-scan base-package="jtuts" />
    <mvc:annotation-driven validator="validator" />
  
    <bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
  
</beans>

The working application

This is what the working application looks like when we try to submit an empty form. Notice that we are displaying the error messages as an unordered list which was not possible with the original version of the errors tag.

how-to-create-a-custom-spring-form-errors-tag-20150126-registration-form

Download

You can download the whole working source code from here.

 

Validating dates in spring form objects

Validating dates on a form managed by Spring is a bit more tricky than checking a simple string attribute for emptiness.

You can use the org.springframework.format.annotation.DateTimeFormat annotation on the field that you would like to validate. This annotation has an attribute called pattern that allows you to set a the pattern to validate against. Example usage of the annotation:

public class PostForm {
    
    @DateTimeFormat(pattern = "yyyy-MM-dd")
    @NotNull(message = "Please provide a date.")
    private Date date;
    
    private String contents;
    
    // getters and setters...
}

So far it was similar to using other validation annotations. However, you might notice that this annotation does not have a message attribute, so you can’t specify what message should be printed. Let’s see what error message we get back if we use the annotation. I used 2014-99-11 as the date and yyyy-MM-dd as the pattern to validate against.

Failed to convert property value of type java.lang.String to 
required type java.util.Date for property date; 
nested exception is org.springframework.core.convert.ConversionFailedException: 
Failed to convert from type java.lang.String to type @org.springframework.format.annotation.DateTimeFormat 
@javax.validation.constraints.NotNull java.util.Date for value 2014-99-11; 
nested exception is java.lang.IllegalArgumentException: Unable to parse 2014-99-11

You can see that this results in not too nicely looking error message that explains what is the problem in detail…but users may expect a bit more concise message.

To override the default message, you need to have a messageSource and in that a message with a specific key that corresponds to the validated attribute.

Our message source is set up like this in the application context:

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="basenames">
        <value>classpath:i18n/messages</value>
    </property>
</bean>

Of course we have a messages.properties file in the i18n folder under our classpath. Our messages.properties file looks like this:

typeMismatch.postForm.date=Please enter a valid date.

The name of the message key is very important. It is structured like this: {ValidationClass}.{modelObjectName}.{field}. You don’t necessarily need to specify the whole key, because the resolving happens in the following way in our case:

  1. First the typeMismatch.postForm.date key is looked up.
  2. If it is not found, the typeMismatch.postForm key is looked up.
  3. If it is not found, the typeMismatch key is looked up.
  4. If it is not found, the default message will be rendered.

Download

You can download the source code of an example application here.