I know it's a possible duplicate and I found several threads like How can I find all beans with the custom annotation #Foo? or Custom annotation is not working on spring Beans but that's not really what I do or want to do.
I want an easy validator for the length of attributes of a class. Dont tell me about #Length or #Size. They're not helpful here. I tried several solutions and none of them did work.
CustomAnnotation:
#Target({ ElementType.METHOD, ElementType.FIELD, ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = CheckLengthValidator.class)
#Qualifier // got this one from a solution
public #interface CheckLength {
Class<?> className();
Class<?>[] groups() default {};
String message() default "List is not valid";
Class<? extends Payload>[] payload() default {};
}
CustomAnnotationValidator (methods not implemented yet):
public class CheckLengthValidator implements ConstraintValidator<CheckLength, List<Transaction>> {
#Override
public void initialize(CheckLength a) {
//get values which are defined in the annotation
System.out.println("init");
test = a.message();
}
#Override
public boolean isValid(List<Transaction> value, ConstraintValidatorContext context) {
for (Transaction x : value) {
if (x.getTimestamp().length() > 30) {
System.out.println("not valid");
return false;
}
}
return true;
}
}
So where do I use it? At my API where all autowired repos are.
#CrossOrigin(origins = "http://localhost:4200")
#RestController
public class FileManagementRestAPI {
#Autowired
#CheckLength(className = Transaction.class)
List<Transaction> transaction = new ArrayList<>();
...
}
Later this will be called to fill the list.
transaction.addAll((Collection<? extends Transaction>) csvToBean.parse());
What I tried:
I read about a solution which I later found out it is deprecated or not working anymore with CommandLineRunner and AnnotationConfigApplicationContext.
Then I've read that I have to declare it as Bean but a List here isn't a Bean or do I still need to do something with Beans? Saw something like this but didn't know what to do with it then:
public class InitBeans implements BeanPostProcessor { ... }
The error I get now:
Field transaction in com.stuff.project.apis.FileManagementRestAPI required a bean of type 'java.util.List' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
- #com.stuff.project.utils.CheckLength(message=List is not valid, groups=[], payload=[], className=class com.stuff.project.entity.Transaction)
Action:
Consider defining a bean of type 'java.util.List' in your configuration.
There were several other errors when I was trying to get it running.
Related
In my custom annotation, I am taking domains I am going to allow for mail
#Documented
#Constraint(validatedBy = ValidEmail.CheckIfValidMail.class)
//#Constraint(validatedBy = {})
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
//#Repeatable(List.class)
#Retention(RUNTIME)
#Component
public #interface ValidEmail {
String message() default "Invalid String !!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
#Retention(RUNTIME)
#Documented
#interface List {
ValidEmail[] value();
}
#Component
public class CheckIfValidMail implements ConstraintValidator<ValidEmail, String> {
#Autowired
UserServiceImplOld userServiceImpl;
// #Value("${app.mail.allowedDomains:gmail}")
private String[] allowedDomainsForMail;
#Value("${app.mail.allowedDomains:gmail}")
public void setAllowedDomainsForMail(String[] allowedDomainsForMail) {
this.allowedDomainsForMail = allowedDomainsForMail;
}
// private String[] allowedDomainsForMail = new String[] { "gmail", "rediffmail", "yahoo" };
protected String message;
#Override
public void initialize(ValidEmail validEmail) {
this.message = validEmail.message() + " Allowed Domain(s): " + Arrays.toString(allowedDomainsForMail);
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context) {
if (value == null)
return true;
String allowedDomainRegEx = String.join("|", allowedDomainsForMail);
String mailRegex = "^(?i)[A-Za-z0-9+_.-]+#(?:" + allowedDomainRegEx + ")\\.(?:.*)$";
context.disableDefaultConstraintViolation();
context.buildConstraintViolationWithTemplate(message).addConstraintViolation();
return value.matches(emailRegex);
}
}
}
If I am calling this from rest API, each time the value is there, But
If I am calling validation explicitly from code I can see in debug allowedMailDomains value is null
But, if I am not taking from properties file and hardcoding, it's working both the time like this
Dto object
#Data //lombok
public class UserDto{
#ValidEmail(message = "Wrong emailId format !!")
private String emailId;
#NotNull
private Boolean isLoginAllowed;
}
In implementation
UserDto addUserDto = new UserDto();
addUserDto.setEmailId("satish");
addUserDto.setisLoginAllowed(false);
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
Validator validator = factory.getValidator();
System.out.println("Validating :-\n" + validator.validate(addUserDto));
So, While validating it goes to ValidMail interface, and there allowedMailDomains value is null
Try to register LocalValidatorFactoryBean and use it instead of Validation.buildDefaultValidatorFactory(). LocalValidatorFactoryBean should allow you to get dependency injection benefits inside your validator.
UPD: https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#validation-beanvalidation-spring-constraints
By default, the LocalValidatorFactoryBean configures a SpringConstraintValidatorFactory that uses Spring to create ConstraintValidator instances. This lets your custom ConstraintValidators benefit from dependency injection like any other Spring bean.
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
#Configuration
public class ValidatorConfiguration {
#Bean
public ValidatorFactory localValidatorFactoryBean() {
return new LocalValidatorFactoryBean();
}
}
Then in your implementation class autowire this bean:
#Autowired
private ValidatorFactory validatorFactory;
And use it instead of DefaultValidatorFactory:
Validator validator = validatorFactory.getValidator();
validator.validate(addUserDto);
To inject the property allowedMailDomains, your class CheckIfValidMail must be a bean. So if you add #Component annotation (for example) to your class, the problem will be resolved.
So here is the issue:
When running the code in the REST API, you are running in a Spring container which initializes the CheckIfValidMail as a Component, which then get's all the values and everything from the Spring container that it needs.
When you intialize it programatically, it isn't initialized as a Component, and there is no Container as well to get the #Value from, I would even doubt the Properties are loaded at all in the second case (I cannot see the entire code so I don't know for sure).
Without full context here is a solution right the top of my head:
in the validator do this:
private String[] allowedMailDomains;
#Value("${poss.mail.allowedDomains:gmail}")
public void setAllowedMailDomains(String[] allowedMailDomains) {
this.allowedMailDomains = allowedMailDomains;
}
And then set that value in your programmatic validation, that should then work.
Otherwise, just initialize it in a container and that should fix it.
I'm trying to implement a custom annotation to validate my fields. The idea is that the validation fails whenever the annotated field is null. Something like this.
#RequiredProperty
public abstract Person getPerson();
Now if this returns a null Person, I'd like the validation to fail (ideally with a custom message "Person field is null").
I tried to do it like this.
#Documented
#Constraint(validatedBy = RequiredPropertyValidator.class)
#Retention(RetentionPolicy.RUNTIME)
#Target({FIELD, METHOD, PARAMETER, ANNOTATION_TYPE, TYPE_USE})
#ReportAsSingleViolation
public #interface RequiredProperty {
String message() default "{javax.validation.constraints.RequiredProperty.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And validator.
public class RequiredPropertyValidator implements ConstraintValidator<RequiredProperty, Object> {
#Override
public void initialize(RequiredProperty constraintAnnotation) {
}
#Override
public boolean isValid(Object property, ConstraintValidatorContext context) {
return property != null;
}
}
However this won't work. It doesn't validate at all. Object property is never even passed to the isValid method. Any advice on how to get it working?
UPDATE
Removing the empty initialize method got it working. However, I'm not sure how to create a custom error message that the "Person" field is null. Is that possible?
I created a custom message in .properties file, but this is just a static message, and I'd like to capture the actual field from runtime.
I'm trying to create a custom bean validation, so I write this custom constraint:
#Documented
#Constraint(validatedBy = ValidPackageSizeValidator.class)
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
public #interface ValidPackageSize {
String message() default "{br.com.barracuda.constraints.ValidPackageSize}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And a validator:
public class ValidPackageSizeValidator implements ConstraintValidator<ValidPackageSize, PackageSize> {
...
#Override
public boolean isValid(PackageSize value, ConstraintValidatorContext context) {
...validation login here..
}
}
Also, I wanted the validation to be performed on the service layer just after some decorators are called, so I created an another decorator to handle this task..
#Decorator
public abstract class ConstraintsViolationHandlerDecorator<T extends AbstractBaseEntity> implements CrudService<T> {
#Any
#Inject
#Delegate
CrudService<T> delegate;
#Inject
Validator validator;
#Override
#Transactional
public T save(T entity) {
triggerValidations(entity);
return delegate.save(entity);
}
private void triggerValidations(T entity) {
List<String> errorMessages = validator.validate(entity).stream()
.map(ConstraintViolation::getMessage)
.collect(Collectors.toList());
if (!errorMessages.isEmpty()) {
throw new AppConstraintViolationException(errorMessages);
}
}
}
Everything works, but if validations pass, hibernate throws an error:
ERROR [default task-6] (AssertionFailure.java:50) - HHH000099: an assertion failure occurred (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session): org.hibernate.AssertionFailure: null id in br.com.barracuda.model.entities.impl.PackageSize entry (don't flush the Session after an exception occurs)
My entities use auto-generated id values.
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
protected Long id;
Using Widlfly 9 with JEE 7.
Validation was being executed twice, once in the service layer (where I wanted it to happen) and once when entity was persisted/merged (jpa was calling it). So I disabled it by adding this line to my persistence.xml:
<property name="javax.persistence.validation.mode" value="none"/>
Now everything works fine
I'm working with bean validations and I'm searching for a possibility to set a default group of my own bean validation annotation.
I have something (working) like this:
Application.class (calling validate on MyBean)
public class Application {
public static void main(String[] args) {
Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
Set<ConstraintViolation<MyBean>> violations =
validator.validate(new MyBean(), SecondStep.class);
}
}
MyBean.class (the bean itself; here is what I want to prevent)
public class MyBean {
// I don't want to write this "groups" attribute every time, because it's very clear,
// that this should only be validated for the second step, isn't it?
#RequiredBySecondStep(groups=SecondStep.class)
private Object myField;
}
RequiredBySecondStep.class (the bean validation annotation)
#Documented
#Target(FIELD)
#Retention(RUNTIME)
#Constraint(validatedBy = RequiredBySecondStepValidator.class)
public #interface RequiredBySecondStep {
String message() default "may not be null on the second step";
Class<?>[] groups() default {}; // <-- here I want to set SecondStep.class
Class<? extends Payload>[] payload() default {};
}
RequiredBySecondStepValidator.class (an implemented constraint validator)
public class RequiredBySecondStepValidator implements ConstraintValidator<RequiredBySecondStep, Object> {
public void initialize(RequiredBySecondStep constraintAnnotation) {
}
public boolean isValid(Object object, ConstraintValidatorContext constraintContext) {
return object != null;
}
}
SecondStep.class (the bean validation group)
public interface SecondStep {
}
Unfortunately, it's not possible by specification, to set the default group in the RequiredBySecondStep annotation like this:
Class<?>[] groups() default SecondStep.class;
// and using just the following in the bean:
#RequiredBySecondStep
private Object myField;
This will result in a RuntimeException:
javax.validation.ConstraintDefinitionException: Default value for
groups() must be an empty array
Furthermore, there is not only a SecondStep. There are probably 5 different groups which I want to annotate directly with a #RequiredByFirstStep or #RequiredByFifthStep.
Is there a good way to implement this?
I think you got it all a bit wrong. There is indeed to way to do what you want and that's because the aspect of constraints and their validation via a ConstraintValidator is orthogonal to groups and groups sequences. Per design a constraint (annotation and its validator) should be independent of the group getting validated. Even if you would get this to work, it would not be portable constraints. Personally, I would re-think what you want to achieve. #RequiredByFirstStep does not tell you what the requirement is. You should develop constraints which imply what they are valiating (a string length, not null, etc), when or better in which condition they are executed is a then controlled by group interfaces.
Let's consider the following example, I'm creating a custom bean validation constraint by means of a new annotation type:
#Target( { METHOD, FIELD, ANNOTATION_TYPE })
#Retention(RUNTIME)
#Constraint(validatedBy = MyAbstractOrInterfaceValidator.class)
#Documented
public #interface MyAnnotation {
String message() default "{}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
SomeClass value();
}
My question is: can MyBastractOrInterfaceValidator class be an abstract class or an interface? How can I control which implementation of that interface or abstract class is used to validate an element the annotation is placed on?
No, that's not possible. If you want to hide your validator implementations from the public constraint definitions, you have the following options:
Provide a constraint mapping XML file and bundle it with your distribution. Users of the constraint will have to add the file to their validation.xml, though.
If you are using a DI solution such as CDI or Spring, provide just a very slim validator implementation as part of your public API (possibly as an inner class of the annotation) and obtain the actual implementation via dependency injection
If you are on Hibernate Validator 5.2 (currently under development), constraint validators can be registered via a META-INF/services file, which would be exactly what you are after; You even can plug in a custom constraint definition contributor for implementing other lookup strategies
See http://docs.oracle.com/javaee/7/api/javax/validation/Constraint.html
First, note that validatedBy is a Class<? extends ConstraintValidator<?,?>>[], that's an array, not a single class.
Second, there's no reason to use an interface or abstract class, because that class needs to be instantiated.
But, if you want to change the validator's implementation in the runtime, try using this:
public class MyValidator implements ConstraintValidator<MyAnnotation, String> {
//store the Class information in a static variable
private static Class<? extends ConstraintValidator<MyAnnotation, String>> implementationClass = MyValidatorOne.class;
//and change it by an accessor
public static void setImplementationClass(Class<? extends ConstraintValidator<MyAnnotation, String>> implClass) {
this.implementationClass = implClass;
}
//this delegate will do all the job
private final ConstraintValidator<MyAnnotation, String> implementation;
public MyValidator() {
implementation = implementationClass.newInstance();
}
#Override
void initialize(MyAnnotation constraintAnnotation) {
implementation.initialize(constraintAnnotation);
}
#Override
boolean isValid(T value, ConstraintValidatorContext context) {
return implementation.isValid(value, context);
}
}
somewhere in the code:
MyValidator.setImplementationClass(MyValidatorTwo.class);
But there's one problem. Most probably an instance of each validator is created once in the runtime for a single class - on the first validation call on object of that class. Implementation change will only take effect if done before that.
Other way is to store implementationClass values in external class, like java.util.Properties or a class which picks available implementations with some priority.