I am using javax.validation.constraints.Pattern.
The pojo i'm adding the pattern also contains a List object.
How can I add the #Pattern annotation so it would check the elements?
#NotNull
private List<String> myListOfStrings;
Thanks
See Container element constraints. With Bean Validation 2.0 you should be able to add your constraints to type arguments. In your case, you'll have:
#NotNull
private List<#Pattern(regexp="pattern-goes-here") String> myListOfStrings;
If instead of String you had some custom object, annotating the List with #Valid and expressing the rules in the custom object would do the trick.
For this case (you cannot express validations in the String class) I believe the best chance is a custom validator to apply a pattern on a list of strings:
#NotNull
#ListPattern("regexp")
private List<String> myListOfStrings;
The annotation would roughly look like:
#Constraint(validatedBy=ListPatternValidator.class)
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
public #interface ListPattern {
... // standard stuff
}
And the validator:
public class ListPatternValidator
implements ConstraintValidator<ListPattern, List<?>> {
public void initialize(ListPattern constraintAnnotation) {
// see Pattern implementation
}
public boolean isValid(List<?> value, ConstraintValidatorContext context) {
for( Object o : value ) {
if( does not match ) return false;
}
return true;
}
}
I am using kotlin, and mark_o answer doesn't work in my case. Following Nikos answer my whole custom implementation is:
#Target(AnnotationTarget.FIELD, AnnotationTarget.VALUE_PARAMETER)
#Retention
#Constraint(validatedBy = [ListPatternValidator::class])
annotation class ListPattern(val message: String = "Invalid input",
val regexp: String,
val groups: Array<KClass<*>> = [],
val payload: Array<KClass<out Payload>> = [])
class ListPatternValidator : ConstraintValidator<ListPattern, List<String>> {
var pattern: String? = null
override fun initialize(constraintAnnotation: ListPattern) {
pattern = constraintAnnotation.regexp
}
override fun isValid(values: List<String>, context: ConstraintValidatorContext): Boolean {
val regex = pattern?.toRegex() ?: return false
return values.all { regex.matches(it) }
}
}
Related
Is it possible, when using custom oval annotation and custom class for check, to access the annotation and retrieve the used annotation attributes ?
Reference for oval: https://sebthom.github.io/oval/USERGUIDE.html#custom-constraint-annotations
Minimal example
Lets assume we have class Foo.
It has two annotated fields.
Each time, the annotation has a different myValue – a and b.
class Foo {
#CustomAnnotation(myValue = "a")
public String first;
#CustomAnnotation(myValue = "b")
public String second;
}
This is the annotation.
It is noted that a check should be performed using MyCheck.class, also setting some default value for myValue.
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
#Constraint(checkWith = MyCheck.class)
public #interface CustomAnnotation {
String myValue() default "";
}
Now we want to use oval to validate this field.
Most importantly, we want to extract the value a or b from the annotation's myValue and use it inside our validation logic.
public class MyCheck extends AbstractAnnotationCheck<CustomAnnotation> {
#Override
public boolean isSatisfied(Object validatedObject, Object valueToValidate, OValContext context,
Validator validator) throws OValException {
// how to get the value of `myValue`, which is `a` or `b` or empty string as default
}
}
What I have tried and failed:
validatedObject is Foo.class. You can easily get its fields and annotations. However, there is no way to differentiate between the two annotations.
valueToValidate is in this case String value – what first or second holds.
context not useful, you can get compile time type from it, which is String
validator not useful ?
After some digging in the superclass I have found that you can override method
configure
This method gets as the only parameter the annotation that is currently being checked at the field.
You can then read the myValue.
public class MyCheck extends AbstractAnnotationCheck<CustomAnnotation> {
private String myValue;
#Override
public void configure(CustomAnnotation customAnnotation) {
super.configure(customAnnotation);
this.myValue = customAnnotation.myValue();
}
#Override
public boolean isSatisfied(Object validatedObject, Object valueToValidate, OValContext context,
Validator validator) throws OValException {
if (myValue.equals("a")) {}
else if (myValue.equals("b")){}
else {}
}
Consider a UrlValidator method annotation that tests if a given url is valid before calling a method.
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface UrlValdator{
String value();
}
This is working fine when routes are static and known ahead of time. For example:
#UrlValidator("http://some.known.url")
public void doSomething();
But this is not very flexible. For example, what if the route was implicit in the doSomething() method signature? Could I somehow access it form the Spring Expression Language, or some other means? For example, this doesn't work but is what I'm shooting for
#UrlValidator("#p1")
public void doSomething(String url)
or
#UrlValidator("#p1.url")
public void doSomething(Request request)
Is it possible to make annotations dynamic this way?
Related
This is the closest I've found, but the thread is old and the accepted answer is quire cumbersome/hard to follow. Is there a minimal working example/updated way to do this?
I'm not entirely sure if that's what you had in mind, but i can suggest using Spring AOP as it can give you a lot of flexibility.
Since you've mentioned in one of the comments that you're already using Spring AOP, I'm going to assume that you've added spring-boot-starter-aop as a dependency and that you've enabled support for handling components marked with #Aspect by annotating one of your config classes with #EnableAspectJAutoProxy
For example, having defined annotations as such:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface EnsureUrlValid {
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface UrlToVerify {
}
I can use them in a sample spring component as follows:
#Component
public class SampleComponent {
private static final Logger logger = LogManager.getLogger(SampleComponent.class);
#EnsureUrlValid
public void fetchData(String url) {
logger.info("Fetching data from " + url);
}
#EnsureUrlValid
public long fetchData(Long id, #UrlToVerify String url) {
logger.info("Fetching data for user#" + id + " from " + url);
// just to show that a method annotated like this can return values too
return 10L;
}
#EnsureUrlValid
public void fetchDataFailedAttempt() {
logger.info("This should not be logged");
}
}
And here's a sample "processor" of the EnsureUrlValid annotation. It looks for the annotated methods, tries to extract the passed-in url and depending on whether the url is valid or not, it proceeds with invoking the method or throws an exception. It's simple but it shows that you have complete control over the methods that you've annotated.
#Aspect
#Component
public class UrlValidator {
#Around(value = "#annotation(EnsureUrlValid)")
public Object checkUrl(ProceedingJoinPoint joinPoint) throws Throwable {
final Optional<String> urlOpt = extractUrl(joinPoint);
if (urlOpt.isPresent()) {
final String url = urlOpt.get();
if (isUrlValid(url)) {
return joinPoint.proceed();
}
}
throw new RuntimeException("The passed-in url either could not be resolved or is not valid");
}
private Optional<String> extractUrl(JoinPoint joinPoint) {
Object[] methodArgs = joinPoint.getArgs();
Object rawUrl = null;
if (methodArgs.length == 1) {
rawUrl = methodArgs[0];
}
else if (methodArgs.length > 1) {
// check which parameter has been marked for validation
Method method = ((MethodSignature) joinPoint.getSignature()).getMethod();
Parameter[] parameters = method.getParameters();
boolean foundMarked = false;
int i = 0;
while (i < parameters.length && !foundMarked) {
final Parameter param = parameters[i];
if (param.getAnnotation(UrlToVerify.class) != null) {
rawUrl = methodArgs[i];
foundMarked = true;
}
i++;
}
}
if (rawUrl instanceof String) { // if rawUrl is null, instanceof returns false
return Optional.of((String) rawUrl);
}
// there could be some kind of logic for handling other types
return Optional.empty();
}
private boolean isUrlValid(String url) {
// the actual validation logic
return true;
}
}
I hope it's somewhat helpful.
Short answer: Yes.
Long answer:
ElementType specifies the target of the annotation, which can be the following: ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, MODULE, PARAMETER, TYPE, and TYPE_PARAMETER. Were are interested in PARAMETER here. Since we want from the compiler the run our code, RetentionPolicy.RUNTIME is fine for the retention type.
Next we have to add #Constraint annotation, which according to the documentation:
Marks an annotation as being a Bean Validation constraint.
This means, Spring will pick up your parameter and validate it in runtime. The last thing we have to do is to implement the validation itself which implies creating a class which implements ConstraintValidator interface.
Putting it all together:
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = UrlValidatorImplementation.class)
public #interface UrlValidator{
String message() default "Invalid url";
}
Implementation of the UrlValidatorImplementation class:
public class UrlValidatorImplementation implements ConstraintValidator<UrlValidator, String> {
#Override
public void initialize(UrlValidator annotation) {
// initialization, probably not needed
}
#Override
public boolean isValid(String url, ConstraintValidatorContext context) {
// implementation of the url validation
}
}
Usage of the annotation:
public void doSomething(#UrlValidator url) { ... }
my custom annotation is:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface CacheClear {
long versionId() default 0;
}
I want to achieve something like this, in which I can pass the method param "versionTo" to my custom annotation.
#CacheClear(versionId = {versionTo})
public int importByVersionId(Long versionTo){
......
}
What should I do?
That's not possible.
Annotations require constant values and a method parameter is dynamic.
You cannot pass the value, but you can pass the path of that variable in Spring Expression and use AOP's JoinPoint and Reflection to get and use it. Refer below:
Your Annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface CacheClear {
String pathToVersionId() default 0;
}
Annotation Usage:
#CacheClear(pathToVersionId = "[0]")
public int importByVersionId(Long versionTo){
......
}
Aspect Class:
#Component
#Aspect
public class YourAspect {
#Before ("#annotation(cacheClear)")
public void preAuthorize(JoinPoint joinPoint, CacheClear cacheClear) {
Object[] args = joinPoint.getArgs();
ExpressionParser elParser = new SpelExpressionParser();
Expression expression = elParser.parseExpression(cacheClear.pathToVersionId());
Long versionId = (Long) expression.getValue(args);
// Do whatever you want to do with versionId
}
}
Hope this helps someone who wants to do something similar.
I'm using Spring Boot 1.4.3 #AutoConfiguration where I create beans automatically based on properties user specifies. User can specify an array of services, where name and version are required fields:
service[0].name=myServiceA
service[0].version=1.0
service[1].name=myServiceB
service[1].version=1.2
...
If the user forgets to specify a required field on even just one service, I want to back-off and not create any beans. Can I accomplish this with #ConditionalOnProperty? I want something like:
#Configuration
#ConditionalOnProperty({"service[i].name", "service[i].version"})
class AutoConfigureServices {
....
}
This is the custom Condition I created. It needs some polishing to be more generic (ie not hardcoding strings), but worked great for me.
To use, I annotated my Configuration class with #Conditional(RequiredRepeatablePropertiesCondition.class)
public class RequiredRepeatablePropertiesCondition extends SpringBootCondition {
private static final Logger LOGGER = LoggerFactory.getLogger(RequiredRepeatablePropertiesCondition.class.getName());
public static final String[] REQUIRED_KEYS = {
"my.services[i].version",
"my.services[i].name"
};
#Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
List<String> missingProperties = new ArrayList<>();
RelaxedPropertyResolver resolver = new RelaxedPropertyResolver(context.getEnvironment());
Map<String, Object> services = resolver.getSubProperties("my.services");
if (services.size() == 0) {
missingProperties.addAll(Arrays.asList(REQUIRED_KEYS));
return getConditionOutcome(missingProperties);
}
//gather indexes to check: [0], [1], [3], etc
Pattern p = Pattern.compile("\\[(\\d+)\\]");
Set<String> uniqueIndexes = new HashSet<String>();
for (String key : services.keySet()) {
Matcher m = p.matcher(key);
if (m.find()) {
uniqueIndexes.add(m.group(1));
}
}
//loop each index and check required props
uniqueIndexes.forEach(index -> {
for (String genericKey : REQUIRED_KEYS) {
String multiServiceKey = genericKey.replace("[i]", "[" + index + "]");
if (!resolver.containsProperty(multiServiceKey)) {
missingProperties.add(multiServiceKey);
}
}
});
return getConditionOutcome(missingProperties);
}
private ConditionOutcome getConditionOutcome(List<String> missingProperties) {
if (missingProperties.isEmpty()) {
return ConditionOutcome.match(ConditionMessage.forCondition(RequiredRepeatablePropertiesCondition.class.getCanonicalName())
.found("property", "properties")
.items(Arrays.asList(REQUIRED_KEYS)));
}
return ConditionOutcome.noMatch(
ConditionMessage.forCondition(RequiredRepeatablePropertiesCondition.class.getCanonicalName())
.didNotFind("property", "properties")
.items(missingProperties)
);
}
}
Old question, but I hope my answer will help for Spring2.x:
Thanks to #Brian, I checked migration guide, where I was inspired by example code. This code works for me:
final List<String> services = Binder.get(context.getEnvironment()).bind("my.services", List.class).orElse(null);
I did try to get List of POJO (as AutoConfigureService) but my class differs from AutoConfigureServices. For that purpose, I used:
final Services services = Binder.get(context.getEnvironment()).bind("my.services", Services.class).orElse(null);
Well, keep playing :-D
Here's my take on this issue with the use of custom conditions in Spring autoconfiguration. Somewhat similar to what #Strumbels proposed but more reusable.
#Conditional annotations are executed very early in during the application startup. Properties sources are already loaded but ConfgurationProperties beans are not yet created. However we can work around that issue by binding properties to Java POJO ourselves.
First I introduce a functional interface which will enable us to define any custom logic checking if properties are in fact present or not. In your case this method will take care of checking if the property List is empty/null and if all items within are valid.
public interface OptionalProperties {
boolean isPresent();
}
Now let's create an annotation which will be metannotated with Spring #Conditional and allow us to define custom parameters. prefix represents the property namespace and targetClass represents the configuration properties model class to which properties should be mapped.
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Conditional(OnConfigurationPropertiesCondition.class)
public #interface ConditionalOnConfigurationProperties {
String prefix();
Class<? extends OptionalProperties> targetClass();
}
And now the main part. The custom condition implementation.
public class OnConfigurationPropertiesCondition extends SpringBootCondition {
#Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) {
MergedAnnotation<ConditionalOnConfigurationProperties> mergedAnnotation = metadata.getAnnotations().get(ConditionalOnConfigurationProperties.class);
String prefix = mergedAnnotation.getString("prefix");
Class<?> targetClass = mergedAnnotation.getClass("targetClass");
// type precondition
if (!OptionalProperties.class.isAssignableFrom(targetClass)) {
return ConditionOutcome.noMatch("Target type does not implement the OptionalProperties interface.");
}
// the crux of this solution, binding properties to Java POJO
Object bean = Binder.get(context.getEnvironment()).bind(prefix, targetClass).orElse(null);
// if properties are not present at all return no match
if (bean == null) {
return ConditionOutcome.noMatch("Binding properties to target type resulted in null value.");
}
OptionalProperties props = (OptionalProperties) bean;
// execute method from OptionalProperties interface
// to check if condition should be matched or not
// can include any custom logic using property values in a type safe manner
if (props.isPresent()) {
return ConditionOutcome.match();
} else {
return ConditionOutcome.noMatch("Properties are not present.");
}
}
}
Now you should create your own configuration properties class implementing OptionalProperties interface.
#ConfigurationProperties("your.property.prefix")
#ConstructorBinding
public class YourConfigurationProperties implements OptionalProperties {
// Service is your POJO representing the name and version subproperties
private final List<Service> services;
#Override
public boolean isPresent() {
return services != null && services.stream().all(Service::isValid);
}
}
And then in Spring #Configuration class.
#Configuration
#ConditionalOnConfigurationProperties(prefix = "", targetClass = YourConfigurationProperties.class)
class AutoConfigureServices {
....
}
There are two downsides to this solution:
Property prefix must be specified in two locations: on #ConfigurationProperties annotation and on #ConditionalOnConfigurationProperties annotation. This can partially be alleviated by defining a public static final String PREFIX = "namespace" in your configuration properties POJO.
Property binding process is executed separately for each use of our custom conditional annotation and then once again to create the configuration properties bean itself. It happens only during app startup so it shouldn't be an issue but it still is an inefficiency.
You can leverage the org.springframework.boot.autoconfigure.condition.OnPropertyListCondition class. For example, given you want to check for the service property having at least one value:
class MyListCondition extends OnPropertyListCondition {
MyListCondition() {
super("service", () -> ConditionMessage.forCondition("service"));
}
}
#Configuration
#Condition(MyListCondition.class)
class AutoConfigureServices {
}
See the org.springframework.boot.autoconfigure.webservices.OnWsdlLocationsCondition used on org.springframework.boot.autoconfigure.webservices.WebServicesAutoConfiguration#wsdlDefinitionBeanFactoryPostProcessor for an example within Spring itself.
I just started using the #NotNull annotation with Java 8 and getting some unexpected results.
I have a method like this:
public List<Found> findStuff(#NotNull List<Searching> searchingList) {
... code here ...
}
I wrote a JUnit test passing in the null value for the argument searchingList. I was expecting some type of error to happen but it went through as though the annotation was not there. Is this expected behavior? From what I understood, this was to allow you to skip writing the boilerplate null check code.
An explanation of what exactly #NotNull is supposed to do would be greatly appreciated.
#Nullable and #NotNull do nothing on their own. They are supposed to act as Documentation tools.
The #Nullable Annotation reminds you about the necessity to introduce an NPE check when:
Calling methods that can return null.
Dereferencing variables (fields, local variables, parameters) that can be null.
The #NotNull Annotation is, actually, an explicit contract declaring the following:
A method should not return null.
A variable (like fields, local variables, and parameters) cannot should not hold null value.
For example, instead of writing:
/**
* #param aX should not be null
*/
public void setX(final Object aX ) {
// some code
}
You can use:
public void setX(#NotNull final Object aX ) {
// some code
}
Additionally, #NotNull is often checked by ConstraintValidators (eg. in spring and hibernate).
The #NotNull annotation doesn't do any validation on its own because the annotation definition does not provide any ConstraintValidator type reference.
For more info see:
Bean validation
NotNull.java
Constraint.java
ConstraintValidator.java
As mentioned above #NotNull does nothing on its own. A good way of using #NotNull would be using it with Objects.requireNonNull
public class Foo {
private final Bar bar;
public Foo(#NotNull Bar bar) {
this.bar = Objects.requireNonNull(bar, "bar must not be null");
}
}
To make #NonNull active you need Lombok:
https://projectlombok.org/features/NonNull
import lombok.NonNull;
Follow: Which #NotNull Java annotation should I use?
If you are using Spring, you can force validation by annotating the class with #Validated:
import org.springframework.validation.annotation.Validated;
More info available here:
Javax #NotNull annotation usage
You could also use #NonNull from projectlombok (lombok.NonNull)
SO #NotNull just is a tag...If you want to validate it, then you must use something like hibernate validator jsr 303
ValidatorFactory validatorFactory = Validation.buildDefaultValidatorFactory();
Validator validator = validatorFactory.getValidator();
Set<ConstraintViolation<List<Searching>> violations = validator.validate(searchingList);
I do this to create my own validation annotation and validator:
ValidCardType.java(annotation to put on methods/fields)
#Constraint(validatedBy = {CardTypeValidator.class})
#Documented
#Target( { ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface ValidCardType {
String message() default "Incorrect card type, should be among: \"MasterCard\" | \"Visa\"";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
And, the validator to trigger the check:
CardTypeValidator.java:
public class CardTypeValidator implements ConstraintValidator<ValidCardType, String> {
private static final String[] ALL_CARD_TYPES = {"MasterCard", "Visa"};
#Override
public void initialize(ValidCardType status) {
}
public boolean isValid(String value, ConstraintValidatorContext context) {
return (Arrays.asList(ALL_CARD_TYPES).contains(value));
}
}
You can do something very similar to check #NotNull.
To test your method validation in a test, you have to wrap it a proxy in the #Before method.
#Before
public void setUp() {
this.classAutowiredWithFindStuffMethod = MethodValidationProxyFactory.createProxy(this.classAutowiredWithFindStuffMethod);
}
With MethodValidationProxyFactory as :
import org.springframework.context.support.StaticApplicationContext;
import org.springframework.validation.beanvalidation.MethodValidationPostProcessor;
public class MethodValidationProxyFactory {
private static final StaticApplicationContext ctx = new StaticApplicationContext();
static {
MethodValidationPostProcessor processor = new MethodValidationPostProcessor();
processor.afterPropertiesSet(); // init advisor
ctx.getBeanFactory()
.addBeanPostProcessor(processor);
}
#SuppressWarnings("unchecked")
public static <T> T createProxy(T instance) {
return (T) ctx.getAutowireCapableBeanFactory()
.applyBeanPostProcessorsAfterInitialization(instance, instance.getClass()
.getName());
}
}
And then, add your test :
#Test
public void findingNullStuff() {
assertThatExceptionOfType(ConstraintViolationException.class).isThrownBy(() -> this.classAutowiredWithFindStuffMethod.findStuff(null));
}
I resolved it with
#JsonSetter(nulls = Nulls.AS_EMPTY)
#NotBlank
public String myString;
Request Json:
{
myString=null
}
Response:
error must not be blank