ConstraintValidation not getting applied to the list - java

I have List of Objects that I need to run some validation on
#KeyValid
#Valid
protected List<KeyValue> keyValues;
and I have a the following annotation created for it:
#Target({ElementType.TYPE, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = KeyValidator.class)
public #interface KeyValid{
String message() default "invalid_parameter_default_message";
String[] checks() default {};
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And this is my validator:
public class KeyValidator implements ConstraintValidator<KeyValid, KeyValue> {
#Override
public void initialize(KeyValid keyValid) {
}
#Override
public boolean isValid(KeyValue keyValue, ConstraintValidatorContext constraintValidatorContext) {
return true;
}
}
I had read somewhere that collections can be validated in bulk if the list or map or set is annotated by custom constraint then all of the elements of the collection call the validator but the above code throws the following error
javax.validation.UnexpectedTypeException: HV000030: No validator could be found for constraint 'com.util.Validators.KeyValid' validating type 'java.util.List<com.model.KeyValue>'. Check configuration for 'keyValue'

Your constraint would get the actual List passed not its elements. If you are using Java 8 and the latest version of Hibernate Validator, you can use type argument constraints. You just have to make sure to also add ElementType.TYPE_USE to #Target in your constraint. Type argument constraints are not yet official part of Bean Validation, but will be in the next version of it (BV 2.0).
You would have something like this:
protected List<#KeyValid KeyValue> keyValues;
Alternatively, could you not put the #KeyValid constraint as class level constraint on KeyValue?

Related

JSR-303 custom annotation

I'm trying to implement a custom annotation to validate my fields. The idea is that the validation fails whenever the annotated field is null. Something like this.
#RequiredProperty
public abstract Person getPerson();
Now if this returns a null Person, I'd like the validation to fail (ideally with a custom message "Person field is null").
I tried to do it like this.
#Documented
#Constraint(validatedBy = RequiredPropertyValidator.class)
#Retention(RetentionPolicy.RUNTIME)
#Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_USE})
#ReportAsSingleViolation
public #interface RequiredProperty {
String message() default "{javax.validation.constraints.RequiredProperty.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And validator.
public class RequiredPropertyValidator implements ConstraintValidator<RequiredProperty, Object> {
#Override
public void initialize(RequiredProperty constraintAnnotation) {
}
#Override
public boolean isValid(Object property, ConstraintValidatorContext context) {
return property != null;
}
}
However this won't work. It doesn't validate at all. Object property is never even passed to the isValid method. Any advice on how to get it working?
UPDATE
Removing the empty initialize method got it working. However, I'm not sure how to create a custom error message that the "Person" field is null. Is that possible?
I created a custom message in .properties file, but this is just a static message, and I'd like to capture the actual field from runtime.

Annotations from javax.validation.constraints in combination with custom ConstraintValidator

I have a problem to combine the javax annotation with custom ConstraintValidators.
I have an example class Person, where the name and the age are required.
#PersonConstraint
public class Person {
#NotNull
private String name;
#NotNull
private Integer age;
...
}
And I have an additional constraint:
#Constraint(validatedBy = PersonValidator.class)
#Target(TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface PersonConstraint{
String message() default "...";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
For the custom Validator:
public class PersonValidator implements ConstraintValidator<PersonConstraint, Person> {
...
#Override
public boolean isValid(Person value, ConstraintValidatorContext context) {
if(value.getAge() < 18)
return false;
...
}
}
When a Person object is validated now and the age is null, I got a Nullpointer Exception in the PersonValidator. I thoguht, this is something, which is checked by the javax Annoation #NotNull.
Is there a solution to combine the annotations with the custom validators or do I have to check the null values in the validor by myself?
(The code is just an example - but this is a general question)
Unless you are working with the group and group sequences, there is no guaranteed order in which constraints are going to be evaluated. You cannot rely in your custom constraint that the #NotNull already has occured. And even it it had you would still get a NullPointerException. Bean Validation will per default not stop after the first constraint violation, but collect all violations of a required validation (hence a set of ConstraintViolations are returned. So there might actually be already a constraint violation for age, but in your custom ConstraintValidator you would still be accessing a null value. You cannot get around doing a null check at this stage.

How to set default group in bean validation context

I'm working with bean validations and I'm searching for a possibility to set a default group of my own bean validation annotation.
I have something (working) like this:
Application.class (calling validate on MyBean)
public class Application {
public static void main(String[] args) {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<MyBean>> violations =
validator.validate(new MyBean(), SecondStep.class);
}
}
MyBean.class (the bean itself; here is what I want to prevent)
public class MyBean {
// I don't want to write this "groups" attribute every time, because it's very clear,
// that this should only be validated for the second step, isn't it?
#RequiredBySecondStep(groups=SecondStep.class)
private Object myField;
}
RequiredBySecondStep.class (the bean validation annotation)
#Documented
#Target(FIELD)
#Retention(RUNTIME)
#Constraint(validatedBy = RequiredBySecondStepValidator.class)
public #interface RequiredBySecondStep {
String message() default "may not be null on the second step";
Class<?>[] groups() default {}; // <-- here I want to set SecondStep.class
Class<? extends Payload>[] payload() default {};
}
RequiredBySecondStepValidator.class (an implemented constraint validator)
public class RequiredBySecondStepValidator implements ConstraintValidator<RequiredBySecondStep, Object> {
public void initialize(RequiredBySecondStep constraintAnnotation) {
}
public boolean isValid(Object object, ConstraintValidatorContext constraintContext) {
return object != null;
}
}
SecondStep.class (the bean validation group)
public interface SecondStep {
}
Unfortunately, it's not possible by specification, to set the default group in the RequiredBySecondStep annotation like this:
Class<?>[] groups() default SecondStep.class;
// and using just the following in the bean:
#RequiredBySecondStep
private Object myField;
This will result in a RuntimeException:
javax.validation.ConstraintDefinitionException: Default value for
groups() must be an empty array
Furthermore, there is not only a SecondStep. There are probably 5 different groups which I want to annotate directly with a #RequiredByFirstStep or #RequiredByFifthStep.
Is there a good way to implement this?
I think you got it all a bit wrong. There is indeed to way to do what you want and that's because the aspect of constraints and their validation via a ConstraintValidator is orthogonal to groups and groups sequences. Per design a constraint (annotation and its validator) should be independent of the group getting validated. Even if you would get this to work, it would not be portable constraints. Personally, I would re-think what you want to achieve. #RequiredByFirstStep does not tell you what the requirement is. You should develop constraints which imply what they are valiating (a string length, not null, etc), when or better in which condition they are executed is a then controlled by group interfaces.

Java Bean validation: Combine two contstraints with OR on one field

I want to validate a field 'foo' against either of two constraints, i.e. something like this
#ConstraintA OR #ConstraintB
private String foo;
Is this possible?
This is possible with Hibernate Validator, but only using a Hibernate Validator specific extension. Using it is not standard conform to Bean Validation.
You will have to use boolean composition of constraints as described here - http://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#section-boolean-constraint-composition
You will need a "wrapper" constraint. Something like this:
#ConstraintComposition(OR)
#PConstraintA
#ConstraintB
#ReportAsSingleViolation
#Target({ METHOD, FIELD })
#Retention(RUNTIME)
#Constraint(validatedBy = { })
public #interface ConstraintAOrB {
String message() default "{com.acme.ConstraintAOrB.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}

Bean validation: at least one element of a list must have one field filled

I have a list of lets say Car objects. Each car has a miles member.
I need to validate (using Hibernate Validator) that at least one car in my list has a not null miles member. The best solution would be an annotation that would apply to all the elements of a collection, but would validate in the context of the whole collection.
I have two questions:
Is there already an annotation for this (I do not know of any)?
If there is no annotation for this, is there a way to create an generic annotation?
I thought of specifiyng the name of the field that has to be not null at least for one element in the list, then I can apply this not only for Car classes.
public class VechicleTransport {
#AtLeastOneNotNull( fieldName = "miles" )
private List<Car> carList;
}
public class Car {
private Double miles;
....
}
AFAIK there is no such annotation, You need to define custom constraint annotations and define your validation logic inside it.
Like
Defining custom constraint annotation AtLeastOneNotNull
#Target({ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Constraint(validatedBy=AtLeastOneNotNullValidator.class)
public #interface AtLeastOneNotNull{
String message() default "Your error message";
Class<!--?-->[] groups() default {};
Class<!--? extends Payload-->[] payload() default {};
}
Defining validator for custom annotation.
public class AtLeastOneNotNullValidator implements ConstraintValidator<AtLeastOneNotNull, object=""> {
#Override
public void initialize(AtLeastOneNotNull constraint) {
}
#Override
public boolean isValid(Object target, ConstraintValidatorContext context) {
// Add logic to check if atleast one element have one field
}
}
Link for more details

Categories