hi I have a problem while Implementing custom validation using ConstraintValidator.
I am implementing some bean validations using JSR303 and constraint validator and upto now I have following implementation as
#ProductId// this is the problem
#NotEmpty(message = "{Domain.NotEmpty.productid}")
protected String productId;
#Pattern(regexp = "[A-Za-z]+", message = "{Domain.Pattern.firstName}")
....
#ProductId
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE })
#Documented
#Constraint(validatedBy = ProductIdValidator.class)
public #interface ProductId {
String message() default "{ProductId.validaton.Error}";
Class<?>[] groups() default {};
public abstract Class<? extends Payload>[] payload() default {};
}
ProductIdValidator
public class ProductIdValidator implements ConstraintValidator<ProductId, String> {
#Autowired
private RepositiryObject repo;
#Override
public void initialize(ProductId productId) {
}
#Override
public boolean isValid(String string, ConstraintValidatorContext constraintValidatorContext) {
Domain domain;
try {
domain = repo.getProductById(string);
} catch (ProdctNotFoundException ex) {
return true;
}
if (domain != null) {
return false;
}
return true;
}
}
StackTrace
Request processing failed; nested exception is javax.validation.ValidationException: HV000028: Unexpected exception during isValid call.] with root cause
java.util.NoSuchElementException: No value present
at java.util.Optional.get(Optional.java:135)
at com.model.domain.controller.repo.RepositiryObject.getProductById(RepositiryObject.java:45)
at com.java.spring.custom.validation.annotations.constraints.ProductIdValidator.isValid(ProductIdValidator.java:28)
at com.java.spring.custom.validation.annotations.constraints.ProductIdValidator.isValid(ProductIdValidator.java:1)
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateSingleConstraint(ConstraintTree.java:448)
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:127)
at org.hibernate.validator.internal.engine.constraintvalidation.ConstraintTree.validateConstraints(ConstraintTree.java:87)
at org.hibernate.validator.internal.metadata.core.MetaConstraint.validateConstraint(MetaConstraint.java:73)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateMetaConstraint(ValidatorImpl.java:617)
at org.hibernate.validator.internal.engine.ValidatorImpl.validateConstraint(ValidatorImpl.java:580)
dispatcherservlet
<mvc:annotation-driven validator="validator" />
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource" ref="messageSource" />
</bean>
......
EditSince exception says that no such element is present but when I ran test its working fine as
public RepositiryObject() {
domainList = new ArrayList<>();
Domain domain = new Domain();
domain.setProductId("P1234");
domainList.add(domain);
}
#Test
public void checkIfProductExists() {
Assert.assertEquals("P1234", domainList.stream().filter(product -> product.getProductId().equals("P1234"))
.map(Domain::getProductId).findAny().get());
}
Now the problem is when I remove that #ProductId it works fine not sure what I am missing here
Related
The following code yields null on the teamResource auto-wired object inside isValid() function:
#Component //I am not sure it is required he
public class TeamIdValidator implements ConstraintValidator<TeamIdConstraint, Integer> {
#Autowired private TeamResource teamResource;
public TeamIdValidator() {
}
#Override
public void initialize(TeamIdConstraint teamIdConstraint) {
// this.teamIdConstraint = teamIdConstraint;
}
#Override
public boolean isValid(Integer teamId, ConstraintValidatorContext cxt) {
int numOfAvailableTeams = teamResource.retrieveAllTeams().size(); //teamResource is null :(
return teamId < 0 || teamId >= numOfAvailableTeams;
}
}
TeamResource class:
#RestController
#CrossOrigin
public class TeamResource {
#Autowired private TeamRepository teamRepository;
public TeamResource() {
}
...
//mapping methods
}
And if it relevant...
#Documented
#Constraint(validatedBy = TeamIdValidator.class)
#Target({ ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER })
#Retention(RetentionPolicy.RUNTIME)
public #interface TeamIdConstraint {
String message() default "Invalid team id!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Why on earth teamResource is null?
It is not being explicitly initialized with new anywhere else.
Already mentioned in the comments but declare a a bean like this
#Configuration
public class ValidationConfig{
#Bean
public LocalValidatorFactoryBean validator() {
return new LocalValidatorFactoryBean();
}
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor(Validator validator) {
MethodValidationPostProcessor methodValidationPostProcessor = new MethodValidationPostProcessor();
methodValidationPostProcessor.setValidator(validator);
return methodValidationPostProcessor;
}
}
Remove all the #autowired, prefer constructor injection. (check the internet why).
And then, it makes no sense to inject a RestContoller to validator.
I have controller with method:
void save(#Validated(BookingForm.ValidationSave.class) #RequestBody BookingForm form, Errors result) {...}
and BookingForm with a field and my custom validator:
public class BookingForm {
public interface ValidationSave {}
public interface ValidationEstimate {}
....
#Future
private LocalDateTime when;
....
}
Future.java
#Target({ ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = FutureValidator.class)
#Documented
public #interface Future {
String message() default "{javax.validation.constraints.Future.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
I've expected that validator will valid my 'when' field but it does not.
When I removed the group from #Validated it works. Any ideas?
Works fine with #NotNull and others javax validators.
Updated:
FutureValidator.java
public class FutureValidator implements ConstraintValidator<Future, Temporal> {
#Override
public void initialize(Future constraintAnnotation) {
}
#Override
public boolean isValid(Temporal value, ConstraintValidatorContext context) {
if (value == null) {
return true;
}
LocalDate ld = LocalDate.from(value);
return ld.isAfter(LocalDate.now());
}
}
I'using Spring 4.1.1 with Hibernate Validator 5.1.3. I'm trying to validate method parameter with the JSR303 validation, and It can't retrieve the parameter's name of the validated method because spring apply validation on the interface, but the parameter's names are in the compiled implementation class.
Interface :
Code:
#Validated
public interface NomenclatureVenteRest {
public Response recupererArborescenceNomenclatureVente(
#FieldValidation(required="dateAppli") StimeDate dateAppli,
#FieldValidation(check = GENE_M010_PAYS) String codePays,
#FieldValidation(check = GENE_M010_ENSEIGNE) List<String> listeCodeEns) throws StimeFunctionalException;
}
Implementation :
Code:
#Path("/")
#Service(value = "nomenclatureVenteRestImpl")
public class NomenclatureVenteRestImpl implements NomenclatureVenteRest {
#GET
#Path("/")
#Produces(RestUtils.UTF8_ENCODED_JSON)
#Override
public Response recupererArborescenceNomenclatureVente(
#QueryParam("dateAppli") StimeDate dateAppli,
#QueryParam("codePays")String codePays,
#QueryParam("listeCodeEns")List<String> listeCodeEns) throws StimeFunctionalException{
...Business logic...
}
}
#FieldValidation is my custom constraint validation
#Target({ PARAMETER, ANNOTATION_TYPE ,FIELD})
#Retention(RUNTIME)
#Constraint(validatedBy = FieldValidator.class)
#Documented
public #interface FieldValidation {
String message() default "";
Class<?>[] groups() default {};
boolean required() default false;
FieldTypeValidator[] check() default {};
Class<? extends Payload>[] payload() default {};
}
I want tu use the parameter name in my validator :
public class FieldValidator implements ConstraintValidator<FieldValidation, Object>, ApplicationContextAware {
ApplicationContext context;
private boolean fieldRequired;
private FieldTypeValidator[] fieldCheck;
#Override
public final void initialize(final FieldValidation annotation) {
fieldCheck = annotation.check();
fieldRequired = annotation.required();
}
#SuppressWarnings("unchecked")
#Override
public boolean isValid(final Object value, ConstraintValidatorContext constraintValidatorContext) {
//If I take look a the constraintValidatorContext.methodParameterNames contains ["arg0", "arg1", "arg2"] , but I need the real parameter names ["dateAppli","codePays","listeCodeEns"]
return true;
}
Jsr303 configuration :
<!-- JSR 303 validation -->
<bean id="validator"
class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
<bean
class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor">
<property name="validator" ref="validator" />
</bean>
<bean
class="org.springframework.validation.beanvalidation.BeanValidationPostProcessor">
<property name="validator" ref="validator" />
</bean>
Regards ,
Régis LIMARE
I am trying to implement custom annotation in spring as follows:-
Test annotation is as follows
#Documented
#Target( { ElementType.METHOD, ElementType.FIELD })
#Constraint(validatedBy=CheckUser.class)
#Retention(RetentionPolicy.RUNTIME)
public #interface Test {
String user();
}
And for validation I have written CheckUser class as follows::-
private String xyz;
#Override
public void initialize(Test user) {
xyz=user.toString();
}
#Override
public boolean isValid(Principal principal, ConstraintValidatorContext context) {
if(principal.getName().equalsIgnoreCase(xyz))
return true;
else
return false;
}
But its not working. Can some one tell me what is wrong here??
In security as well as application context I have written
<Secured:global-method-security secured-annotations="enabled" pre-post-annotations="enabled" jsr250-annotations="enabled"/>
You need to include message, groups, and payload so it plays nicely with regards to the JSR-303 spec: http://beanvalidation.org/1.0/spec/#constraintsdefinitionimplementation-constraintdefinition-properties
#Documented
#Target( { ElementType.METHOD, ElementType.FIELD })
#Constraint(validatedBy=CheckUser.class)
#Retention(RetentionPolicy.RUNTIME)
public #interface Test {
String user();
String message() default "test failed"
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
I having trouble in showing Invalid value in the error message
I have messages.properties file as follows
ProductId.exists.custom.validator.validation = A product already exists with product id ${validatedValue}.
and following is my custom validtor interface
#Target( { METHOD, FIELD, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = ProductIdValidator.class)
#Documented
public #interface ProductId {
String message() default "{ProductId.exists.custom.validator.validation}";
Class<?>[] groups() default {};
public abstract Class<? extends Payload>[] payload() default {};
}
Here is the implementation
#Component
public class ProductIdValidator implements ConstraintValidator<ProductId, String>{
#Autowired
private ProductService productService;
#Override
public void initialize(ProductId constraintAnnotation) {
// TODO Auto-generated method stub
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
Product product = productService.getProductById(value);
if(product!= null) {
return false;
}
return true;
}
}
When I am running my application I am getting error message like
A product already exists with product id ${validatedValue}. but I am expecting A product already exists with product id P1234. as error message
How to get the validatedValue in error message?
Additional details
I have used hibernate-validator:4.3.1.Final version and spring-webmvc:3.2.0.RELEASE
And I am triggering the validation in the context as follows
<mvc:annotation-driven validator="validator"/>
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
<property name="validationMessageSource" ref="messageSource" />
</bean>
I’m not sure what was the main reason for this solution I’ve used in my project – if to make interpolation work, or just use ReloadableResourceBundleMessageSource (that supports properties in UTF-8 and runtime reloading!) instead of the default one. However, this should work for you.
<mvc:annotation-driven validator="validator" />
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" primary="true"
p:messageInterpolator-ref="messageInterpolator" />
<!-- Hibernate Validator which can interpolate the value being validated in the constraint message -->
<bean id="messageInterpolator" class="ValueFormatterMessageInterpolatorFactoryBean"
p:messageSource-ref="validatorMessageSource" />
<bean id="validatorMessageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource"
p:basename="classpath:/config/i18n/validator-messages"
p:defaultEncoding="utf-8"
p:cacheSeconds="0" />
And the custom ValueFormatterMessageInterpolatorFactoryBean class:
/**
* {#linkplain FactoryBean} that creates {#link ValueFormatterMessageInterpolator}
* with underlying {#link ResourceBundleMessageInterpolator} that uses the given
* {#link MessageSource}.
*/
public class ValueFormatterMessageInterpolatorFactoryBean implements FactoryBean<MessageInterpolator> {
private MessageSource messageSource;
public MessageInterpolator getObject() throws Exception {
ResourceBundleLocator resourceBundleLocator = new MessageSourceResourceBundleLocator(messageSource);
return new ValueFormatterMessageInterpolator(
new ResourceBundleMessageInterpolator(resourceBundleLocator));
}
public Class<?> getObjectType() {
return ValueFormatterMessageInterpolator.class;
}
public boolean isSingleton() {
return true;
}
#Required
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
}
Note: I’m using Hibernate Validator 4.3.0.Final.