How to obtain ConstraintValidatorContext?
Solution 1
The simple answer is you cannot. ConstraintValidatorContext is an interface and there is no Bean Validation API to get an instance like this. You could write your own implementation, but to implement it properly you would have to re-implement a lot of functionality of a Bean Validation provider. Look for example at the Hibernate Validator specific implementation - https://github.com/hibernate/hibernate-validator/blob/master/engine/src/main/java/org/hibernate/validator/internal/engine/constraintvalidation/ConstraintValidatorContextImpl.java
That said, I believe your attempt of reuse is misguided. This is not in the indent of Bean Validation and you are ending up with non portable and hard to maintain code. If you want to reuse existing constraints have a look at constraint composition, for example @NotEmpty reusing @NotNull and @Size
@Documented
@Constraint(validatedBy = { })
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@ReportAsSingleViolation
@NotNull
@Size(min = 1)
public @interface NotEmpty {
String message() default "{org.hibernate.validator.constraints.NotEmpty.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
/**
* Defines several {@code @NotEmpty} annotations on the same element.
*/
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
@Retention(RUNTIME)
@Documented
public @interface List {
NotEmpty[] value();
}
}
Solution 2
I recently had exactly the same issue as the OP. However contrary to the accepted answer it is possible to write Unit tests that include the ConstraintValidationContext. This excellent link explains how to do it, http://farenda.com/java/bean-validation-unit-testing/
Basically you need to use the ValidatorFactory to obtain a Validator interface, then call validate(c) on that interface, where the parameter c is an instance of the class containing the bean validation annotations. A code example is clearer, code sample taken from the above link.
public class Player {
// name have to be 3 chars:
@Size(min = 3, max = 3)
private String name;
// possible score in game:
@Min(0) @Max(100)
private int score;
public Player(String name, int score) {
this.name = name;
this.score = score;
}
// just for logs
@Override
public String toString() {
return "Player{name='" + name + '\'' + ", score=" + score + '}';
}
}
public class PlayerValidationTest {
private static ValidatorFactory validatorFactory;
private static Validator validator;
@BeforeClass
public static void createValidator() {
validatorFactory = Validation.buildDefaultValidatorFactory();
validator = validatorFactory.getValidator();
}
@AfterClass
public static void close() {
validatorFactory.close();
}
@Test
public void shouldDetectInvalidName() {
//given too short name:
Player player = new Player("a", 44);
//when:
Set<ConstraintViolation<Player>> violations
= validator.validate(player);
//then:
assertEquals(violations.size(), 1);
}
}
Related videos on Youtube
alexsmail
Java/Python Professional blog https://alex-ber.medium.com/ Personal Blog at http://toalexsmail.com/
Updated on June 04, 2022Comments
-
alexsmail almost 2 years
I am writing code that has explicit call to Bean Validation (JSR-303) something like this:
public class Example { @DecimalMin(value = "0") private static final String ANNOTATED = ""; public void isPossitiveNumber(String str){ ValidatorFactory factory = Validation.buildDefaultValidatorFactory(); ConstraintValidator<DecimalMin, String> validator = factory.getConstraintValidatorFactory().getInstance( DecimalMinValidatorForString.class); validator.initialize( ReflectionUtils.findField(getClass(), "ANNOTATED") .getAnnotation( DecimalMin.class)); boolean isValid = validator.isValid(str, null); return isValid; } }
Note the line boolean isValid = validator.isValid(str, null); I transfer
null
forConstraintValidatorContext
because I found no way to obtain/construct it. In this particular case, this if fine, because there is no use of theConstraintValidatorContext
internally, but it is obvious a hack. How should I getConstraintValidatorContext
?ADDED
I was asked to provide use-cases. So, for example, I am writting custom validator and I want to reuse exisiting validations. Or I am writting plane Java code as desribed above and I want to reuse exisiting validation.
-
Gunnar about 11 yearsCould you add some details to your use case? As a user, you normally interact with Bean Validation through the
javax.validation.Validator
API, not by creating and invoking constraint validators by hand.ConstraintValidatorContext
is passed by the engine to validators during validation, so its implementation is typically not exposed to users. -
alexsmail about 11 yearsFor example, I am writting custom validator and I want to reuse exisiting validations. Or I am writting plane Java code as desribed above and I want to reuse exisiting validation. I will add this clarification to my post.
-
-
alexsmail about 11 yearsI want to reuse existing ConstraintValidator, not to process bean validation.
-
SpaceTrucker about 11 years@alexsmail From the documentation of
ConstraintValidator
:This method can be accessed concurrently, thread-safety must be ensured by the implementation.
And the spec also statesCompliant implementations are allowed to cache ConstraintValidator instances retrieved from the ConstraintValidatorFactory.
(section 2.4) -
Hardy about 11 yearsYou are reusing the constraints
-
alexsmail about 11 yearsNot really. I can't call to validation form the plain code as above and I can't call to validation from custom validation in some complex flow.
-
Catalina about 3 yearscould you please help me stackoverflow.com/questions/67424185/…
-
Catalina about 3 yearscould you help plz stackoverflow.com/questions/67424185/…