Close

Java Bean Validation - Constraint payloads

[Last Updated: Aug 27, 2021]

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. By default this interface does not define any methods.

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



Examples

Using payload to specify severity level

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.

package com.logicbig.example;

import javax.validation.*;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;
import java.util.Set;

public class ConstraintPayloadExample {
    private static final Validator validator;

    static {
        Configuration<?> config = Validation.byDefaultProvider().configure();
        ValidatorFactory factory = config.buildValidatorFactory();
        validator = factory.getValidator();
        factory.close();
    }

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

        public static class Error implements Payload {
        }
    }

    public static 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;
        }
    }

    public static void main (String[] args) {
        TestBean bean = new TestBean();
        //uncommenting next line will give us info severity
        // or setting an 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 must 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 which will send email to the support team on errors.


package com.logicbig.example;

import javax.validation.*;
import javax.validation.constraints.NotNull;
import java.util.Set;

public class ConstraintPayloadExample2 {
    private static final Validator validator;

    static {
        Configuration<?> config = Validation.byDefaultProvider().configure();
        ValidatorFactory factory = config.buildValidatorFactory();
        validator = factory.getValidator();
        factory.close();
    }

    public interface AppErrorHandler<T> extends Payload {
        void onError (ConstraintViolation<T> violation);
    }

    public static 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());
        }
    }

    public static class TestBean {

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

        public String getStr () {
            return str;
        }

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

    public static void main (String[] args) {
        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 must not be null

Example Project

Dependencies and Technologies Used:

  • hibernate-validator 6.2.0.Final (Hibernate's Jakarta Bean Validation reference implementation)
     Version Compatibility: 5.0.0.Final - 6.2.0.Final Version List
    ×

    Version compatibilities of hibernate-validator with this example:

      groupId: org.hibernate
      artifactId: hibernate-validator
      Reference implementation for Bean Validation 1.1
    • 5.0.0.Final
    • 5.0.1.Final
    • 5.0.2.Final
    • 5.0.3.Final
    • 5.1.0.Final
    • 5.1.1.Final
    • 5.1.2.Final
    • 5.1.3.Final
    • 5.2.0.Final
    • 5.2.1.Final
    • 5.2.2.Final
    • 5.2.3.Final
    • 5.2.4.Final
    • 5.2.5.Final
    • 5.3.0.Final
    • 5.3.1.Final
    • 5.3.2.Final
    • 5.3.3.Final
    • 5.3.4.Final
    • 5.3.5.Final
    • 5.3.6.Final
    • 5.4.0.Final
    • 5.4.1.Final
    • 5.4.2.Final
    • 5.4.3.Final
    • groupId: org.hibernate.validator
      artifactId: hibernate-validator
      Reference implementation for Bean Validation 2.0
    • 6.0.0.Final
    • 6.0.1.Final
    • 6.0.2.Final
    • 6.0.3.Final
    • 6.0.4.Final
    • 6.0.5.Final
    • 6.0.6.Final
    • 6.0.7.Final
    • 6.0.8.Final
    • 6.0.9.Final
    • 6.0.10.Final
    • 6.0.11.Final
    • 6.0.12.Final
    • 6.0.13.Final
    • 6.0.14.Final
    • 6.0.15.Final
    • 6.0.16.Final
    • 6.0.17.Final
    • 6.0.18.Final
    • 6.0.19.Final
    • 6.0.20.Final
    • 6.0.21.Final
    • 6.0.22.Final
    • 6.1.0.Final
    • 6.1.1.Final
    • 6.1.2.Final
    • 6.1.3.Final
    • 6.1.4.Final
    • 6.1.5.Final
    • 6.1.6.Final
    • 6.1.7.Final
    • 6.2.0.Final
    • Version 7 and later:
      Jakarta Bean Validation 3.0
      jakarta.* packages

    Versions in green have been tested.

  • javax.el-api 3.0.0 (Expression Language 3.0 API)
  • javax.el 2.2.6 (Expression Language 2.2 Implementation)
  • JDK 8
  • Maven 3.8.1

Java Bean Validation - payloads Example Select All Download
  • constraint-payload
    • src
      • main
        • java
          • com
            • logicbig
              • example
                • ConstraintPayloadExample2.java

    See Also