Wednesday, 3 August 2011

The JSR 303 javax.validation.Payload Class - WTF???

During the course of my last few blogs I’ve covered quite a few features of the JSR 303 Valdator and there is one feature that I’ve avoided looking into until now and that's the payload attribute of the constraint annotation interface.

There’s not much on this Hibernates documentation, and I couldn't find an entry for Payload in the JSR 303 Javadocs (if you know of one please let me know), so it raises the question of what it’s for... Looking at Hibernate’s documentation it states that: “an attribute payload that can be used by clients of the Bean Validation API to assign custom payload objects to a constraint. This attribute is not used by the API itself.” From this, you’d think that wouldn’t have to include it in your custom constraint declaration, but try taking it out and nothing works. This means that the validator is reflectively checking for its presence even though it’s not using it. Something smells fishy here.

So, I tried implementing the suggested example of a severity flag given in the Hibernate documentation and applying it to my Address validator mentioned in previous blogs, and that went wrong. They suggest that you may want to use the payload to denote failure severity using:

import javax.validation.Payload;

public class A1 {
   
public static class Info extends Payload {};
   
public static class Error extends Payload {};
}

This doesn't even compile! It turns out that Payload is a marker interface and NOT a class. The correct implementation should be:

import javax.validation.Payload;

public class Severity {
 
public static class Info implements Payload {
  }
;

 
public static class Error implements Payload {
  }
;
}

The following unit test code snippet demonstrates how you’re supposed to pick up on this information:

  @Test
 
public void payloadDemo() {

   
address.setCountry("ZR");

    Set<ConstraintViolation<Address>> constraintViolations = validator.validate
(address,
        CheckAllAddressConstraints.
class);
    assertEquals
(1, constraintViolations.size());
    printViolations
(constraintViolations, true);
 
}

 
private void printViolations(Set<ConstraintViolation<Address>> constraintViolations,
     
boolean printPayload) {

   
for (ConstraintViolation<Address> violation : constraintViolations) {

     
String invalidValue = (String) violation.getInvalidValue();
      String message = violation.getMessage
();
      System.out.println
("Found constraint violation. Value: " + invalidValue
          +
" Message: " + message);
     
if (printPayload) {
       
System.out.println("Payload Value is: "
           
+ violation.getConstraintDescriptor().getPayload());
     
}
    }
  }

The key line here is: violation.getConstraintDescriptor().getPayload().

I finally came to the conclusion that the payload attribute is superfluous as it’s really a solution looking for a problem. The suggested application was as a Severity indicator, which is a non-problem as a validator either passes or fails - there’s nothing in between. It also breaks the rule of only writing as much code as you need to solve the your current problem and no more. To sum up, I don't think that it’s going to be a widely used JSR 303 feature and would suggest that it’s removed from the validator to avoid it being pasted in to custom constraints the world over.

4 comments:

Anonymous said...

I actually found a use for the payload attribute. Our application reads in properties from a tab-delimited file, marshals them into a bean, and then uses JSR303 to validate it. One of the requirements is that when validation fails, we tell the user the field number in the file that failed validation so they can fix it. Unfortunately, there is nothing in the ConstraintViolation interface that would give us such information directly. Previously, our plan was to make a map of property paths to field numbers, and then use the getPropertyPath() method to do a lookup on this map to find the field number. But after reading your blog post, I realize that we can just put a payload on each constraint which is equal to its field number and use this information when validation fails.

Sébastien Lorber said...

I may have another usecase too.

Check that:
http://stackoverflow.com/questions/17211209/pass-flexible-data-in-java-annotations-exemple-beanvalidation-payload

Sébastien Lorber said...

I may have another usecase.

Check that:
http://stackoverflow.com/questions/17211209/pass-flexible-data-in-java-annotations-exemple-beanvalidation-payload

Hardy Ferentschik said...

Regarding your comment that the example code does not compile. Note, in your case you are using severity classes whereas the example uses interfaces and an interface can very well extend another interface ;-)

About the use of such a features, there are some as suggested by the other comments and some people might not only be interested in a simple fail or pass, but also in a severity. Either way, in most cases you can ignore the payload attribute. The only thing you have to do is to add it to your custom constraint.