Jan 19, 2012

Creating a Spring @StrictDateTimeFormat Annotation

Spring Formatters and Converters make it easy to annotate fields for conversion from Objects to Strings, and are especially useful in web apps. But there is no easy or straightforward way to strictly validate the String before parsing into an object, without creating a custom Formatter. Here is a reusable solution that uses a RegexParserDecorator to decorate any Spring Formatter to apply a regex pattern, in turn creating a @StrictDateTimeFormat annotation as an example implementation.

A little background: Spring 3.0 brought the Converter and Formatter framework with a concise @DateTimeFormat annotation, simplifying the Date to Object conversion that previously took custom binders or other wiring code. With @DateTimeFormat you can easily supply a String pattern used to parse and print a Date (or joda DateTime) object. However, the annotation does not strictly validate the String before converting to a Date. For instance, supplying a MM/dd/yyyy pattern does NOT enforce a 4 digit year. Instead a 2 digit year will be accepted and parsed using the SimpleDateFormat rules. Similar loose checking goes for 1 digit days and months, and also the slash character used as a separator. It would be easy to just throw the @Pattern tag onto your Date field except that @Pattern is only allowed on String fields. Combining @Pattern and @DateTimeFormat is what drove the creation of @StrictDateTimeFormat:

//sample usage using defaults for regex and pattern
@StrictDateTimeFormat
private DateTime birthday;

Read more below for a discussion and snippets of code, and the entire codeset with comments is available on github.

The RegexParserDecorator: The first step is to create a Regex Parser class that will apply a regex pattern to validate a String for us. Creating this as a Decorator gives the added benefit that you can easily wrap any Spring Formatter to apply Regex patterns. The constructor takes a Parser to wrap and a regex to apply; and the parse method first validates against the regex before passing onto the decorated Parser:

public RegexParserDecorator(Parser parser, String regex) {
	this.parser = parser;
	this.regexPattern = Pattern.compile(regex);
}
public T parse(String text, Locale locale) throws ParseException {
	if (!regexPattern.matcher(text).matches()) {
		throw new IllegalArgumentException("Text does not match regex: " + text);
	}
	return parser.parse(text, locale);
}

The @StrictDateTimeFormat Annotation: Next step is to setup the annotation interface class. It is very similar to DateTimeFormat but adds the field to hold a regex. The default regex allows 1 or 2 digit days and months, requires a forward slash as the separator, and enforces a 4 digit year. This can be easily overriden when applying the annotation to a field by supplying your own (regex=””, pattern=””) extension. A pattern is still required so make sure your pattern and regex are paired appropriately.

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface StrictDateTimeFormat {
    public static final String REGEX_DEFAULT = "^(0?[1-9]|1[012])/(0?[1-9]|[12][0-9]|3[01])/dddd$";
    public static final String PATTERN_DEFAULT = "MM/dd/yyyy";
    String regex() default REGEX_DEFAULT;
    String pattern() default PATTERN_DEFAULT;
}

Spring then requires a StrictDateTimeFormatAnnotationFormatterFactory to wire the annotation with the parser. Nothing fancy here as it borrows heavily upon Spring’s own JodaDateTimeFormatAnnotationFormatterFactory. The getParser method applies our regex to the DateTimeFormat functionality:

public class StrictDateTimeFormatAnnotationFormatterFactory implements
		AnnotationFormatterFactory {
...
	public Parser getParser(StrictDateTimeFormat annotation, Class<!--?--> fieldType) {
		DateTimeParser parser = new DateTimeParser(forPattern(annotation.pattern()));
		return new RegexParserDecorator(parser, annotation.regex());
	}
	private DateTimeFormatter forPattern(String pattern) {
		return org.joda.time.format.DateTimeFormat.forPattern(pattern);
	}
...

Hooking it all together: Here is the snippet from my applicationConfig.xml showing how the annotation is registered into Spring:

&lt;mvc:annotation-driven conversion-service="myConversionService" /&gt;
&lt;bean id="myConversionService" class="org.springframework.format.support.FormattingConversionServiceFactoryBean"&gt;
	&lt;property name="formatters"&gt;
		&lt;list&gt;
			&lt;bean class="jeffsheets.util.format.StrictDateTimeFormatAnnotationFormatterFactory" /&gt;
		&lt;/list&gt;
	&lt;/property&gt;
&lt;/bean&gt;

Hopefully this information is helpful in creating a reusable regex validating DateTime formatter for use in your own web application.

About the Author

Jeff Sheets profile.

Jeff Sheets

VP - Technology

Jeff has developed Java, Groovy, Grails, and Javascript web apps for industries as varied as Defense, Energy, Weather, Insurance, and Telecom. He is a co-organizer of the Omaha Java Users Group. Jeff has worked on Grails projects since the Grails 1.3.x days, and has experience with production Groovy code as well as Spock tests and Gradle builds. His latest focus has been on AngularJS and Spring Boot applications using JHipster. Jeff also enjoys volunteering at local CoderDojo events to teach programming to our next generation.

One thought on “Creating a Spring @StrictDateTimeFormat Annotation

  1. Stephen Skinner says:

    Hi,

    Excellent article, I’ve implemented this for our web app, much appreciated.

    One thing I found was missing was adding i18n messages as the annotation doesnt follow the standard hibernate annotation message format, it would be an excellent addition to add this at the bottom of your article.

    I found how to at the bottom of this article:
    http://jtuts.com/2014/11/09/validating-dates-in-spring-form-objects/

    Basically need format similar to:
    typeMismatch.playerDetailsModel.player.lastChampionshipGame.footballDate

    Many thanks,
    Stephen

  2. Yanis says:

    Hi Jeff, I tried your solution using Java classes for Spring configuration and I cannot figure out, how the second part i.e. the webflow configuration should be done. I also noticed that the DefaultConversionService of Spring does not have any arguments, while in the xml it is written . I would appreciate if you could give me any hint.

    Cheers,
    Yanis

    1. Jeff Sheets says:

      Hi Yanis,
      Unfortunately the issue could be many things. Perhaps you could share a github code gist and I could take a look there to provide comments?
      — Jeff

Leave a Reply

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

Related Blog Posts
Android Development for iOS Developers
Android development has greatly improved since the early days. Maybe you tried it out when Android development was done in Eclipse, emulators were slow and buggy, and Java was the required language. Things have changed […]
Add a custom object to your Liquibase diff
Adding a custom object to your liquibase diff is a pretty simple two step process. Create an implementation of DatabaseObject Create an implementation of SnapshotGenerator In my case I wanted to add tracking of Stored […]
Keeping Secrets Out of Terraform State
There are many instances where you will want to create resources via Terraform with secrets that you just don’t want anyone to see. These could be IAM credentials, certificates, RDS DB credentials, etc. One problem […]
Validating Terraform Plans using Open Policy Agent
When developing infrastructure as code using terraform, it can be difficult to test and validate changes without executing the code against a real environment. The feedback loop between writing a line of code and understanding […]