2

I have implemented validation for Long field and @PastOrPresent validation

public class PastOrPresentValidator implements ConstraintValidator<PastOrPresent, Long>{
    @Override
    public boolean isValid(Long value, ConstraintValidatorContext context) {
        if (value == null) {
            return false;
        }
        return value <= System.currentTimeMillis();
    }
}

public class MyDto {
    @PastOrPresent
    Long timestamp;    
}

And got error:

HV000030: No validator could be found for constraint 'javax.validation.constraints.PastOrPresent' validating type 'java.lang.Long'. Check configuration for 'push.dto.timestamp'

But when I create custom annotation like @PastOrPresentLong and use same validator code everything works.

Is there a way to add custom validator for javax.validation.constraints annotation?

1
  • I think you should accept this wonderful answer. Commented Oct 9, 2020 at 16:39

2 Answers 2

6

Just implementing the ConstraintValidator interface isn't enough for your new validator to work. It should be somehow registered so that BeanValidation provider knows about it. This can be done via XML (see for more info here):

<constraint-mappings
        xmlns="http://xmlns.jcp.org/xml/ns/validation/mapping"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/validation/mapping
            http://xmlns.jcp.org/xml/ns/validation/mapping/validation-mapping-2.0.xsd"
        version="2.0">
    <!-- anything else -->
    <constraint-definition annotation="javax.validation.constraints.PastOrPresent">
        <validated-by include-existing-validators="true">
            <value>org.mycompany.PastOrPresentLongValidator</value>
        </validated-by>
    </constraint-definition>
</constraint-mappings>

Then if you are using Hibernate Validator as BeanValidation provider you also have two more options - use service loader or programmatic definition. Using Service loader is the easiest way of adding new validator. All you need to do is create a META-INF/services/javax.validation.ConstraintValidator file and add a FQCN of your validator to it. See detailed examples in this post under section "Use standard constraints for non standard classes" or in the doc. With programmatic approach you would have to do something like:

ConstraintMapping constraintMapping = configuration.createConstraintMapping();

constraintMapping
        .constraintDefinition( PastOrPresent.class )
        .validatedBy( PastOrPresentLongValidator.class );

And then add this constraint mapping to your configuration and create a validator from it:

Validator validator = configuration.addMapping( constraintMapping )
        .buildValidatorFactory()
        .getValidator();

But as I mentioned earlier - Service Loader is the easiest and quickest way to go.

Sign up to request clarification or add additional context in comments.

Comments

2

Simpler Approach

A simpler declarative approach to linking the annotation to the validator is to specify the linkage through a Constraint(validatedBy) annotation.

@Retention(RUNTIME)
@Target({ FIELD, METHOD, PARAMETER, ANNOTATION_TYPE })
@Constraint(validatedBy = PastOrPrsentValidator.class)
public class PastOrPresentValidator implements ConstraintValidator<PastOrPresent, Long>{
...

1 Comment

I think he is trying to create a custom validator for an existing @Constraint, not write a new one. This might be the simpler solution, though, since the registration can occur automagically.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.