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.

Apostrophes stripped, parameters not evaluated when using the spring:message tag

The <spring:message> tag is often used in JPSs to output localized messages that are read from property files. In most cases it outputs the expected message without problem, but if you have apostrophe(s) in a message, there could be some unexpected results. The following could happen:

  • The apostrophes in the message are stipped, not visible.
  • Some of the parameters in the messages don’t get replaced by the arguments passed in. As a result the paramter placeholder is displayed.

Let’s see some examples that demonstrate this problem. First, we create a ReloadableResourceBundleMessageSource object that will let us access the messages in our property files. This object is created like this in the application context xml:

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

We also create a property file that will contain the messages and we output them using the <spring:message> tag in our JSP files.  Our property file is placed here to match the configuration of the messageSource bean:

/src/main/resources/i18n/messages.properties

Now let’s see some example properties and the result of them. Our properties are:

home.paragraph1=There is no apostrophe in this message.
home.paragraph2=There is no apostrophe in this message, but it has {0} and {1} parameters.
home.paragraph3=Look it's an apostrophe, but no params.
home.paragraph4=Look it's an apostrophe, and it has {0} and {1} params.
home.paragraph5=Look it's it's two apostrophes, and it has {0} and {1} params.
home.paragraph6=Look it's two apostrophes, {0} but they're between the parameters {1}.

In the JSP we diplay them like this:

<p><spring:message code="home.paragraph1"/></p>
<p><spring:message code="home.paragraph2" arguments="P1,P2"/></p>
<p><spring:message code="home.paragraph3"/></p>
<p><spring:message code="home.paragraph4" arguments="P1,P2"/></p>
<p><spring:message code="home.paragraph5" arguments="P1,P2"/></p>
<p><spring:message code="home.paragraph6" arguments="P1,P2"/></p>

And the result is:

There is no apostrophe in this message.
There is no apostrophe in this message, but it has P1 and P2 parameters.
Look it's an apostrophe, but no params.
Look its an apostrophe, and it has {0} and {1} params.
Look its its two apostrophes, and it has P1 and P2 params.
Look its two apostrophes, {0} but theyre between the parameters P2.

As you can see, when apostrophes and parameters are both present, the apostrophes always disappear, but only some of the parameters are not evaluated.

The logic behind the magic

Before a message is printed, the ReloadableResourceBundleMessageSource object that we use passes messages through a call to the MessageFormat.format() method. This substitutes the arguments and formats them property. During this call the apostrophe has a special meaning, it encloses parts of the string that should not be evaluated. Also, after parsing the string all single apostrophes are removed.

If you look at the previous examples you can see that where parameters are present the apostrophes really disapear, and if a parameter is after an “opening” apostrophe it is not evaluated.

However, note that the third example has apostrophe in it but it is not removed from the string when it is printed. This is because for optimization reasons only messages that contain parameters are passed through the formatter method. The rest of the messages are just printed as is.

Using apostrophes and parameters together, the right way

If you would like for an apostrophe to display in a string where there are also parameters, you must write double apostrophes. They will be converted to single ones, and parameters will not be harmed.

So let’s take our fourth example and modify it to get the expected result. We just have to add an extra apostrophe:

home.paragraph7=Look it''s an apostrophe, and it has {0} and {1} params.
Look it's an apostrophe, and it has P1 and P2 params.

So sometimes you have to add double apostrophes, and sometimes not. Won’t this cause a confusion?

Yes, probably it will. But there is a way to force the ReloadableResourceBundleMessageSource object to work the same way every time. You can set a parameter on it that will cause all messages to go through the MessageFormat.format() method. So this way you can be sure that you can use double apostrophes every time you need to print an apostrophe. To enable this option, modify the bean definition like this:

<bean id="messageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
    <property name="defaultEncoding" value="UTF-8"/>
    <property name="alwaysUseMessageFormat" value="true"/>
    <property name="basenames">
        <list>
            <value>classpath:i18n/messages</value>
        </list>
    </property>
</bean>

Download source

You can download the source files used in this project here.