2

I'd like to create a custom validator of a List that would ensure that at least two item are selected. The problem is that neither initialize nor isValid method are ever called. How to make list validator working?

I use Java EE 7.

package com.sample.listvalidation;

import org.apache.commons.lang3.tuple.MutablePair;

import javax.annotation.PostConstruct;
import javax.faces.view.ViewScoped;
import javax.inject.Named;
import java.io.Serializable;
import java.util.Arrays;
import java.util.List;

@ViewScoped
@Named
public class Bean implements Serializable {

    @AtLeast2Selected
    private List<MutablePair<String, Boolean>> selection;

    public List<MutablePair<String, Boolean>> getSelection() {
        return selection;
    }

    public void setSelection(List<MutablePair<String, Boolean>> selection) {
        this.selection = selection;
    }

    @PostConstruct
    void init() {
        selection = Arrays.asList(
                MutablePair.of("a", true),
                MutablePair.of("b", true),
                MutablePair.of("c", false),
                MutablePair.of("d", false)
                );
    }

    public String validate() {
        return null;
    }
}
package com.sample.listvalidation;

import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
@Constraint(validatedBy = AtLeast2SelectedValidator.class)
public @interface AtLeast2Selected {
    String message() default "At leaset 2 items have to be selected.";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
}
package com.sample.listvalidation;

import org.apache.commons.lang3.tuple.MutablePair;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.List;

public class AtLeast2SelectedValidator implements ConstraintValidator<AtLeast2Selected, List> {
    @Override
    public void initialize(AtLeast2Selected constraintAnnotation) {
        System.out.println("AtLeast2SelectedValidator.initialize");
    }

    @Override
    public boolean isValid(List value, ConstraintValidatorContext context) {
        System.out.println("AtLeast2SelectedValidator.isValid");
        if (value == null) {
            return false;
        }
        @SuppressWarnings("unchecked")
        final List<MutablePair<String, Boolean>> typedValue = (List<MutablePair<String, Boolean>>) value;
        return typedValue.stream()
                .filter(MutablePair::getRight)
                .count() >= 2;
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
      xmlns:f="http://xmlns.jcp.org/jsf/core">
<f:view>
    <h:messages />
    <h:form>
        <h:dataTable value="#{bean.selection}" var="row">
            <h:column>
                <h:selectBooleanCheckbox value="#{row.right}" id="checkbox" />
            </h:column>
            <h:column>
                <h:outputLabel for="checkbox" value="#{row.left}" />
            </h:column>
        </h:dataTable>
        <h:commandButton value="validate" action="#{bean.validate}" />
    </h:form>
</f:view>
</html>
BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
czerny
  • 15,090
  • 14
  • 68
  • 96
  • @BalusC Yes, it doesn't but the code sample tries to use property level rather than class level constraints (`@Target(ElementType.FIELD)`). However the referenced article probably contains the explanation: > When you are using bean validation in your application, JSF automatically uses the constraints for beans that are referenced by UIInput values. And my constraint is not applied to property referenced by UIInput, rather elements of the list are referenced by UIInput. Thanks for the link. You can summarize it to an answer. – czerny Sep 04 '15 at 11:40
  • 1
    The `List` is here not the property. It's an entity (a class). The actual property is `right` field as represented by `#{row.right}`. – BalusC Sep 04 '15 at 11:49

0 Answers0