how to perform validations dynamically in spring? - spring

I made a custom validation annotation for unique email (When user registers itself, program checks if email is already in database).
Everything works just fine, but when I need to modify user's info and not to create a new one I run into a problem that says "Email is already in use"
Can I somehow turn off only #EmailAvailablity validation(I need the others like email pattern and password validation)?
All validation annotations are in the same User bean.
These ARE MY CUSTOM VALIDATOR CLASS
User AVailablity
package com.vicky.finalYearProject.customValidations;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
import javax.validation.Payload;
#Constraint(validatedBy = UserAvailabilityValidator.class)
#Target({ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
public #interface UserAvailability {
public String message() default "Email is already Registered";
public Class<?>[] groups() default { };
public Class<? extends Payload>[] payload() default { };
}
Constraint Validator
package com.vicky.finalYearProject.customValidations;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import com.vicky.finalYearProject.Service.CheckUserAvailabilityService;
public class UserAvailabilityValidator implements ConstraintValidator<UserAvailability, String>{
CheckUserAvailabilityService availabilityService = new CheckUserAvailabilityService();
#Override
public void initialize(UserAvailability constraintAnnotation) {
ConstraintValidator.super.initialize(constraintAnnotation);
}
#Override
public boolean isValid(String email, ConstraintValidatorContext context) {
return availabilityService.isAvailable(email);//return true or false;
}
}
i have checked one question similar :
How to prevent Spring validation on update
but i cant understand it...
Thank You.

As this is a custom validator, you should consider adding the condition yourself, e.g. based on an ID which is only generated on save. For that purpose, making it a class-level constraint would be helpful, allowing you to check multiple values of your class.
Something like:
#Target(AnnotationTarget.CLASS)
#Retention(AnnotationRetention.RUNTIME)
#MustBeDocumented
#Constraint(validatedBy = [EmailAvailabilityValidator::class])
annotation class EmailAvailability(
val message: String = "Email must be available",
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<out Any>> = []
)
class EmailAvailabilityValidator : ConstraintValidator<EmailAvailability, User> {
override fun isValid(value: User?, context: ConstraintValidatorContext?): Boolean {
if (value == null) return true
if (value.id != null) return true // don't execute it on saved entities
// your extra validation
}
}
Alternatively, when using Hibernate, here's a proposal on making validations only run on insert: Hibernate validations on save (insert) only
Besides that I would consider moving this check out of the bean. It sounds like you do any kind of database interaction in the validation. Coupling the model to the database breaks encapsulation and limits your test capabilities. Adding e.q. uniqueness to a column would be an alternative.

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());
}
}
}

Deserialise JSON fields based on user role

I have some fields in a model that I only want to be returned when the logged in user has the role ROLE_ADMIN. I can use #JsonIgnore but that hides it for everyone. How can I make it hide dynamically?
You should use Jackson Json Views technology to acheive it - it allows to choose a different set of fields to be serialized programatically. It is also supported by Spring
Consider you have a class Model with two properties: commonField which should be available for everyone and secretField which should be available only for certain users. You should create an hierarchy of views (any classes would work) and specify which field is available in which view using #JsonView annotation
package com.stackoverflow.jsonview;
import com.fasterxml.jackson.annotation.JsonView;
public class Model {
public static class Public {}
public static class Secret extends Public {}
#JsonView(Public.class)
private String commonField;
#JsonView(Secret.class)
private String secretField;
public Model() {
}
public Model(String commonField, String secretField) {
this.commonField = commonField;
this.secretField = secretField;
}
public String getCommonField() {
return commonField;
}
public void setCommonField(String commonField) {
this.commonField = commonField;
}
public String getSecretField() {
return secretField;
}
public void setSecretField(String secretField) {
this.secretField = secretField;
}
}
Now you can specify the view you want to use in concrete ObjectMapper
package com.stackoverflow.jsonview;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import static org.junit.Assert.*;
/**
*/
public class ModelTest {
#Test
public void testSecretField() throws JsonProcessingException {
Model model = new Model("commonField","secretField");
assertEquals("{\"commonField\":\"commonField\",\"secretField\":\"secretField\"}", new ObjectMapper().writerWithView(Model.Secret.class).writeValueAsString(model));
assertEquals("{\"commonField\":\"commonField\"}", new ObjectMapper().writerWithView(Model.Public.class).writeValueAsString(model));
}
}
I am not sure if you can use declaratie approach to make spring choose the right view based on user role out of the box, so probably you will have to write some code like this:
#RequestMapping("/data")
public String getData(HttpServletRequest request) {
Model model = service.getModel();
ObjectMapper objectMapper = new ObjectMapper();
objectMapper = request.isUserInRole("ROLE_ADMIN") ? objectMapper.writerWithView(Model.Secret.class) : objectMapper.writerWithView(Model.Public.class);
return objectMapper.writeValueAsString(model);
}
I solved this after literally a full month of trying various things. I'm working with Spring 4.3.1 and boot, with data being returned in Hal using a pagedrepository.
extend RepositoryRestMvcConfiguration as MyRepositoryRestMvcConfiguration and add #Configuration to the class, make sure your starter class has #EnableWebMvc
add this to MyRepositoryRestMvcConfiguration- extend TypeConstrainedMappingJackson2HttpMessageConverter as MyResourceSupportHttpMessageConverter
add this to MyRepositoryRestMvcConfiguration
#Override
#Bean
public TypeConstrainedMappingJackson2HttpMessageConverter halJacksonHttpMessageConverter() {
ArrayList<MediaType> mediaTypes = new ArrayList<MediaType>();
mediaTypes.add(MediaTypes.HAL_JSON);
if (config().useHalAsDefaultJsonMediaType()) {
mediaTypes.add(MediaType.APPLICATION_JSON);
}
int order = config().useHalAsDefaultJsonMediaType() ? Ordered.LOWEST_PRECEDENCE - 10
: Ordered.LOWEST_PRECEDENCE - 1;
TypeConstrainedMappingJackson2HttpMessageConverter converter = new MyResourceSupportHttpMessageConverter(
order);
converter.setObjectMapper(halObjectMapper());
converter.setSupportedMediaTypes(mediaTypes);
converter.getObjectMapper().addMixIn(Object.class, MyFilteringMixin.class);
final FilterProvider myRestrictionFilterProvider = new SimpleFilterProvider()
.addFilter("MyFilteringMixin", new MyPropertyFilter()).setFailOnUnknownId(false);
converter.getObjectMapper().setFilterProvider(myRestrictionFilterProvider);
return converter;
}
Create an empty Mixin
package filters;
import com.fasterxml.jackson.annotation.JsonFilter;
#JsonFilter("MyFilteringMixin")
public class MyFilteringMixin {}
Create an empty Mixin
create class MyPropertyFilter extending SimpleBeanPropertyFilter and override adapt this method
serializeAsField(Object, JsonGenerator, SerializerProvider, PropertyWriter)you need to call either super.serializeAsField(pPojo, pJgen, pProvider, pWriter) or pWriter.serializeAsOmittedField(pPojo, pJgen, pProvider) depending on whether you wish to include or discard this particular field.
I added an annotation to the particular fields I wanted to alter and interrogated that annotation when deciding which of these two to call. I injected the security role and stored permitted roles in the annotation.
This alters what Hal shares out to the caller, not what Hal is holding in its repository. Thus you can morph it depending on who the caller is.

can #Email and spring custom validation coexists?

I need to validate Email and password while creating an account using web services. I am using Spring REST controller and plan to use #Email(hibernate validator) annotation for email id validation.For Password validation I have to write custom validator by implementing Validator interface.
#XmlRootElement
public class Account implements Serializable {
#Email(message =AccountErrorCodes.ACCOUNT_EMAIL_VALIDATION)
private String emailId;
private String password;
For password writing the own validator
#Autowired
private PasswordValidator passwordValidator;
#InitBinder
private void initBinder(WebDataBinder binder) {
binder.setValidator(passwordValidator);
}
It looks like both cant coexist. When I try to validate EmailId spring calls passwordValidator but fails to validate Email. I expect the failure due to incorrect Email id. When I disable the custom validator I get the required error message.
Looks like I am missing something. Do I need to do something to make it work? or it is not supported? If not supported Can I call Hibernate validator directly from spring custom validator to avoid writing my own validation for email?
Instead of injecting your custom validator in your controller, make a special #Password annotation for it that uses this as a validator. Remove any constraint logic from controller and do it all on your DTO class. Something like this:
#Target({ METHOD, FIELD, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = PasswordValidator.class)
public #interface Password {
String message() default "{Invalid password}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
You will find loads of tutorials for custom annotations that use your own made validators. Hope that helps.
You asked :
By Writing our own custom annotations can I still use Binding Result object that Spring provides which returns multiple error messages?
Yes .
You can make your own custom annotations for any type .
For example :
Phone Number
#Phone
#NotEmpty
private String phone;
Now make a java file :
Phone.java
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
import java.lang.annotation.RetentionPolicy;
import javax.validation.Constraint;
import javax.validation.Payload;
#Documented
#Constraint(validatedBy = PhoneValidator.class)
#Target( { ElementType.METHOD, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface Phone {
String message() default "{Phone}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And a class to provide validations measures :
PhoneValidator.java
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class PhoneValidator implements ConstraintValidator<Phone, String> {
#Override
public void initialize(Phone paramA) {
// TODO Auto-generated method stub
}
#Override
public boolean isValid(String phoneNo, ConstraintValidatorContext ctx) {
// TODO Auto-generated method stub
if(phoneNo == null) {
return false;
}
//Validate phone numbers of format "1234567890"
if (phoneNo.matches("\\d{10}")) return true;
//validating phone number with -, . or spaces
else if(phoneNo.matches("\\d{3}[-\\.\\s]\\d{3}[-\\.\\s]\\d{4}")) return true;
//validating phone number with extension length from 3 to 5
else if(phoneNo.matches("\\d{3}-\\d{3}-\\d{4}\\s(x|(ext))\\d{3,5}")) return true;
//validating phone number where area code is in braces ()
else if(phoneNo.matches("\\(\\d{3}\\)-\\d{3}-\\d{4}")) return true;
//return false if nothing matches the input
else return false;
}
}
Now in :
message_en.properties
Include :
#Custom validation annotation
Phone=Invalid format, valid formats are 1234567890, 123-456-7890 x1234
By this way , you will succesfully get a custom validator .
BindingResult
will catch the error and get the message from the properties file .
Hope this helps .

Validation with Spring 3.2.0

I'm using HibernateValidator 4.3.1. Validations are performed as intended throughout the entire application.
I have registered some custom editors to perform validation globally such as for ensuring numeric values (double, int etc) in a text-field, for ensuring valid dates regarding the Joda-Time API etc.
In this type of validation, I'm allowing null/empty values by setting the allowEmpty parameter to false as usual to validate it separately especially for displaying separate user friendly error messages when such fields are left blank.
Therefore, in addition to validating with HibernateValidator and custom editors, I'm trying to use the following validation strategy. Again, this kind of validation is only for those fields which are registered for custom editors are when left blank.
The following is the class that implements the org.springframework.validation.Validator interface.
package test;
import org.springframework.stereotype.Component;
import org.springframework.validation.Errors;
import org.springframework.validation.ValidationUtils;
import org.springframework.validation.Validator;
import validatorbeans.TempBean;
#Component
public final class TempValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
System.out.println("supports() invoked.");
return TempBean.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
TempBean tempBean = (TempBean) target;
System.out.println("startDate = " + tempBean.getStartDate() + " validate() invoked.");
System.out.println("doubleValue = " + tempBean.getDoubleValue() + " validate() invoked.");
System.out.println("stringValue = " + tempBean.getStringValue() + " validate() invoked.");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "startDate", "java.util.date.nullOrEmpty.error");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "doubleValue", "java.lang.double.nullOrEmpty.error");
}
}
The class is designated with the #Component annotation so that it can be auto-wired to a specific Spring controller class. The debugging statements display exactly based on the input provided by a user.
The following is the controller class.
package controller;
import customizeValidation.CustomizeValidation;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import javax.validation.groups.Default;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.DataBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import test.TempValidator;
import validatorbeans.TempBean;
#Controller
public final class TempController {
#Autowired
private TempService tempService;
private TempValidator tempValidator;
public TempValidator getTempValidator() {
return tempValidator;
}
#Autowired
public void setTempValidator(TempValidator tempValidator) {
this.tempValidator = tempValidator;
}
#RequestMapping(method = {RequestMethod.GET}, value = {"admin_side/Temp"})
public String showForm(#ModelAttribute("tempBean") #Valid TempBean tempBean, BindingResult error, Map model, HttpServletRequest request, HttpServletResponse response) {
return "admin_side/Temp";
}
#RequestMapping(method = {RequestMethod.POST}, value = {"admin_side/Temp"})
public String onSubmit(#ModelAttribute("tempBean") #Valid TempBean tempBean, BindingResult errors, Map model, HttpServletRequest request, HttpServletResponse response) {
//tempValidator.supports(TempBean.class);
//tempValidator.validate(tempBean, errors);
DataBinder dataBinder = new DataBinder(tempBean);
dataBinder.setValidator(tempValidator);
dataBinder.validate();
//errors=dataBinder.getBindingResult();
if (CustomizeValidation.isValid(errors, tempBean, TempBean.ValidationGroup.class, Default.class) && !errors.hasErrors()) {
System.out.println("Validated");
}
return "admin_side/Temp";
}
}
I'm invoking the validator from the Spring controller class itself (which I indeed want) by
DataBinder dataBinder = new DataBinder(tempBean);
dataBinder.setValidator(tempValidator);
dataBinder.validate();
The validator is called but the validation which is expected is not performed.
If only I invoke the validator manually using the following statement (which is commented out above),
tempValidator.validate(tempBean, errors);
then validation is performed. So I don't believe my validator is correctly working. Why does it fail to work with DataBinder?
In my application-context.xml file, this bean is simply configured as follows.
<bean id="tempValidator" class="test.TempValidator"/>
This many packages as below including the test package which the TempValidator class is enclosed within are auto-detected.
<context:component-scan base-package="controller spring.databinder validatorbeans validatorcommands test" use-default-filters="false">
<context:include-filter expression="org.springframework.stereotype.Controller" type="annotation"/>
<context:include-filter expression="org.springframework.web.bind.annotation.ControllerAdvice" type="annotation"/>
</context:component-scan>
I have even tried to put
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
In my dispatcher-servlet.xml file.
What am I overlooking here?
If I understand well what you try to achieve - distinguish between blank fields and incorrect values entered - you can use MUCH MORE SIMPLER approach:
public class MyBean {
#NotNull
#DateTimeFormat(pattern="dd.MM.yyyy HH:mm")
private DateTime date;
#NotNull
#Max(value=5)
private Integer max;
#NotNull
#Size(max=20)
private String name;
// getters, setters ...
}
Controller mapping:
public void submitForm(#ModelAttribute #Valid MyBean myBean, BindingResult result) {
if (result.hasErrors){
// do something}
else{
// do something else
}
}
Validation messages:
NotNull=Required field.
NotNull.date=Date is required field.
NotNull.max=Max is required field.
Size=Must be between {2} and {1} letters.
Max=Must be lower than {1}.
typeMismatch.java.lang.Integer=Must be number.
typeMismatch.org.joda.time.DateTime=Required format dd.mm.yyyy HH:mm
Spring configuration:
#Configuration
public class BaseValidatorConfig {
#Bean
public LocalValidatorFactoryBean getValidator() {
LocalValidatorFactoryBean lvfb = new LocalValidatorFactoryBean();
lvfb.setValidationMessageSource(getValidationMessageSource());
return lvfb;
}
protected MessageSource getValidationMessageSource() {// return you validation messages ...}
}
I can provide more details and explanation, if needed.
I don't know why the approach as mentioned in the question didn't work. I didn't make it work but walking through this document, I found another approach that worked for me as per my requirements.
I set the validator inside a method which was designated by the #InitBinder annotation.
From docs
The Validator instance invoked when a #Valid method argument is
encountered may be configured in two ways. First, you may call
binder.setValidator(Validator) within a #Controller's #InitBinder
callback. This allows you to configure a Validator instance per
#Controller class:
Specifically, in my requirements, the validation should only be performed while updating or inserting data into the database i.e when an associated submit button for those operations is pressed (there is a common button for both of these tasks (insert and update) in my application whose name is btnSubmit).
The validation should be muted in any other case (for example, when the delete button is pressed). To meet this requirement, I have registered the validator as follows.
#InitBinder
protected void initBinder(WebDataBinder binder, WebRequest webRequest) {
if (webRequest.getParameter("btnSubmit") != null) {
binder.setValidator(new TempValidator());
} else {
binder.setValidator(null);
}
}
In this situation, the validator - TempValidator would only be set when the submit button whose name attribute is btnSubmit is clicked by the client.
There is no need for xml configuration anywhere as well as auto-wiring.
The exemplary controller class now looks like the following.
#Controller
public final class TempController {
#Autowired
private TempService tempService;
#InitBinder
protected void initBinder(WebDataBinder binder, WebRequest webRequest) {
if (webRequest.getParameter("btnSubmit") != null) {
binder.setValidator(new TempValidator());
} else {
binder.setValidator(null);
}
}
//Removed the #Valid annotation before TempBean, since validation is unnecessary on page load.
#RequestMapping(method = {RequestMethod.GET}, value = {"admin_side/Temp"})
public String showForm(#ModelAttribute("tempBean") TempBean tempBean, BindingResult error, Map model, HttpServletRequest request, HttpServletResponse response) {
return "admin_side/Temp";
}
#RequestMapping(method = {RequestMethod.POST}, value = {"admin_side/Temp"})
public String onSubmit(#ModelAttribute("tempBean") #Valid TempBean tempBean, BindingResult errors, Map model, HttpServletRequest request, HttpServletResponse response) {
if (CustomizeValidation.isValid(errors, tempBean, TempBean.ValidationGroup.class, Default.class) && !errors.hasErrors()) {
System.out.println("Validated");
}
return "admin_side/Temp";
}
}
The WebRequest paramenter in the initBinder() method is not meant for handling the entire Http request as obvious. It's just for using general purpose request metadata.
Javadocs about WebRequest.
Generic interface for a web request. Mainly intended for generic web
request interceptors, giving them access to general request metadata,
not for actual handling of the request.
If there is something wrong that I might be following, then kindly clarify it or add another answer.

How to write annotation processor to raise a warning message if a java source is calling an annotated method

Here is my requirement in Java 6: I am using Eclipse JUNO.
Annotate a method with a custom annotation.
During compilation, raise warning message if a method is calling the
annotated method.
I am looking for something like #Deprecated annotation.
This is what I have done:
Wrote a custom annotation.
Wrote an annotation processor to read and process the methods with
the annotation.
Created a jar and added it in annotation processor path. My sample code (see below) raises the warning message in the annotated method. But it is not my requirement.
What I couldn’t do:
I could not get the calling methods. I want to raise the warning
message in those calling methods.
My sample code:
Custom annotation:
package tool.apichecks;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.SOURCE)
#Target({ ElementType.METHOD })
public #interface HighCostMethod {
String altMethod();
}
Annotation Processor:
package tool.apichecks;
import java.util.Set;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.RoundEnvironment;
import javax.annotation.processing.SupportedAnnotationTypes;
import javax.lang.model.element.Element;
import javax.lang.model.element.TypeElement;
import javax.tools.Diagnostic.Kind;
#SupportedAnnotationTypes({ "tool.apichecks.HighCostMethod" })
public class MethodProcessor extends AbstractProcessor {
private enum MethodType {
HIGH_COST(HighCostMethod.class.getName());
private String name;
private MethodType(String name) {
this.name = name;
}
private static MethodType getMethodType(String name) {
MethodType methodType = null;
for (MethodType methodType2 : MethodType.values()) {
if (methodType2.name.equals(name)) {
methodType = methodType2;
break;
}
}
return methodType;
}
}
private ProcessingEnvironment processingEnvironment;
#Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
this.processingEnvironment = processingEnvironment;
}
#Override
public boolean process(Set<? extends TypeElement> annotations,
RoundEnvironment roundEnvironment) {
if (!roundEnvironment.processingOver()) {
for (TypeElement annotation : annotations) {
final Set<? extends Element> elements = roundEnvironment
.getElementsAnnotatedWith(annotation);
MethodType methodType = MethodType.getMethodType(annotation
.toString());
for (Element element : elements) {
switch (methodType) {
case HIGH_COST: {
processHighCostMethod(element);
break;
}
}
}
}
}
return true;
}
protected void processHighCostMethod(Element element) {
HighCostMethod highCostMethod = element
.getAnnotation(HighCostMethod.class);
/* TODO This warns the annotated method itself. I don't want this. I want to warn the methods that calls this method */
processingEnvironment
.getMessager()
.printMessage(
Kind.WARNING,
String.format(
"Do not use high cost method %s. Instead use %s method.",
element, highCostMethod.altMethod()), element);
}
}
Using an AnnotationProcessor will only work on the files containing the annotations or overriding methods, but not calling methods. Maybe there's a way around this, but then you will probably be limited by projects, because the processor only looks at one project at a time.
I guess you need to write an Eclipse plugin with a builder, that analyses code in all files and checks called methods for annotations.
That a lot more work than an annotation processor, but you also have more options. E.g. you could implement a quick fix for the error markers.

Resources