Custom class level bean validation constraint - constraints

I already know how to add annotation based validation on specific attributes in Entity class like :-
public class Person {
#NotNull
private String firstName;
private String lastName;
//...
}
But is it possible to add annotation on class Person, in order to validate all the attributes inside this class, by creating a Customised Validation Class and handling validation there somewhere like :-
#Retention(value = RetentionPolicy.RUNTIME)
#Target(value = ElementType.METHOD)
public #interface PersonneName {
public String firstName();
}
I am working on a project to get Constraints from Database and creating Customised Validation Class and applying on the Entity class attributes according to the constaints got from DB.
Please suggest.

Yes, of course, it's possible. First, create the definition of your annotation. Pretty much like you did in your example, however, with a different #Target type
#Target({ElementType.TYPE, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = PersonValidator.class)
public #interface ValidPerson {
String message () default "Your custom message";
Class<?>[] groups () default {};
Class<? extends Payload>[] payload () default {};
}
Then implement the validator whose isValid method takes the instance of your Person class:
public class PersonValidator implements ConstraintValidator<ValidPerson, Person> {
#Override
public boolean isValid (Person person, ConstraintValidatorContext context) {
// your validation logic
}
}

Sure it is possible, just check the documentation regarding how to write custom class level constraints - http://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/#section-class-level-constraints
The important thing of course is that you make sure that one can actually place the constraint annotation on the type level. For that you need to add ElementType.TYPE to the #Target annotation.

Related

spring mvc rest validation on all method parameters

I would like to find a a way to validate a rest method based on all parameters outside the Controller.
First Question: Is there already a way to do it?
Second Question: If not - how can I hook the validation into spring mvc binding prozess.
A way how it could look like. It would be nice to mark the method with a new #MethodValidation Annotation:
#Validate
#MethodValidation(MyValidator.class)
public Response doSomthing(String param1, Integer param2, Something param3){}
Annotation
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface MethodValidation{
Class<? extends MethodValidator<?, ?>>[] value();
}
Implement a Validator
public class MyValidator implements MethodValidator{
public void validate(Object[] params, Errors errors){
String param1 = (String ) params[0];
Integer param2 = (Integer) params[1];
Something param3 = (Something)params[3];
// .... do some validations
if(error)
errors.reject("Some.error.done");
}
}
what kind of parameters exactly? a lot of spring stuff is actually available in ThreadLocals, if you dare to dig into it.
you CAN inject stuff into the binding process:
#ControllerAdvice
public class FooControllerAdvice {
#InitBinder
public void initBinder(WebDataBinder binder) {
binder.registerCustomEditor(
Date.class,
new CustomFooEditor()
);
}
}
and the actual editor:
public class CustomFooEditor extends PropertyEditorSupport {
}
but this doesn't give you that much of an edge over regular validation.
or you can use spring aop triggered by an annotation, then annotate your methods, with the config:
#EnableAspectJAutoProxy(proxyTargetClass=true)
an aspect:
#Aspect
#Component
public class ValidationAspect {
#Pointcut("execution(public * * (..))")
private void anyPublicMethod() {}
#Around("anyPublicMethod() && #annotation(foo)")
public Object all(
ProceedingJoinPoint proceedingJoinPoint,
Foo ann) throws Throwable {
}
[...]
}
an annotation:
#Inherited
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Foo {
}
public String value();
and then annotating your method:
#RequestMapping...
#Foo(value="foo.bar.ValidatorClassname")
public Response x() {
}
... so you see, there's a lot of ways you can go. i'd really like to know what keeps you from using standard validation?
.rm
thanx for the answer.
I hope I am right: The standard validation outside the controller just allows me to to validate each method parameter separately.
I actually get into problems when the validation depends on 2 or more method parameter. This could be in following situation: Some thing is a part of an Object hierarchy:
public class Parent{
private Integer id;
private List<Something> childs;
...
}
public class Something{
private Integer id;
private String name;
...
}
The Constrain: it is not allowed that a Parent has 2 somethings in the list with the same name. For saving a new some thing I am calling the method.
#RequestMapping(
value = "/chargingstation/{parentId}",
method = RequestMethod.Post)
public Response doSomthing(
#PathVariable("parentId") Integer parentId,
Something param3)
Add the parentId to the Something-ModelOject was not an option.
So is there a way to handle this situation with the standard validation?

How to get some common data in a custom ConstraintValidator

I am validating a input class using Java validation api and hibernate validator.
i have created few custom annontation for some business validation and i am using these annotation on the input class. below is an example of such annotation validator -
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class DBColumnConstraintValidator implements ConstraintValidator<DatabaseColumnConstraint, Object> {
private DBColumnConstraintValidator databaseColumnConstraint;
private final List<DatabaseConstraintValidationStep> steps = new ArrayList<DatabaseConstraintValidationStep>();
#Override
public void initialize(DBColumnConstraintValidator databaseColumnConstraint) {
}
#Override
public boolean isValid(Object value, ConstraintValidatorContext context) {
//Validation logic
}
}
I want to use some data for my validation logic inside isValid() method and this data is common and will be used by few other validator classes as well.
is there any way to set or make that common data available to isValid() method?
If you are using CDI or Spring, you can simply use dependency injection (e.g. using #Inject) within your constraint validator implementation to obtain whatever contextual service or data your need.

JSR-303 Bean Validation Collection Values

I am trying to use JSR-303 Bean Validation with Hibernate Validator to ensure that a collection does not contain null values.
I know that I can annotate my collection as follows:
#Valid
#NotEmpty
private Set<EmailAddress> emails = new HashSet<>();
This does the job for ensuring that the collection itself is not null or empty and in the case that I add a non-null EmailAddress element it also validates this correctly. However, it doesn't prevent adding a null element.
Is there a way to prevent a null element being added to the collection? In an ideal world the solution would be declarative (like the rest of the validation) and wouldn't involve programmatically iterating through the collection manually doing null checks.
Thanks in advance!
Bean Validation is missing the #NotBlank annotation for Collections that would pretty much fit the scenario you are describing. Basically, as you mentioned, you would require to implement a custom validator that will programatically check that the contents of the collection ensuring that none of the elements inside it are null.
Here is an example of the custom validator you would need:
public class CollectionNotBlankValidator implements
ConstraintValidator<CollectionNotBlank, Collection> {
#Override
public void initialize(CollectionNotBlank constraintAnnotation) {
}
#Override
public boolean isValid(Collection value, ConstraintValidatorContext context) {
Iterator<Object> iter = value.iterator();
while(iter.hasNext()){
Object element = iter.next();
if (element == null)
return false;
}
return true;
}
}
As you can see I have named the custom annotaion CollectionNotBlank. Here is an example of the code for the custom annotation:
#Target(FIELD)
#Retention(RUNTIME)
#Constraint(validatedBy = CollectionNotBlankValidator.class)
#ReportAsSingleViolation
#Documented
public #interface CollectionNotBlank {
String message() default "The elements inside the collection cannot be null values.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

Consistent JSR-303 validation with optional properties in Spring controllers and Spring webflow?

Let's say I have a Model Bean "Vendor" with the mandatory property "name" and an optional property "email".
class Vendor {
#Email
private String email;
#NotNull
private String name;
}
#Email allows Null!
I want to use hibernate-validation in my #Controller and in my flows as well. I have in both scenarios the problem that a posted form with an empty field for email binds with "" and not NULL.
StringTrimmerEditor seems to solve my problem by converting "" into NULL. For my #Controller I found SPR-7077 which suggests implementing StringTrimmerEditor globally with #ControllerAdvice. But this doesn't work for my flows.
How do I achieve that globally empty Strings ("") are converted into NULL for JSR-303 validation in spring webflow?
I seem to have found a configuration which supports Null values for empty Strings. For my #Controller I stick with the StringTrimmerEditor in my #ControllerAdvice. Nothing new here.
For SWF ConversionService seems to do the trick: First I create a Converter which converts "" into Null:
public class StringToNullConverter implements Converter<String, String> {
#Override
public String convert(String source) {
if (StringUtils.isEmpty(source)) {
return null;
}
return source;
}
}
Now I have to register this in Spring:
import org.springframework.core.convert.*;
#Configuration
public class SpringConfiguration {
#Bean public ConversionService conversionService() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
conversionService.addConverter(new StringToNullConverter());
return conversionService;
}
}
Until now nothing new happens and we're talking about the org.springframework.core.convert package. Now comes the SWF glue into the org.springframework.binding.convert package:
import org.springframework.binding.convert.*;
#Bean public ConversionService flowConversionService(org.springframework.core.convert.ConversionService conversionService) {
DefaultConversionService service = new DefaultConversionService(conversionService);
return service;
}
Wire this into SWF with
<webflow:flow-builder-services [..] conversion-service="flowConversionService" />.
This looks a bit too much, but it does the job. I'm sure there must be a better way, as I have two different implementations (StringTrimmerEditor and StringToNullConverter) to achieve the same thing. For me it looks like ConversionService seems to be the one and only way to go. But I didn't figure out how to get it done for a Spring #Controller.
you can create annotation contains #ConstraintComposition if you want to using optional field.
For example:
import org.hibernate.validator.constraints.ConstraintComposition;
import org.hibernate.validator.constraints.Length;
import org.hibernate.validator.constraints.NotBlank;
#ConstraintComposition
#Target({METHOD, FIELD, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = {})
#Documented
#NotBlank
#Length(min = 1, max = 5)
#Pattern(regexp = "[A-Z]*")
public #interface SpecialField {
String message() default "{}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
and
#ConstraintComposition(OR)
#Target({METHOD, FIELD, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = {})
#Documented
#Null
#SpecialField
public #interface OptionalSpecialField {
String message() default "{}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
and after that mark optional filed that:
class NewUser {
#OptionalSpecialField
private String firstName;
#SpecialField
private String lastName;
#Min(18)
private Integer age;
}
First name is not required but if is not null then have to be not blank, match pattern, etc.
All example is on here: https://github.com/lukaszguz/optional-field-validation/tree/master/src/main/java/pl/guz/domain/validation

Type check in JSR-303 custom validator initialize method

I'm attempting to create a class level JSR-303 validation definition that checks that one property occurs before another in time. Because the this validation only makes sense for Calendar properties I was wondering if it is possible to test the property type in the initialize method.
My annotation definition is:
#Target({TYPE, ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = TemporalSequenceValidator.class)
#Documented
public #interface TemporalSequence {
String message() default "{uk.co.zodiac2000.vcms.constraints.TemporalSequence}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String first();
String second();
}
and the validator implementation:
public class TemporalSequenceValidator implements
ConstraintValidator<TemporalSequence, Object> {
private String firstFieldName;
private String secondFieldName;
#Override
public void initialize(final TemporalSequence constraintAnnotation) {
firstFieldName = constraintAnnotation.first();
secondFieldName = constraintAnnotation.second();
// Is it possible to test type of firstFieldName and
// secondFieldName properties here?
}
#Override
public boolean isValid(final Object value, final ConstraintValidatorContext context) {
// omitted
}
}
Is this a sensible thing to do? What approach would you suggest I use if it is? And what action should occur if the properties are not of the correct type?
You can't really do the check in initialize() since you can't access the validated object there. Instead you could check the type of the fields of the validated object in isValid() using reflection:
if ( !Calendar.class.isAssignableFrom(
value.getClass().getField( firstFieldName ).getType() ) ) {
throw new ValidationException( "Field " + firstFieldName + " is not of type Calendar." );
}

Resources