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

Object Partners profile.

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 to Yanis Cancel reply

Your email address will not be published.

Related Blog Posts
Natively Compiled Java on Google App Engine
Google App Engine is a platform-as-a-service product that is marketed as a way to get your applications into the cloud without necessarily knowing all of the infrastructure bits and pieces to do so. Google App […]
Building Better Data Visualization Experiences: Part 2 of 2
If you don't have a Ph.D. in data science, the raw data might be difficult to comprehend. This is where data visualization comes in.
Unleashing Feature Flags onto Kafka Consumers
Feature flags are a tool to strategically enable or disable functionality at runtime. They are often used to drive different user experiences but can also be useful in real-time data systems. In this post, we’ll […]
A security model for developers
Software security is more important than ever, but developing secure applications is more confusing than ever. TLS, mTLS, RBAC, SAML, OAUTH, OWASP, GDPR, SASL, RSA, JWT, cookie, attack vector, DDoS, firewall, VPN, security groups, exploit, […]