How to set default group in bean validation context - java

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.

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.

ConstraintValidation not getting applied to the list

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?

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.

Custom bean validation by an abstract class or interface

Let's consider the following example, I'm creating a custom bean validation constraint by means of a new annotation type:
#Target( { METHOD, FIELD, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = MyAbstractOrInterfaceValidator.class)
#Documented
public #interface MyAnnotation {
String message() default "{}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
SomeClass value();
}
My question is: can MyBastractOrInterfaceValidator class be an abstract class or an interface? How can I control which implementation of that interface or abstract class is used to validate an element the annotation is placed on?
No, that's not possible. If you want to hide your validator implementations from the public constraint definitions, you have the following options:
Provide a constraint mapping XML file and bundle it with your distribution. Users of the constraint will have to add the file to their validation.xml, though.
If you are using a DI solution such as CDI or Spring, provide just a very slim validator implementation as part of your public API (possibly as an inner class of the annotation) and obtain the actual implementation via dependency injection
If you are on Hibernate Validator 5.2 (currently under development), constraint validators can be registered via a META-INF/services file, which would be exactly what you are after; You even can plug in a custom constraint definition contributor for implementing other lookup strategies
See http://docs.oracle.com/javaee/7/api/javax/validation/Constraint.html
First, note that validatedBy is a Class<? extends ConstraintValidator<?,?>>[], that's an array, not a single class.
Second, there's no reason to use an interface or abstract class, because that class needs to be instantiated.
But, if you want to change the validator's implementation in the runtime, try using this:
public class MyValidator implements ConstraintValidator<MyAnnotation, String> {
//store the Class information in a static variable
private static Class<? extends ConstraintValidator<MyAnnotation, String>> implementationClass = MyValidatorOne.class;
//and change it by an accessor
public static void setImplementationClass(Class<? extends ConstraintValidator<MyAnnotation, String>> implClass) {
this.implementationClass = implClass;
}
//this delegate will do all the job
private final ConstraintValidator<MyAnnotation, String> implementation;
public MyValidator() {
implementation = implementationClass.newInstance();
}
#Override
void initialize(MyAnnotation constraintAnnotation) {
implementation.initialize(constraintAnnotation);
}
#Override
boolean isValid(T value, ConstraintValidatorContext context) {
return implementation.isValid(value, context);
}
}
somewhere in the code:
MyValidator.setImplementationClass(MyValidatorTwo.class);
But there's one problem. Most probably an instance of each validator is created once in the runtime for a single class - on the first validation call on object of that class. Implementation change will only take effect if done before that.
Other way is to store implementationClass values in external class, like java.util.Properties or a class which picks available implementations with some priority.

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