title: Spring MVC and JSR 303 custom validation with custom resource bundle
body: <p>I know this has been asked thousands of times, but this really is a nightmare. I've been trying to figure out for days how to configure Hibernate Validator to use my property resource bundle and I still cannot get a satisfying result...</p>
  
  <p>Here is a part of my web context:</p>
  
  <pre><code>    &lt;!-- i18n handling --&gt;
      &lt;bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource"&gt;
              &lt;property name="basename" value="i18n.eSporx" /&gt;
      &lt;/bean&gt;
  </code></pre>
  
  <p>Here is a part of my application context:</p>
  
  <pre><code>    &lt;bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"&gt;
              &lt;property name="validationMessageSource" ref="messageSource"/&gt;
      &lt;/bean&gt;
  </code></pre>
  
  <p>And finally, my (class-level cross-field) custom constraint and its validator:</p>
  
  <pre><code>@Documented
  @Target(TYPE)
  @Retention(RUNTIME)
  @Constraint(validatedBy = CrossDateConstraintsValidator.class)
  public @interface CrossDateConstraints {
  
      String message() default "crossdate.validation.error";
  
      Class&lt;?&gt;[] groups() default {};
  
      Class&lt;? extends Payload&gt;[] payload() default {};
  
      String startDateFieldName() default "startDate";
  
      String endDateFieldName() default "endDate";
  }
  
  
  @Component
  @Scope(SCOPE_PROTOTYPE)
  public class CrossDateConstraintsValidator implements ConstraintValidator&lt;CrossDateConstraints, Object&gt; {
  
      private String startDateField;
      private String endDateField;
      private String message;
  
      @Override
      public void initialize(final CrossDateConstraints constraintAnnotation) {
          startDateField = constraintAnnotation.startDateFieldName();
          endDateField = constraintAnnotation.endDateFieldName();
          message = constraintAnnotation.message();
      }
  
      @Override
      public boolean isValid(final Object annotatedClassInstance, final ConstraintValidatorContext context) {
          boolean isValid = false;
          try {
              Date startDate = getDateFieldValue(annotatedClassInstance, startDateField);
              Date endDate = getDateFieldValue(annotatedClassInstance, endDateField);
              isValid = endDate.after(startDate);
  
              if (!isValid) {
                  forceErrorsOnBothFields(context);
              }
  
              return isValid;
          }
          catch (Exception e) {
              return isValid;
          }
      }
  
      private void forceErrorsOnBothFields(final ConstraintValidatorContext context) {
          context.buildConstraintViolationWithTemplate(message).addNode(startDateField).addConstraintViolation();
          context.buildConstraintViolationWithTemplate(message).addNode(endDateField).addConstraintViolation();
      }
  
      private Date getDateFieldValue(final Object annotatedClassInstance, final String fieldName) throws IntrospectionException, IllegalAccessException, InvocationTargetException {
          Method dateGetter = new PropertyDescriptor(fieldName, annotatedClassInstance.getClass()).getReadMethod();
          Date date = (Date) dateGetter.invoke(annotatedClassInstance);
          return date;
      }
  }
  </code></pre>
  
  <p>The resource bundle is located under <code>src/main/resources/i18n/eSporx.properties</code> (with its locale variants). Such things as <code>&lt;spring:message code="my.key" /&gt;</code> in my JSPs work fine. However, whenever a CrossDateConstraints error is raised, all I can read is "crossdate.validation.error" even if the key is stored in the properties file!</p>
  
  <p>If you have an idea, please tell me :)
  This is so frustrating.</p>
  
  <p>Rolf</p>