No validator could be found for constraint - java

So im trying to implement my custom validation with annotation and something is wrong. Down below I put my examples.
Error:
javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'com.spring.mvc.demo.validation.CourseCode' validating type 'com.spring.mvc.demo.validation.CourseCode'. Check configuration for 'courseCode' at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.throwExceptionForNullValidator(ConstraintTree.java:166) at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.getInitializedConstraintValidator(ConstraintTree.java:181) at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:111) at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:82) at org.hibernate.validator.internal.metadata.core.MetaConstraint.doValidateConstraint(MetaConstraint.java:116) at org.hibernate.validator.internal.metadata.core.MetaConstraint.validateConstraint(MetaConstraint.java:109) at org.hibernate.validator.internal.engine.ValidatorImpl.validateMetaConstraint(ValidatorImpl.java:552) at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForSingleDefaultGroupElement(ValidatorImpl.java:510) at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForDefaultGroup(ValidatorImpl.java:479) at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraintsForCurrentGroup(ValidatorImpl.java:444) at org.hibernate.validator.internal.engine.ValidatorImpl.validateInContext(ValidatorImpl.java:394) at org.hibernate.validator.internal.engine.ValidatorImpl.validate(ValidatorImpl.java:183)
This is my CourseCOde annotation class:
#Constraint(validatedBy = CourseCodeConstraintValidator.class)
#Target({ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface CourseCode {
public String value() default "LUV";
public String message() default "must start with LUV";
public Class<?>[ ]groups() default {};
public Class<? extends Payload>[] payload() default{};
}
And how im trying to add annotation:
#CourseCode
private String courseCode;

Related

Validate UUID Restful service

I have a RESTful service which receives POST request with UUID values and writes them in DB. So the problem is to validate if UUID is valid or not. For this purpose I implemented custom annotation:
#Constraint(validatedBy = {})
#Target({ElementType.FIELD})
#Retention(RUNTIME)
#Pattern(regexp = "[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[34][0-9a-fA-F]{3}-[89ab][0-9a-fA-F]{3}-[0-9a-fA-F]{12}")
public #interface validUuid {
String message() default "{invalid.uuid}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
But for some reason it doesn't work, even if I pass valid UUID I constantly get an exception:
javax.validation.UnexpectedTypeException: HV000030: No validator
could be found for constraint 'javax.validation.constraints.Pattern'
validating type 'java.util.UUID'
Are there any options to validate UUID properly?
You cannot apply the #Pattern annotation to something (java.util.UUID) that is not a CharSequence. From the #Pattern annotation documentation (emphesizes mine):
Accepts CharSequence. null elements are considered valid.
Moreover, as far as I see you try to extend the behavior of the validation annotation handler by passing it to the new annotation definition.
If you wish to perform more complex validation, simply create your annotation without another validation annotations - their combining doesn't work like this. There must be something to recognize annotations and validate them.
#Target({ElementType.FIELD})
#Retention(RUNTIME)
#Constraint(validatedBy = UuidValidator.class)
public #interface ValidUuid {
String message() default "{invalid.uuid}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Now, create a validator which implements ConstraintValidator<ValidUuid, UUID> and override the methods performing the validation itself.
public class UuidValidator implements ConstraintValidator<ValidUuid, UUID> {
private final String regex = "....." // the regex
#Override
public void initialize(ValidUuid validUuid) { }
#Override
public boolean isValid(UUID uuid, ConstraintValidatorContext cxt) {
return uuid.toString().matches(this.regex);
}
}
And apply the annotation:
#ValidUuid
private UUID uuId;
you can use UUID.fromString(...); and catch IllegalArgumentException

Changing default group in custom constraint annotations

I have a working custom class level constraint that validates all annotations that do not belong in a group. How can I change that to validate a specific group with that constraint?
This is the annotation class I have:
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = CheckCase.class)
#Documented
public #interface ValidName {
String message() default "{message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Documented
#interface List {
ValidName[] value();
}
}
Right now, the validation works only if I use:
#CheckCase(message = "Error message")
I would like to use this:
#CheckCase(message = "Error message", groups = CheckCaseGroup.class)
Do I have to change something in the following line?
Class<?>[] groups() default {};
Separate question:
If I run the validator without passing in a group, will it go through all validations including the ones that have a group, or will it go only through the ones that don't have a specified group? For example:
will running this:
validator.validate(inputData);
go through this annotations:
#NotBlank(groups = CheckCaseGroup.class)
or only this:
#NotBlank()

Combining custom constraint validator annotation

So I have created a custom constraint validator annotation like so:
#Documented
#Constraint(validatedBy = PostcodeValidator.class)
#Target( { ElementType.METHOD, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface Postcode {
String message() default "not a well-formed postcode";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
and I wanted to use it in combination with others like so:
#NotEmpty
#Postcode
private final String postcode;
But when attempting to test this I get the following:
java.lang.IllegalStateException: Duplicate key not a well-formed postcode
I was expecting it to fail the #NotEmpty constraint.
I know I can use composition to include one constraint within the other, but my question is, is it possible to use them as I am? And if so, what am I doing wrong?
Cheers,

Can I add a bean validation annotation with group?

I want to build a NotNull check that only work on Create group, so I can write like this
#Data
public static class TestDto {
#NotNullOnCreate
// #NotNull(groups = Create.class) // instead of this
private String id;
}
I create the NotNullOnCreate like this
#Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
#Retention(RUNTIME)
#Documented
#Repeatable(NotNullOnCreate.List.class)
#ReportAsSingleViolation
#Constraint(validatedBy = {})
#NotNull(groups = Create.class)
#interface NotNullOnCreate {
String message() default "{javax.validation.constraints.NotNull.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
#Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
#Retention(RUNTIME)
#Documented
#interface List {
NotNullOnCreate[] value();
}
}
This doesn't work, because composing constraint annotation's (see ConstraintDescriptorImpl#createComposingConstraintDescriptor) group will be set to host annotation's group('Default').
I also tried this Class<?>[] groups() default {Create.class};, but this is not allowed, the default group must empty.
How can I achieve this ? I don't want to write the group everywhere.
EDIT
Create HV-1355 Constraint annotation with explicit groups
Wrote a workable demo project hv-annotation-with-group
This is not possible at the moment. If you like, open an issue in the Hibernate Validator issue tracker and we can explore this feature.

How do I display the field name description in the constraint violation message of a custom constraint annotation?

How do I display the field name description in the constraint violation message of a Bean Validation 1.1 (JSR-349) custom constraint annotation?
For example, given the following custom constraint annotation #Required, resource bundle ValidationMessages.properties, and class Person, how can I compose the constraint violation message "First Name is required." for required field firstName and "Last Name is required." for required field lastName?
#Documented
#Constraint(validatedBy = {})
#Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
#Retention(RetentionPolicy.RUNTIME)
#NotEmpty
#ReportAsSingleViolation
public #interface Required {
public abstract Class<?>[] groups() default {};
public abstract String message() default "{Required.message}";
public abstract Class<? extends Payload>[] payload() default {};
}
In resource bundle, ValidationMessages.properties:
Required.message=is required.
Class Person:
public class Person {
#Required
private String firstName;
#Required
private String lastName;
}
ConstraintViolation constraintViolation ..
((PathImpl)constraintViolation.getPropertyPath()).getLeafNode().getName();
There is no API you can use to get the current property name. And if there were, you still would do some string manipulation to get from the property name 'firstName' to the display name "First Name".
That said, I can see the benefit of exposing the current Path in ConstraintValidatorContext which is passed to ConstraintValidator#isValid. This is per specification not possible atm, but it could be implemented as a provider specific feature. You could create a issue request for Hibernate Validator here.
Regarding your problem, the best solution imo is to add a 'labelattribute to#Required`:
public class Person {
#Required(label="First Name"
private String firstName;
#Required(label="Last Name"
private String lastName;
}
Then you can interpolate the label in the message bundle like so:
Required.message={label} is required.
The constraint would look something like this
#Documented
#Constraint(validatedBy = {})
#Target({ ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
#Retention(RetentionPolicy.RUNTIME)
#NotEmpty
#ReportAsSingleViolation
public #interface Required {
public abstract String label();
public abstract Class<?>[] groups() default {};
public abstract String message() default "{Required.message}";
public abstract Class<? extends Payload>[] payload() default {};
}
Note, you can add parameters you like (provided the parameter type is supported by Java).

Categories

Resources