Creating Custom Constraints

Consider an employee in a firm located in U.S.A. When you register the phone number of an employee or modify the phone number, the phone number needs to be validated to ensure that the phone number conforms to US phone number pattern.

Bean Validation supports cascading validation for various entities. You can specify @Valid on a member of the object that is validated to ensure that the member is also validated in a cascading fashion. You can validate type arguments, for example, parameterized types and its members if the members have the specified @Valid annotation.

public class Department {
    private List<@Valid Employee> employeesList;
}

By specifying @Valid on a parameterized type, when an instance of Department is validated, all elements such as Employee in the employeesList are also validated. In this example, each employee’s "phone" is validated against the constraint @USPhoneNumber.

Value Extractor:

While validating the object or the object graph, it may be necessary to validate the constraints in the parameterized types of a container as well. To validate the elements of the container, the validator must extract the values of these elements in the container. For example, in order to validate the element values of List against one or more constraints such as List<@NotOnVacation Employee> or to apply cascading validation to List<@Valid Employee>, you need a value extractor for the container List.

Bean validation provides in-built value extractors for most commonly used container types such as List, Iterable, and others. However, it is also possible to implement and register value-extractor implementations for custom container types or override the in-built value-extractor implementations.

Consider a Statistics Calculator for a group of 'Person' entity and 'Employee' is one of the sub-type of the entity 'Person'.

public class StatsCalculator<T extends Person> {

  /* Cascading validation as well as @NotNull constraint */
  private List<@NotNull @Valid T> members = new ArrayList<T>();


  public void addMember(T member) {
    members.add(member);
  }

  public boolean removeMember(T member) {
    return members.remove(member);
  }

  public int getAverageAge() {

    if (members.size() == 0)
      return 0;

    short sum = 0;
    for (T member : members) {
      if(member != null) {
        sum += member.getAge();
      }
    }
    return sum / members.size();
  }

  public int getOldest() {
    int oldest = -1;

    for (T member : members) {
      if(member != null) {
        if (member.getAge() > oldest) {
          oldest = member.getAge();
        }
      }
    }
    return oldest;
  }

When the StatsCalculator is validated, the "members" field is also validated. The in-built value extractor for List is used to extract the values of List to validate the elements in List. In the case of an employee based List, each "Employee” element is validated. For example, an employee’s "phone" is validated using the @USPhoneNumber constraint.

In the following example, let us consider a StatisticsPrinter that prints the statistics or displays the statistics on screen.

public class StatisticsPrinter {
    private StatsCalculator<@Valid Employee> calculator;

    public StatisticsPrinter(StatsCalculator<Employee> statsCalculator){
      this.calculator = statsCalculator;
    }

    public void displayStatistics(){
      //Use StatsCalculator, get stats, format and display them.
    }

    public void printStatistics(){
      //Use StatsCalculator, get stats, format and print them.
    }

  }

The container StatisticsPrinter uses StatisticsCalculator. When StatisticsPrinter is validated, the StatisticsCalculator is also validated by using the cascading validation such as @Valid annotation. However, in order to retrieve the values of StatsCalculator container type, a value extractor is required. An implementation of ValueExtractor for StatsCalculator is as follows:

public class ExtractorForStatsCalculator implements ValueExtractor<StatsCalculator<@ExtractedValue ?>>{

    @Override
    public void extractValues(StatsCalculator<@ExtractedValue ?> statsCalculator,
        ValueReceiver valueReceiver) {
        /* Simple value retrieval is done here.
           It is possible to adapt or unwrap the value if required.*/
      valueReceiver.value("<extracted value>", statsCalculator);
    }
  }

There are multiple mechanisms to register the ValueExtractor with Bean Validation. See, “Registering ValueExtractor” implementations section in the Bean Validation specification http://www.jcp.org/en/jsr/detail?id=380. One of the mechanisms is to register the value extractor with Bean Validation Context.

ValidatorFactory validatorFactory = Validation
        .buildDefaultValidatorFactory();

    ValidatorContext context = validatorFactory.
        usingContext()
        .addValueExtractor(new ExtractorForStatsCalculator());


    Validator validator = context.getValidator();

Using this validator, StatsisticsPrinter is validated in the following sequence of operations:

  1. StatisticsPrinter is validated.

    1. The members of StatisticsPrinter that need cascading validation are validated.

    2. For container types, value extractor is determined. In the case of StatsCalculator, ExtractorForStatsCalculator is found and then values are retrieved for validation.

    3. StatsCalculator and its members such as List are validated.

    4. In-built ValueExtractor for java.util.List is used to retrieve the values of elements of the list and the validated. In this case, Employee and the field "phone" that is annotated with @USPhoneNumber constraint is validated.