JavaBean Validation - Constraint payloads

[Updated: Dec 7, 2017, Created: Sep 26, 2016]

Constraint annotation must have an element 'payload' which may be assigned to an array of classes extending javax.validation.Payload. By default this array should be empty , so it's optional.

Payload is an interface, we can provide an implementation of this interface as an element for 'payload' to provide some additional metadata information in a typed-safe way (as compare to some string literal) or we can even invoke some dynamic code with it.

Let's see how we can do that with some examples.



Creating payloads

This example idea is taken from the spec documents.

In this example, we are using Payload to specify a level of validation error severity with the constraint declaration so that the invoking code make some decision based on that.

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

        public static class Error implements Payload {}
    }


Creating the bean

    public class TestBean {
      @NotNull(payload = {Severity.Error.class})
      @Size(min = 1, payload = {Severity.Info.class})
      private String str;

       public String getStr () {
            return str;
       }

       public void setStr (String str) {
            this.str = str;
       }
    }


Performing validation and using severity level

Here we going to make some decision based on the 'severity' payload

        TestBean bean = new TestBean();
        //uncommenting next line will give us info severity
        // or setting a non-empty string won't give any validation error
        //bean.setStr("");

        Set<ConstraintViolation<TestBean>> constraintViolations =
                            validator.validate(bean);

        boolean severeError = false;

        if (constraintViolations.size() > 0) {
            for (ConstraintViolation<TestBean> violation : constraintViolations) {
                Set<Class<? extends Payload>> payloads =
                                    violation.getConstraintDescriptor().getPayload();
                for (Class<? extends Payload> payload : payloads) {
                    if (payload == Severity.Error.class) {
                        severeError = true;
                        System.out.println("Error: " + violation.getPropertyPath() + " " +
                                            violation.getMessage());
                    } else if (payload == Severity.Info.class) {
                        System.out.println("Info: " + violation.getPropertyPath() + " " +
                                            violation.getMessage());
                    }
                }
            }
        }

        if (!severeError) {
            //continue working with bean
            System.out.println("working with : " + bean);
        }
    }

Output

Error: str may not be null

Invoking dynamic code using payload

This example shows how payload can be used to invoke some dynamic code using reflection.

The idea is to attach an application level error handler, for instance a handler which will send email to the support team on errors.


Creating payload

    public interface AppErrorHandler<T> extends Payload {
        void onError (ConstraintViolation<T> violation);
    }
    public class ErrorEmailSender<T> implements AppErrorHandler<T> {
        @Override
        public void onError (ConstraintViolation<T> violation) {
            System.out.println("Sending email to support team: " +
                                violation.getPropertyPath() + " " +
                                violation.getMessage());
        }
    }

Creating bean

  public  class TestBean {
     @NotNull(payload = {ErrorEmailSender.class})
     private String str;

     //getters and setters

    }


Invoking bean and performing validation

  TestBean bean = new TestBean();
  Set<ConstraintViolation<TestBean>> constraintViolations =
                                                validator.validate(bean);

  if (constraintViolations.size() > 0) {
          constraintViolations.stream().forEach(
                                ConstraintPayloadExample2::processError);
  } else {
          //proceed using user object
            System.out.println(bean);
        }
  }


  private static void processError (ConstraintViolation<TestBean> violation) {
        Set<Class<? extends Payload>> payload =
                                   violation.getConstraintDescriptor().getPayload();

        payload.forEach(p -> {
            if (AppErrorHandler.class.isAssignableFrom(p)) {
                try {
                    AppErrorHandler errorHandler = (AppErrorHandler) p.newInstance();
                    errorHandler.onError(violation);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
  }

Output:

Sending email to support team: str may not be null

Example Project

Dependencies and Technologies Used :

  • Hibernate Validator Engine 5.2.4.Final: Hibernate's Bean Validation (JSR-303) reference implementation.
  • Expression Language API 2.2 2.2.4
  • Expression Language 2.2 Implementation 2.2.4
  • JDK 1.8
  • Maven 3.0.4

Constraint Payload Select All Download
  • constraint-payload
    • src
      • main
        • java
          • com
            • logicbig
              • example

See Also