Bean validation on REST API input, and API versioning - java

I have a bean like:
#Data
public static class ClassUnderTest {
#NotNull
private String field1;
#NotNull
#Since(2.0)
private String field2;
#NotNull
#Until(3.0)
private String field3;
}
#Since and #Until are Gson annotations that permits to avoid serializing / deserializing some fields on my REST API, for certain API versions.
I perform bean validation on the input payload of this API to raise constraint violations.
I'd like to be able to not raise the same violations per version, based on the Gson annotations (and not groups!).
Is there a way, from a ConstraintViolation<T>, to get the Member (like Method / Field) which produced the violation, so that I check if it is annotated by something?
Is there a way to handle validation versionning with Bean Validation?
The only solution I have seems to retrieve that member from the path (getPropertyPath), but it seems not easy to do...

You could retrieve the property name from the violation via getPropertyPath(). That said, assigning your constraints to groups corresponding to the versions might be the better approach. Note that validation groups can also extend existing groups, this might be helpful to model constraints added in a newer version.

Related

hibernate validator error when #NotEmpty used on return type

this is my sample application with it's test. I've defined some constraints on repository as #NotEmpty. When I want to call this repository I'm getting following issue
javax.validation.ConstraintDeclarationException: HV000141: The constraint org.hibernate.validator.constraints.NotEmpty used ConstraintTarget#IMPLICIT where the target cannot be inferred.
Do I need to implement validator for #NotEmpty or is this a bug in hibernate or my spring-boot application?
note when I remove #NotEmpty from return type, it works.
You are running into an interesting corner case here, where for a purely composed constraint it cannot be determined whether it applies to the parameters or the return value of a method. Back in the time of Bean Validation 1.1 we decided to leave it to implementations to handle this case as it's very rare.
Hibernate Validator allows to resolve this case by specifying #SupportedValidationTarget(ValidationTarget.ANNOTATED_ELEMENT) on the constraint definition. This was added in HV 5.2, but then it seems we forgot to make use of it for #NotEmpty and potentially other purely composed constraints defined by HV. Could you therefore please file a bug report in our JIRA instance?
Note that you shouldn't have the problem with the #NotEmpty defined in Bean Validation 2.0 (HV 6.0), as this isn't declared as a purely composed constraint.
Update: this issue has been fixed in Hibernate Validator 6.0.3.Final (and backported to 5.3.6.Final and 5.4.2.Final).

Multiple constraints spring validation

I am using spring to validate a form. The model for the form is similar to this:
public class FormModel {
#NotBlank
private String name;
#NotNull
#ImageSizeConstraint
private MultipartFile image;
}
The '#ImageSizeConstraint' is a custom constraint. What I want is for the #NotNull to be evaluated first and if this evaluates to false, not to evaluate #ImageSizeConstraint.
If this is not possible, I will have to check for null in the custom constraint as well. Which is not a problem, but I would like to seperate the concerns (not null / image size / image / aspectratio / etc).
You may use constraints grouping and group sequences to define the validation order. According to JSR-303 (part 3.5. Validation routine):
Unless ordered by group sequences, groups can be validated in no
particular order. This implies that the validation routine can be run
for several groups in the same pass.
As Hibernate Validator documentation says:
In order to implement such a validation order you just need to define
an interface and annotate it with #GroupSequence, defining the order
in which the groups have to be validated (see Defining a group
sequence). If at least one constraint fails in a sequenced group, none
of the constraints of the following groups in the sequence get
validated.
First, you have to define constraint groups and apply them to the constraints:
public interface CheckItFirst {}
public interface ThenCheckIt {}
public class FormModel {
#NotBlank
private String name;
#NotNull(groups = CheckItFirst.class)
#ImageSizeConstraint(groups = ThenCheckIt.class)
private MultipartFile image;
}
And then, as constraints are evaluated in no particular order, regardless of which groups they belong to (Default group too), you have to create #GroupSequence for your image field constraints groups.
#GroupSequence({ CheckItFirst.class, ThenCheckIt.class })
public interface OrderedChecks {}
You can test it with
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<FormModel>> constraintViolations =
validator.validate(formModel, OrderedChecks.class);
To apply this validation in Spring MVC Controller methods, you may use the #Validated annotation, which can specify the validation groups for method-level validation:
#PostMapping(value = "/processFormModel")
public String processFormModel(#Validated(OrderedChecks.class) FormModel formModel) {
<...>
}
Easy just return true for isValid if image is null for your custom constraint.
Read the specification of JSR-303, you will see that this is normal behaviour and it makes sense as there is "NotNull".

How to impose common constraint on class with javax.validation library?

I am using Bean Validation constraints to validate class instances at run time. I have many DTOs where each has multiple fields often with a common constraint. I want to add a constraint to the class so that it applies to all the properties of the class. (As lombok #NotNull constraint).
e.g
class Person {
#NotNull
private String name;
#NotNull
private String address;
#NotNULL
private String contact;
}
I want to make it something like this.
#NotNull
class Person {
private String name;
private String address;
private String contact
}
You cannot do this with plain Bean Validation. So just adding #NotNull to the class won't work.
One potential approach could be to utilize XML configuration. The idea would be to have your own annotation MyNotNull. Your framework would need to scan for these annotations and build the programmatic configuration for the class in question. This could for example be done via a annotation processor during compile time. Once you have the constraint mapping XML files add them to your jar and reference them in validation.xml. That's just a basic idea. Personally, I am not sure whether it is worth the effort.

How to make a property injection via #Value annotation mandatory?

I have the following Test.java POJO class being populated from a property file using the #ConfigurationProperties annotation. I have seen the usage of #Required annotation to make it a mandatory.
Rather than defining annotations at the setter method level, are there any annotations or options within the #Value annotation that I can use for defining conditions like Mandatory, NotNull, etc?
#Component
#ConfigurationProperties(prefix = "com.test")
public class Test {
private String name;
#Required
public void setName(String name) {
name = name;
}
public String getName(String name) {
name = name;
}
}
Is this the right and only way for making a particular attribute mandatory? What are the other such annotations I could use for such conditions or validations purpose?
You can use validation for the properties, just not in the way you envisaged.
What you can do is use standard validation annotations (like #NotNull etc.) on the fields (or setters) themselves.
For example
#NotNull
#Size(min=2, max=10)
private String name;
Check out this part of the documentation
What the documentation essentially says, is that you simply have to have a compatible JSR303 validator implementation on the classpath, and use the relevant annotations.
Spring Boot will take care of the rest
According to the Spring docs (currently 4.1.6.RELEASE), the Value annotation only has a single property, value, containing the value of the property. You can put a Spring EL expression in this, but that won't let you explicitly express notions like non-nullity.
Further, in your code snippet you're using #ConfigurationProperties which is an alternative approach to configuring property values, compared to the #Value annotation.
The way you're doing it, your Java getter/setter names need to map to the property names, i.e. prefix "com.test" + getName() / setName() matches property com.test.name=...
So, you don't need the #Value annotation to tell Spring what property to use.
With the #Value approach, your getters/setters don't have to match the property names, but you do have to annotate each property e.g. #Value("${com.test.name}") and on the class, a #PropertySource annotation pointing to the properties file that contains com.test.name=...
I found a couple of blog posts with code examples that use the 2 different ways to inject the same properties: http://blog.codeleak.pl/2014/09/using-configurationproperties-in-spring.html and http://blog.codeleak.pl/2014/09/testing-mail-code-in-spring-boot.html

How to use Java Bean Validators (JSR-303/JSR-349) on elements of an array/list/collection

I'm new to using Java Bean validation (JSR-303/JSR-349/Hibernate Validator), and understand the general concepts. However, I'm not sure how to validate the contents of an composed type vs the type itself.
For example:
#NotNull
private List<String> myString;
will validate that the List myString is not null, but does nothing for validating the contents of the list itself. Or given other types of validators (Min/Max/etc), how do I validate the individual elements of the List? Is there a generic solution for any composed type?
There is no easy generic solution as of Bean Validation 1.0/1.1. You could implement a custom constraint like #NoNullElements:
#NoNullElements
private List<String> myStrings;
The constraint's validator would iterate over the list and check that no element is null. Another approach is to wrap your String into a more domain-specific type:
public class EmailAddress {
#NotNull
#Email
private String value;
//...
}
And apply cascaded validation to the list via #Valid:
#Valid
private List<EmailAddress> addresses;
Having such a domain-specific data type is often helpful anyways to convey a data element's meaning as it is passed through an application.
In the future a generic solution for the issue may be to use annotations on type parameters as supported by Java 8 but that's only an idea at this point:
private List<#NotNull String> myStrings;
Take a look at validator-collection – it’s very easy to use any Constraint Annotation on a collection of simple types with this library. Also see https://stackoverflow.com/a/16023061/2217862.
Upcoming jsr 380 (bean validation 2.0) will allow to put constraints annotation to argument type.
#Valid
private List<#NotNull String> myString;
For now Bean Validation 1.1 in you can create custom constraints that checks null condition.
Bean Validation 2.0/Hibernate Validator 6.0
Bean Validation 2.0 (of which Hibernate Validator 6.0 is the reference implementation) allows using its validation annotations directly on generic type arguments. This is noted in the Hibernate 6.0 release documentation:
Hibernate Validator 6.0 is the Reference Implementation of the Bean Validation 2.0 specification so it comes with all its new features:
First class support of container element constraints and cascaded validation (think private Map<#Valid #NotNull OrderCategory, List<#Valid #NotNull Order>> orderByCategories;);
If the project is using Java 8 with Bean Validation 2.0, this feature can be used to validate each element of the list:
private List<#NotNull String> myString; // Validate that none of the list items is null
Bean Validation 1.2/Hibernate Validator 5.2
Hibernate 5.2 (with Bean Validation 1.2) added a limited version of the feature to allow validation annotations directly on generic type arguments. However, none of its built-in Bean Validation or Hibernate Validation constraints could be used in this manner, as the annotations do not specify ElementType.TYPE_USE for backwards-compatibility reasons. Additionally, type argument constraints could be specified for map values but not map keys. This is all described in the Hibernate Validator 5.2 documentation:
Starting from Java 8, it is possible to specify constraints directly on the type argument of a parameterized type. However, this requires that ElementType.TYPE_USE is specified via #Target in the constraint definition. To maintain backwards compatibility, built-in Bean Validation as well as Hibernate Validator specific constraints do not yet specify ElementType.TYPE_USE.
[...]
When applying constraints on an Iterable type argument, Hibernate Validator will validate each element.
[...]
Type argument constraints are also validated for map values. Constraints on the key are ignored.
Summary
In summary, if the code is using Java 8 with Bean Validation 2.0 (such as Hibernate Validator 6), the generic list & map type arguments can be annotated:
private List<#NotNull String> myString;
If the code is using Java 8 with Bean Validation 1.2 and Hibernate Validator 5.2, custom validation annotations can be written with TYPE_USE in its definition, and applied to the generic type of the collection or map value:
private List<#MyCustomNotNull String> myString;
If the code is not using Java 8 or is on a version of Hibernate Validator prior to 5.2, a custom constraint could be written which verifies every element of a collection or map and applied to the collection or map itself.
#MyCustomNotNullElements
private List<String> myString;

Categories