Reputation: 2860
I am using Spring Boot and javax validation, particularly @Size. I am trying to grab the values for size constraints from the application.properties file:
@Size(min= @Value("${device.name.minsize}"), max=@Value("${device.name.maxsize}"))
private String name;
But I receive the following compile time error:
Error:(26, 16) java: annotation not valid for an element of type int
Trying to fix this issue I'm attempting the following:
@Size(min=Integer.parseInt( @Value("${device.name.minsize}") ), max=Integer.parseInt( @Value("${device.name.maxsize}") ) )
But this has multiple errors as well.
How can I convert the @Value annotations correctly? Am I headed down the wrong path? What I am looking for is a clean way to pull size limitations out of code and into configuration that I can access server side and in my templated angularJS/html.
Upvotes: 5
Views: 6621
Reputation: 519
The bad news: there's no way to do what you want with standard annotations from Java Validation API.
The good news: you can easily create a custom annotation that does exactly what you want.
You need to create a custom validation annotation (let's call it @ConfigurableSize
) that takes as parameters two strings, one for the name of the property holding the min size and one for the name of the property holding the max size.
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Repeatable(ConfigurableSize.List.class)
@Constraint(validatedBy = {ConfigurableSizeCharSequenceValidator.class})
public @interface ConfigurableSize {
String message() default "size is not valid";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
String minProperty() default "";
String maxProperty() default "";
@Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE })
@Retention(RUNTIME)
@Documented
@interface List {
ConfigurableSize[] value();
}
}
The validator will retrieve the property values upon initialization, then it will perform the exact same validation checks as the @Size
constraint. Even the constraint violation will have the exact same message. Please notice that if the property name is omitted the min
and max
will default respectively to 0
and Integer.MAX_VALUE
, i.e. the same defaults for @Size
.
public class ConfigurableSizeCharSequenceValidator implements ConstraintValidator<ConfigurableSize, CharSequence> {
private final PropertyResolver propertyResolver;
private int min;
private int max;
@Autowired
public ConfigurableSizeCharSequenceValidator(PropertyResolver propertyResolver) {
this.propertyResolver = propertyResolver;
}
@Override
public void initialize(ConfigurableSize configurableSize) {
String minProperty = configurableSize.minProperty();
String maxProperty = configurableSize.maxProperty();
this.min = "".equals(minProperty) ? 0 :
propertyResolver.getRequiredProperty(minProperty, Integer.class);
this.max = "".equals(maxProperty) ? Integer.MAX_VALUE :
propertyResolver.getRequiredProperty(maxProperty, Integer.class);
validateParameters();
}
private void validateParameters() {
if (this.min < 0) {
throw new IllegalArgumentException("The min parameter cannot be negative.");
} else if (this.max < 0) {
throw new IllegalArgumentException("The max parameter cannot be negative.");
} else if (this.max < this.min) {
throw new IllegalArgumentException("The length cannot be negative.");
}
}
@Override
public boolean isValid(CharSequence value, ConstraintValidatorContext context) {
if (value == null) {
return true;
} else {
int length = value.length();
boolean retVal = length >= this.min && length <= this.max;
if (!retVal) {
HibernateConstraintValidatorContext hibernateContext =
context.unwrap(HibernateConstraintValidatorContext.class);
hibernateContext.addMessageParameter("min", this.min)
.addMessageParameter("max", this.max);
hibernateContext.disableDefaultConstraintViolation();
hibernateContext
.buildConstraintViolationWithTemplate("{javax.validation.constraints.Size.message}")
.addConstraintViolation();
}
return retVal;
}
}
}
You apply the custom annotation in your bean
public class Device {
@ConfigurableSize(minProperty = "device.name.minsize", maxProperty = "device.name.maxsize")
private String name;
}
Then finally in your application.properties
you'll define the properties
device.name.minsize=4
device.name.maxsize=8
And that's it. You can find more details and a full example in this blog post: https://codemadeclear.com/index.php/2021/03/22/easily-configure-validators-via-properties-in-a-spring-boot-project/
Upvotes: 1
Reputation: 1860
For yet another approach take a look at https://github.com/jirutka/validator-spring.
It allows you to use SpSEL expressions in bean validation annotations including config properties.
You won't be able to use the standard annotations like @Size
though, you'd have to formulate the constraints as SpEL expressions.
Upvotes: 1
Reputation: 83
I don't think you'll be able to do that. Annotations require constant values as their parameters, since they need to be handled at compile time.
You could externalize the xml: http://beanvalidation.org/1.1/spec/#xml-config
Alternatively, if you just want to use JSR-303 annotation metadata in AngularJS, you might have a look at Valdr and Valdr BeanValidation: https://github.com/netceteragroup/valdr https://github.com/netceteragroup/valdr-bean-validation
Upvotes: 1