And / Or condition in Spring annotation based validation - spring

I am using Spring 3 annotation based validation. I want to add a following validation for String fields
Field can be Null OR it should contain a non empty string
I know annotation like #Null, #NotEmpty but how I can use both with a OR condition?
Solution:
Using #Size(min=1) helps but it don't handle spaces. So added a custom annotation NotBlankOrNull which will allow null and non empty strings also it takes care of blank spaces. Thanks a lot #Ralph.
Here is my Annotation
#Documented
#Constraint(validatedBy = { NotBlankOrNullValidator.class })
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
public #interface NotBlankOrNull {
String message() default "{org.hibernate.validator.constraints.NotBlankOrNull.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
Validator class
public class NotBlankOrNullValidator implements ConstraintValidator<NotBlankOrNull, String> {
public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
if ( s == null ) {
return true;
}
return s.trim().length() > 0;
}
#Override
public void initialize(NotBlankOrNull constraint) {
}
}
I have also updated it on my site.

First of all, it is not Spring annotation based validation, it is JSR 303 Bean Validation, implemented for example by Hibernate Validation. It is really not spring related/
You can not combine the annotations in an OR way*.
But there is a simple workaround for the not null constraint, because the most basic validations accept null as an valid input (therefore you often need to combine the basic vaidations and an extra #NotNull, if you want to have a "normal" behavior but not what you asked for).
For Example:
#javax.validation.constraints.Size accept null as an valid input.
So what you need in your case is use #Size(min=1) instead of #NotEmpty.
BTW: Not #NotEmpty is just an combination of #NotNull and #Size(min = 1)
*except you implement it by your self.

Related

How can I create custom validator on Java List type?

I have one NumberConstraint as follows:
#Constraint(validatedBy = { StringConstraintValidator.class, })
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD, ElementType.PARAMETER, })
public #interface StringConstraint {
String message() default "'${validatedValue}' ist not valid Number. " +
"A String is composed of 7 characters (digits and capital letters). Valid example: WBAVD13.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
and it is validated by StringConstraintValidator as follows:
#Component
public class StringConstraintValidator implements ConstraintValidator<StringConstraint, String> {
private static final String REGEX_String = "^[A-Z0-9]{7}$";
#Override
public void initialize(final StringConstraint annotation) {
// noop
}
#Override
public boolean isValid(final String value, final ConstraintValidatorContext context) {
return isValid(value);
}
public boolean isValid(final String value) {
// Number is not always null Checked via #NotNull
LoggingContext.get().setString(value);
if (value == null) {
return false;
}
return Pattern.compile(REGEX_STRING).matcher(value).matches();
}
}
I can apply this StringConstraint on single field of request object as follows:
#StringConstraint
private String number;
but if my request object contains a List of Strings then how can I use this constraint on entire List or do I have to define new on List type ?? Something like ConstraintValidator<StringConstraint, List ???>
My request object is:
#JsonProperty(value = "items", required = true)
#Schema(description = "List of items.", required = true)
private List<String> items= new ArrayList<>();
So i want to apply my validator on all the strings in the list. How can i apply #StringConstraint on my list ?
Yes, you can add more validators for one constraint by using a comma-separated list of validator classes in the validatedBy attribute of the #Constraint annotation. For example, you can write:
#Constraint(validatedBy = {StringConstraintValidator.class, BlablaValidot.class})
public #interface MyConstraint {
// other attributes
}
Explanation
The #Constraint annotation is used to define a custom constraint annotation that can be applied to fields, methods, classes, etc. The validatedBy attribute specifies one or more classes that implement the ConstraintValidator interface and provide the logic to validate the annotated element. You can use multiple validators for the same constraint if you want to check different aspects or conditions of the value. For example, you can have one validator that checks the length of a string and another that checks the format of a string.
Examples
Here are some examples of custom constraint annotations with multiple validators:
A #PhoneNumber annotation that validates a phone number using two validators: one for the country code and one for the number format.
#Constraint(validatedBy = {CountryCodeValidator.class, PhoneNumberValidator.class})
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface PhoneNumber {
String message() default "Invalid phone number";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
A #Password annotation that validates a password using three validators: one for the minimum length, one for the maximum length, and one for the presence of special characters.
#Constraint(validatedBy = {MinLengthValidator.class, MaxLengthValidator.class, SpecialCharValidator.class})
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface Password {
String message() default "Invalid password";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
int minLength() default 8;
int maxLength() default 20;
String specialChars() default "!##$%^&*";
}
A custom validator in Java is a way to define your own rules for validating the data of your objects or parameters. You can use the javax.validation API to create and use custom validators. The API consists of two main components: annotations and validators.
Annotations are used to declare the constraints that you want to apply on your data. They are usually placed on the fields or parameters that you want to validate. You can use the built-in annotations provided by the API, such as #NotNull, #Size, #Pattern, etc., or you can define your own annotations for your custom constraints.
Validators are classes that implement the ConstraintValidator interface and provide the logic for validating the data against the constraints. The interface has two generic parameters: A, which is the annotation type, and T, which is the data type. The interface has two methods: initialize and isValid. The initialize method is used to initialize the validator with the annotation attributes, and the isValid method is used to check if the data is valid or not according to the annotation.
To create a custom validator for a list type, you need to specify the list type as the second generic parameter of the ConstraintValidator interface, and implement the isValid method to iterate over the list elements and validate them individually. You can use any logic that suits your needs, such as checking the length, format, range, etc. of the elements. You can also use other annotations or validators inside your custom validator to reuse the existing validation rules.
Example
Here is an example of how to create a custom validator for a list of strings that checks if each element is a valid number.
Define the annotation
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
#Documented
#Constraint(validatedBy = StringConstraintValidator.class)
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface NumberConstraint {
String message() default "Invalid number";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Define the validator
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import java.util.List;
public class StringConstraintValidator implements ConstraintValidator<NumberConstraint, List<String>> {
#Override
public void initialize(NumberConstraint constraintAnnotation) {
// You can use this method to initialize the validator with the annotation attributes
}
#Override
public boolean isValid(List<String> value, ConstraintValidatorContext context) {
// You can use this method to validate the list elements
if (value == null || value.isEmpty()) {
return true; // You can change this to false if you want to reject null or empty lists
}
for (String s : value) {
try {
Double.parseDouble(s); // Try to parse the string as a double
} catch (NumberFormatException e) {
return false; // If the string is not a valid number, return false
}
}
return true; // If all the strings are valid numbers, return true
}
}
Apply the annotation
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.List;
public class Request {
#NotNull
#NumberConstraint
private List<String> numbers;
// Constructor, getters, setters, etc.
}
public class Controller {
public void processRequest(#Valid Request request) {
// Do something with the request
}
}
If you want to unit test the method
public void processRequest(#Valid Request request) {
// Do something with the request
}
with an invalid request, then the output should be an exception or an error message, depending on how you handle the validation.
Explanation
The #Valid annotation is used to indicate that the parameter should be validated before entering the method. This means that the request object should have some constraints or rules that define what makes it valid or invalid. For example, the request might have a required field, a maximum length, a specific format, etc.
If the request object does not meet these constraints, then the validation will fail and the method will not be executed. Instead, an exception will be thrown or an error message will be returned, depending on the implementation of the validation mechanism. This is to prevent the method from processing an invalid request that might cause unexpected behavior or errors.
Example
Suppose the Request class has the following constraints:
public class Request {
#NotNull
#Size(min = 1, max = 10)
private String name;
#Email
private String email;
// getters and setters
}
This means that the request object should have a non-null name that is between 1 and 10 characters long, and a valid email address. If we use the javax.validation API to perform the validation, then we can write a unit test like this:
import static org.junit.Assert.*;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.validation.ValidatorFactory;
import org.junit.Before;
import org.junit.Test;
public class RequestProcessorTest {
private RequestProcessor requestProcessor;
private Validator validator;
#Before
public void setUp() {
requestProcessor = new RequestProcessor();
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
validator = factory.getValidator();
}
#Test
public void testProcessRequestWithInvalidRequest() {
// create an invalid request object
Request request = new Request();
request.setName(""); // empty name
request.setEmail("invalid.com"); // invalid email
// validate the request object
Set<ConstraintViolation<Request>> violations = validator.validate(request);
// assert that there are violations
assertFalse(violations.isEmpty());
// try to process the request
try {
requestProcessor.processRequest(request);
fail("Should throw ConstraintViolationException");
} catch (ConstraintViolationException e) {
// assert that the exception contains the violations
assertEquals(violations, e.getConstraintViolations());
}
}
}

How to implement validation on 2 dependent fields?

Hi everyone iam writing post api in spring boot and i need to implement this validation
i have 2 fields - order type and additional details, order type is enum class which consists of 2 enum's - a and b , suppose if "a" is given as input it will proceed as it is - no validation required, suppose if it's "b" then additional details field must required, this is my requirement --- this is a post call
There are multiple ways to achieve that.
Firstly, javax.validation.constraints package has an #AssertTrue annotation, using which you can define a method in your class with some validation logic, e.g. like this:
#AssertTrue(message = "secondField must not be null if firstField is B")
public boolean isValid() {
return SomeEnum.A.equals(firstField) || secondField != null;
}
Secondly, you can define your own validation annotation - similar to how annotations are implemented in validation package - and a validator that implements ConstraintValidator. E.g. like this:
#ValidMyObject
public class MyObject {
private SomeEnum firstField;
private Object secondField;
// getters, setters, etc.
}
#Target({TYPE,ANNOTATION_TYPE})
#Retention(RUNTIME)
#Constraint(validatedBy = MyObjectValidator.class)
public #interface ValidMyObject {
String message() default "secondField must not be null if firstField is B";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
#Component
public class MyObjectValidator
implements ConstraintValidator<ValidMyObject, MyObject> {
#Override
public boolean isValid(MyObject obj, ConstraintValidatorContext context){
return SomeEnum.A.equals(obj.getFirstField()) || obj.getSecondField != null;
}
}
Note that the #Target annotation defines that this annotation can be used at a TYPE level - on a class, to perform the validation of the entire MyObject object.
Take a look here for details.
P.S. Don't forget about dependencies for validation implementation: for spring-boot the most popular one is 'org.springframework.boot:spring-boot-starter-validation'.

How to apply JSF Validator after annotations constraints?

My problem is more about performance than functionality.
I am dealing with an email field for a sign up form.
For its validation, I use annotations in the User entity for the email format, and an EmailExistValidator to check in the database if this email is already used.
#Entity
#Table(name = "Users")
public class User {
#Column(name = "email")
#NotNull(message = "Email address required")
#Pattern(regexp = "([^.#]+)(\\.[^.#]+)*#([^.#]+\\.)+([^.#]+)", message = "Invalid email address")
private String email;
// ... (other fields and getters/setters here)
}
The validator:
#FacesValidator(value = "emailExistValidator")
public class EmailExistValidator implements Validator {
private static final String EMAIL_ALREADY_EXISTS = "This email address is already used.";
#EJB
private UserDao userDao;
#Override
public void validate(FacesContext context, UIComponent component, Object value)
throws ValidatorException {
String email = (String) value;
try {
if (email != null && userDao.findByEmail(email) != null) {
throw new ValidatorException(new FacesMessage(FacesMessage.SEVERITY_ERROR,
EMAIL_ALREADY_EXISTS, null));
}
} catch (DaoException e) {
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(),
null);
FacesContext facesContext = FacesContext.getCurrentInstance();
facesContext.addMessage(component.getClientId(facesContext), message);
}
}
}
According to my tests, if an email does not verify the regexp pattern, the validator is still applied. I don't like the idea of performing a database query to check for email existence while I already know this email is not even valid. It could really slow down the performance (even more when using ajax on blur events in the form).
I have some guesses but I couldn't find clear answers, that's why I ask a few questions here:
Is this a bad thing to mix a validator with annotation constraints?
During the validation, is every constraint checked even if one of them does not pass? (If yes, then are all the messages (for each constraint) added to the FacesContext for the component, or just the first message?)
If yes to question 2, is there any way to force the validation to stop as soon as one constraint is not verified?
If no to question 2 or yes to question 3, is the order of application of the constraints/validator specified somewhere? Is this order customizable?
If this matters, I'm using PrimeFaces for my Facelets components.
I know I could put everything in the validator and stop whenever, but this validator is to be used only for the Sign Up form. When used for signing in, I would only check the entity's annotation constraints, not the "already exists" part. In addition, annotations and single constraint validators are to my mind more readable than the content of a multi-constraint validator.
JSF validation runs by design before JSR303 bean validation. So there's technically no way to skip JSF validation when bean validation fails. If they would run the other way round, then you would theoretically simply have checked UIInput#isValid() in the JSF validator:
UIInput input = (UIInput) component;
if (!input.isValid()) {
return;
}
There's unfortunately no API-provided way to control the JSF-JSR303 validation order. Your best bet is to turn the JSF validator into a true JSR303 bean validator and assign it to a different group which you declare to run after the default group. In JSR303, constraints are validated on a per-group basis. If one validation group fails, then any subsequent validation groups are not executed.
First create a custom JSR303 bean validation constraint annotation:
#Target({ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = UniqueEmailValidator.class)
#Documented
public #interface UniqueEmail {
String message() default "{invalid.unique.email}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Then create a custom JSR303 bean validation constraint validator:
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, String> {
#Override
public void initialize(UniqueEmail annotation) {
// Grab EJB here via JNDI?
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
return userDao.findByEmail(value) == null;
}
}
Unfortunately, #EJB/#Inject in a ConstraintValidator isn't natively supported in Java EE 6 / JSR303 Bean Validation 1.0. It's only supported in Java EE 7 / JSR303 Bean Validation 1.1. They are however available via JNDI.
Then create a custom validation group:
public interface ExpensiveChecks {}
And finally use it on your entity whereby the group ordering is declared via #GroupSequence annotation (the default group is identified by current class):
#Entity
#Table(name = "Users")
#GroupSequence({ User.class, ExpensiveChecks.class })
public class User {
#Column(name = "email")
#NotNull(message = "Email address required")
#Pattern(regexp = "([^.#]+)(\\.[^.#]+)*#([^.#]+\\.)+([^.#]+)", message = "Invalid email address")
#UniqueEmail(groups = ExpensiveChecks.class, message = "This email address is already used")
private String email;
// ...
}

GWT JSR303 Validation, validate method OR use custom annotations

I'm trying to use GWT's 2.5 in-build validation feature. I have a few complex validations.
Cross field validation with Hibernate Validator (JSR 303) suggests that I could either include methods which do the validation OR write my own annotations. However, both don't work.
public class PageData extends Serializable
#NotNull(message="Cannot be null!")
Boolean value
#AssertTrue(message="isValid() is false!")
private boolean isValid() {
return false;
}
//Getters and Setters
}
Boolean value is validated. However, isValid() is never called/validated. But why?
Is this a GWt specific problem?
Then I tried to write my own annotation, The #FieldMatch example in Cross field validation with Hibernate Validator (JSR 303) uses Beans.getProperty() from Apache Commons BeanUtils, which I cannot use in GWT. Is there any way to make these kind of complex annotations work in GWT?
Here is how I created a custom validation that works across multiple fields of one bean. It checks that when the field ContactProfile for a Contact bean is set to COMPANY, then company name must be filled out, otherwise when set to PERSON, the first name or last name must be filled out :
Annotation definition :
#Target({ ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = ValidCompanyOrPersonValidator.class)
public #interface ValidCompanyOrPerson {
String message() default "{contact.validcompanyorperson}";
Class<?>[] groups() default {};
Class<? extends Contact>[] payload() default {};
}
Implementation :
public class ValidCompanyOrPersonValidator implements ConstraintValidator<ValidCompanyOrPerson, Contact> {
ValidCompanyOrPerson annotation;
public void initialize(ValidCompanyOrPerson annotation) {
this.annotation = annotation;
}
#SuppressWarnings("nls")
public boolean isValid(Contact contact, ConstraintValidatorContext context) {
boolean ret = false;
if (contact.getContactProfile() == null) {
} else if (contact.getContactProfile().equals(ContactProfile.COMPANY)) {
ret = (contact.getCompanyName() != null);
} else if (contact.getContactProfile().equals(ContactProfile.PERSON)) {
ret = (contact.getGivenName() != null || contact.getFamilyName() != null);
}
return ret;
}
}
Now I can set
#ValidCompanyOrPerson
public class Contact {
...
}
I can use this validation both client (GWT) and server side.
Hope that helps ....

Conditional validations in Spring

I have a form which contain some radio button and check-boxes. Whenever user selects/deselects a radio button or check-box few other input fields are enabled/disabled.
I want to add validation only for fields which are enabled when user submits the form. Fields which are disabled at the time of submission should not be considered for validation.
I don't want to add this in client side validation.Is there any better /easy way to implement conditional validation in Spring 3.0 instead of adding multiple if in validator ?
Thanks!
If you use JSR 303 Bean validation then you can use validation groups (groups) for this.
Assume you have this user input, containing two sections.
The two booleans indicating if the sections are enabled or disabled. (Of course you can use more useful annotations than #NotNull)
public class UserInput {
boolean sectionAEnabled;
boolean sectionBEnabled;
#NotNull(groups=SectionA.class)
String someSectionAInput;
#NotNull(groups=SectionA.class)
String someOtherSectionAInput;
#NotNull(groups=SectionB.class)
String someSectionBInput;
Getter and Setter
}
You need two Interfaces for the groups. They work only as marker.
public interface SectionA{}
public interface SectionB{}
Since Spring 3.1 you can use the Spring #Validated annotation (instead of #Validate) in your controller method to trigger the validation:
#RequestMapping...
public void controllerMethod(
#Validated({SectionGroupA.class}) UserInput userInput,
BindingResult binding, ...){...}
Before Spring 3.1 there was no way to specify the validation group that should been used for validation (because #Validated does not exist and #Validate does not have a group attribute), so you need to start the validation by hand written code: This an an example how to trigger the validation in dependence to witch section is enabled in Spring 3.0.
#RequestMapping...
public void controllerMethod(UserInput userInput,...){
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
List<Class<?>> groups = new ArrayList<Class<?>>();
groups.add(javax.validation.groups.Default.class); //Always validate default
if (userInput.isSectionAEnabled) {
groups.add(SectionA.class);
}
if (userInput.isSectionBEnabled) {
groups.add(SectionB.class);
}
Set<ConstraintViolation<UserInput>> validationResult =
validator.validate(userInput, groups.toArray(new Class[0]));
if(validationResult.isEmpty()) {
...
} else {
...
}
}
(BTW: For the Spring 3.0 solution, it is also possible to let Spring inject the validator:
#Inject javax.validation.Validator validator
<mvc:annotation-driven validator="validator"/>
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource" ref="messageSource" />
</bean>
)
When fields are disabled they are transferred to controller as null. I wanted to add a validation which will allow either null i.e disabled field or not blank field i.e. enabled but empty field.
So I created a custom annotation NotBlankOrNull which will allow null and non empty strings also it takes care of blank spaces.
Here is my Annotation
#Documented
#Constraint(validatedBy = { NotBlankOrNullValidator.class })
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
public #interface NotBlankOrNull {
String message() default "{org.hibernate.validator.constraints.NotBlankOrNull.message}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
Validator class
public class NotBlankOrNullValidator implements ConstraintValidator<NotBlankOrNull, String> {
public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) {
if ( s == null ) {
return true;
}
return s.trim().length() > 0;
}
#Override
public void initialize(NotBlankOrNull constraint) {
}
}
I have also updated more details on my site.

Resources