title: How do I display a user friendly error when Spring is unable to validate a model's java.util.Date field?
body: <p>I'm using Spring 3.1.2.RELEASE.  I want to display an error message on my JSP if my date field isn't formatted properly.  I thought I'd followed all the right steps.  I binder a converter in my controller …</p>
  
  <pre><code>@InitBinder
  public void initBinder(final WebDataBinder binder) {
      final DateFormat dateFormat = new SimpleDateFormat(Contract.DATE_FORMAT);
      dateFormat.setLenient(false);
  
      // true passed to CustomDateEditor constructor means convert empty String to null
      binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
  ...
  }
  </code></pre>
  
  <p>I have included these error messages in my messages.properties file (included in the Spring application context)</p>
  
  <pre><code>typeMismatch.activationDate=The activation date format should be of the form MM/dd/yyyy
  typeMismatch.sampleUserForm.activationDate=The activation date format should be of the form MM/dd/yyyy
  </code></pre>
  
  <p>Here's the model I'm using:</p>
  
  <pre><code>public class SampleUserForm
  {
  
      private String userId;
      private String firstName;
      private String middleName;
      private String lastName;
      private String username;
      private String url;
      private String password;
      private String confirmPassword;
      private State state;
      private java.util.Date activationDate;
      private java.util.Date expirationDate;
      private List&lt;Product&gt; products;
      private Set&lt;Role&gt; roles = new HashSet&lt;Role&gt;();
  </code></pre>
  
  <p>And here is the error message I receive when I submit my form with a poorly-formatted date …</p>
  
  <pre><code>org.springframework.validation.BindException: org.springframework.validation.BeanPropertyBindingResult: 1 errors
  Field error in object 'sampleUserForm' on field 'activationDate': rejected value [1900]; codes [typeMismatch.sampleUserForm.activationDate,typeMismatch.activationDate,typeMismatch.java.util.Date,typeMismatch]; arguments [org.springframework.context.support.DefaultMessageSourceResolvable: codes [sampleUserForm.activationDate,activationDate]; arguments []; default message [activationDate]]; default message [Failed to convert property value of type 'java.lang.String' to required type 'java.util.Date' for property 'activationDate'; nested exception is java.lang.IllegalArgumentException: Could not parse date: Unparseable date: "1900"]
      org.springframework.web.method.annotation.ModelAttributeMethodProcessor.resolveArgument(ModelAttributeMethodProcessor.java:111)
  org.springframework.web.method.support.HandlerMethodArgumentResolverComposite.resolveArgument(HandlerMethodArgumentResolverComposite.java:75)
      org.springframework.web.method.support.InvocableHandlerMethod.getMethodArgumentValues(InvocableHandlerMethod.java:156)
      org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:117)
  org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:96)
  org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandlerMethod(RequestMappingHandlerAdapter.java:617)
      org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:578)
      org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
      org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:923)
      org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:852)
      org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:882)
      org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789)
      javax.servlet.http.HttpServlet.service(HttpServlet.java:754)
      javax.servlet.http.HttpServlet.service(HttpServlet.java:847)
      org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:330)
      org.springframework.security.web.access.intercept.FilterSecurityInterceptor.invoke(FilterSecurityInterceptor.java:118)
      org.springframework.security.web.access.intercept.FilterSecurityInterceptor.doFilter(FilterSecurityInterceptor.java:84)
      org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
      org.springframework.security.web.access.ExceptionTranslationFilter.doFilter(ExceptionTranslationFilter.java:113)
      org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
      org.springframework.security.web.session.SessionManagementFilter.doFilter(SessionManagementFilter.java:103)
      org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
      org.springframework.security.web.authentication.AnonymousAuthenticationFilter.doFilter(AnonymousAuthenticationFilter.java:113)
      org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
  org.springframework.security.web.servletapi.SecurityContextHolderAwareRequestFilter.doFilter(SecurityContextHolderAwareRequestFilter.java:54)
      org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
      org.springframework.security.web.savedrequest.RequestCacheAwareFilter.doFilter(RequestCacheAwareFilter.java:45)
      org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
  org.springframework.security.web.authentication.AbstractAuthenticationProcessingFilter.doFilter(AbstractAuthenticationProcessingFilter.java:183)
      org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
      org.springframework.security.web.authentication.logout.LogoutFilter.doFilter(LogoutFilter.java:105)
      org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
      org.springframework.security.web.context.SecurityContextPersistenceFilter.doFilter(SecurityContextPersistenceFilter.java:87)
      org.springframework.security.web.FilterChainProxy$VirtualFilterChain.doFilter(FilterChainProxy.java:342)
      org.springframework.security.web.FilterChainProxy.doFilterInternal(FilterChainProxy.java:192)
      org.springframework.security.web.FilterChainProxy.doFilter(FilterChainProxy.java:160)
      org.springframework.web.filter.DelegatingFilterProxy.invokeDelegate(DelegatingFilterProxy.java:346)
      org.springframework.web.filter.DelegatingFilterProxy.doFilter(DelegatingFilterProxy.java:259)
  </code></pre>
  
  <p>What else do I need to do/check to trap for a badly-formatted date?  I would prefer to display a friendly error message to the user rather than the page gracelessly dying.</p>
  
  <p>Here is the controller method that is supposed to handle the form.  Notice I have already included a BindingResult.</p>
  
  <pre><code>@RequestMapping(value = "/save", method = RequestMethod.POST)
  public ModelAndView save(final HttpServletRequest request,
                           final SampleUserForm sampleUserForm,
                           final Model model,
                           final BindingResult result) 
  {
      String nextPage = "sampleusers/add";
      m_sampleUserFormValidator.validate(sampleUserForm, result);
      if (!result.hasErrors())
      {
         ... process the model and determine the next page ...
      }   // if 
  
      return new ModelAndView(nextPage);
  }
  </code></pre>