4

I would like to know how to pass in the additional parameter as object x as below on isValid method. Anyone can provide help? object x will be passed in during runtime after getting the value from other object. i.e. ( i would like to pass a value retrieve from db to isValid method. )

Default isValid implementation as below

public boolean isValid(Object value, ConstraintValidatorContext ctx)

Expected isValid customization

public boolean isValid(Object value, ConstraintValidatorContext ctx, Object x)

Full source code as below

NotNullIfAnotherFieldHasValueValidator

import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class NotNullIfAnotherFieldHasValueValidator 
        implements ConstraintValidator<NotNullIfAnotherFieldHasValue, Object> {

    private static final Logger LOG = LogManager.getLogger(NotNullIfAnotherFieldHasValueValidator.class);
    private String fieldName;
    private String expectedFieldValue;
    private String dependFieldName;
    private String errorMessage;

    @Override
    public void initialize(NotNullIfAnotherFieldHasValue annotation) {
        fieldName = annotation.fieldName();
        expectedFieldValue = annotation.fieldValue();
        dependFieldName = annotation.dependFieldName();
    }

    @Override
    public boolean isValid(Object value, ConstraintValidatorContext ctx) {

        if (value == null) {
            return true;
        }

        return true;
    }

}

NotNullIfAnotherFieldHasValue

@Target({TYPE, ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = NotNullIfAnotherFieldHasValueValidator.class)
@Documented
public @interface NotNullIfAnotherFieldHasValue {

    String fieldName();
    String fieldValue();
    String dependFieldName();

    String message() default "value is required";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};

    @Target({TYPE, ANNOTATION_TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @interface List {
        NotNullIfAnotherFieldHasValue[] value();
    }

}

SampleBean

@NotNullIfAnotherFieldHasValue.List({
        @NotNullIfAnotherFieldHasValue(fieldName = "yesOrNo", fieldValue = "true", dependFieldName = "status") })
public class SampleBean {

    private String status;
    private String fieldOne;
    private Boolean yesOrNo;
    private Set<Byte> refOfficer;

    public Set<Byte> getRefOfficer() {
        return refOfficer;
    }

    public void setRefOfficer(Set<Byte> refOfficer) {
        this.refOfficer = refOfficer;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getFieldOne() {
        return fieldOne;
    }

    public void setFieldOne(String fieldOne) {
        this.fieldOne = fieldOne;
    }

    public Boolean getYesOrNo() {
        return yesOrNo;
    }

    public void setYesOrNo(Boolean yesOrNo) {
        this.yesOrNo = yesOrNo;
    }

}

Main Program

public static main (String args[])
{

SampleBean sampleBean = new SampleBean();
sampleBean.setYesOrNo(Boolean.True);
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
        Validator validator = factory.getValidator();

        Set<ConstraintViolation<VisitResource>> violations = validator.validate(sampleBean);
}
Banana Tech
  • 347
  • 1
  • 6
  • 18
  • you don't pass it, you apply it as an annotation – Stultuske Nov 27 '17 at 11:07
  • I closed your question as a duplicate, although the questions' requirements are not exactly equal. But the idea is the same: cross-field validation is done at the class level with some custom or some framework-provided annotation. – Seelenvirtuose Nov 27 '17 at 11:24
  • @Seelenvirtuose This is not duplicate question and the field value passing in is not from the same class but from passing from another object into it. i.e. ( i would like to pass a value retrieve from db to isValid method. ) – Banana Tech Nov 28 '17 at 07:09
  • Then ... I fear ... it is not possible. – Seelenvirtuose Nov 28 '17 at 07:51
  • I have reopened the question. Here the link to the cross-field validation: https://stackoverflow.com/questions/1972933/cross-field-validation-with-hibernate-validator-jsr-303 – Seelenvirtuose Nov 28 '17 at 07:52
  • i am using Threadlocal method and set the required object in memory before calling the validation ==> validator.validate(sampleBean); Say I would like to pass in the value I get from the DB or Files to validate in the the isValid method. – Banana Tech Dec 14 '17 at 09:09
  • Continue from previous comment, So, inside the public boolean isValid method then i can retrieve the object without passing the parameter in. This is less preferred way base on my opinion cause i need to destroy the Threadlocal value in the memory and improper handling of Threadlocal will lead to intermittent critical issue and extremely difficult to debug. Not sure there is any other better way of doing it in order to pass in the value? – Banana Tech Dec 14 '17 at 09:09
  • What I was hoping to be able to was associate some information with the ConstraintValidationContext at the point of buliding the validator, so via the ValidatorFactory for example. That would then be accessible from within the validator. However, that doesn't seem possible, so it seems I need to use a threadlocal variable to do what I want, which is basically to access external resource (such as express "this field must be one of the values in this column of a table in a database") – Tom Quarendon Oct 03 '18 at 11:29

0 Answers0