Validation Constraint not working on a List of String - java

I have created a custom validator to validate Strings. It works on a single String but not on a List of Strings. This is what I have tried so far:
#Get("/test1")
public String test1(
#QueryValue(value = "ids") List<#DurationPattern String> ids) { //NOT WORKING
return "not working";
}
#Get("/test2")
public String test2(
#QueryValue(value = "id") #DurationPattern String id){ //WORKS
//it does not get here which is what I want.
return "done";
}
My #DurationPattern code:
package my.package;
import javax.validation.Constraint;
import java.lang.annotation.Documented;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.*;
import my.package.DurationPattern.List;
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE, TYPE, LOCAL_VARIABLE, PACKAGE, TYPE_PARAMETER, MODULE })
#Repeatable(List.class)
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy = { })
public #interface DurationPattern {
String message() default "invalid duration ({validatedValue})";
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE, TYPE, LOCAL_VARIABLE, PACKAGE, TYPE_PARAMETER, MODULE})
#Retention(RUNTIME)
#Documented
public #interface List {
DurationPattern[] value();
}
}
Actual Validator:
package my.package;
import io.micronaut.context.annotation.Factory;
import io.micronaut.validation.validator.constraints.ConstraintValidator;
import javax.inject.Singleton;
#Factory
public class MyValidatorFactory {
#Singleton
ConstraintValidator<DurationPattern, CharSequence> durationPatternValidator() {
return (value, annotationMetadata, context) -> {
System.out.println("Please Print!!! It doesn't for Strings within List");
return value == null || value.toString().matches("^PT?[\\d]+[SMHD]{1}$");
};
}
}

Basically, create an implementation of io.micronaut.validation.validator.constraints.ConstraintValidator and provide them inside #Constraint(validatedBy = { })
#Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
#Repeatable(Regexp.List.class)
#Retention(RUNTIME)
#Documented
#Constraint(validatedBy = {DurationPatternValidator.class, DurationPatternValidatorList.class}) //to validate a single string or a list of strings
public #interface DurationPattern {
...
}
#Singleton
public class RegexValidatorList implements ConstraintValidator<DurationPattern, Collection<CharSequence>> {
#Override
public boolean isValid(...) {
....
}
And then in the controller's handler methods you would use
#Get("/test1")
public String test1(
#QueryValue(value = "ids") #DurationPattern List<String> ids) { //#DurationPattern would appear outside of diamond brackets for it to work.
return "working now";
}

Related

How to create custom #ConditionalOnProperty for simpler usage?

In a Spring-Boot project, I use #ConditionalOnProperty to choose whether some Beans get loaded or not. It looks like the following:
#ConditionalOnProperty(
prefix = "myservice",
name = "implversion",
havingValue = "a"
)
#Service
public class MyServiceImplA implements MyService {
// ...
}
This allows me to choose with specific profiles which Bean should be loaded, for example different implementations of an interface, depending on the value of myservice.implversion being a or b or whatever other value.
I'd like to achieve the same effect with a user-friendlier annotation like such:
#OnMyServiceVersion(value = "a")
#Service
public class MyServiceImplA implements MyService {
// ...
}
How can one do this?
I've tried annotating my custom annotation with #Conditional and implementing the Condition interface but I don't understand how to check properties that way. The Spring-Boot OnPropertyCondition extends SpringBootCondition is not public so I cannot start from there, and extending annotations isn't allowed, so I'm kind of stuck.
I've also tried the following with no success:
// INVALID CODE, DO NOT USE
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#ConditionalOnProperty(
prefix = "myservice",
name = "implversion",
havingValue = OnMyServiceVersion.value()
)
public #interface OnMyServiceVersion {
String value();
}
You can annotate your #OnMyServiceVersion annotation with #ConditionalOnProperty and alias the value of your annotation to the havingValue attribute of #ConditionalOnProperty:
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD })
#ConditionalOnProperty(prefix = "myservice", name = "implversion")
public #interface OnMyServiceVersion {
#AliasFor(annotation = ConditionalOnProperty.class, attribute = "havingValue")
String value() default "";
}
Here's a complete example that shows this in action:
package com.example.demo;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Service;
#SpringBootApplication
public class CustomPropertyConditionApplication {
public static void main(String[] args) {
SpringApplication.run(CustomPropertyConditionApplication.class, "--myservice.implversion=b");
}
#Service
#OnMyServiceVersion("a")
static class ServiceA {
ServiceA() {
System.out.println("Service A");
}
}
#Service
#OnMyServiceVersion("b")
static class ServiceB {
ServiceB() {
System.out.println("Service B");
}
}
#Documented
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE, ElementType.METHOD })
#ConditionalOnProperty(prefix = "myservice", name = "implversion")
static #interface OnMyServiceVersion {
#AliasFor(annotation = ConditionalOnProperty.class, attribute = "havingValue")
String value() default "";
}
}
This will output Service B when run. If you change the arguments in the main method to --myservice.implversion=a it will output Service A. If you remove the argument, it won't output either.
#Bean(name = "emailNotification")
#ConditionalOnProperty(prefix = "notification", name = "service")
public NotificationSender notificationSender() {
return new EmailNotification();
}
for reference
https://www.baeldung.com/spring-conditionalonproperty

Pass values using annotation

I am trying to create an annotation and pass a variable as parameter to the impl method. Below is my code to create an annotation.
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import javax.validation.Constraint;
#Documented
#Target(ElementType.PARAMETER)
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = Loggable1Impl.class)
public #interface Loggable1 {
String message() default "Invalid userId";
}
Below is my impl class
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class Loggable1Impl implements ConstraintValidator<Loggable, String> {
public void initialize(Loggable contactNumber) {
}
public boolean isValid(String generatedUserId, ConstraintValidatorContext cxt) {
System.out.println("generatedUserId in Loggable1 :: " + generatedUserId);
return true;
}
}
I am trying to use it in a spring-boot application
#CrossOrigin(origins = "*", allowedHeaders = "*")
#PostMapping("/api1/{user}")
public Topic regiterUser(#PathVariable String user ) {
String generatedUserId = user + "1";
return generatedUserId ;
}
I want to pass generatedUserId to my annotaion Loggable1
Please assist

Custom annotation validation does no validation on #pathParam in spring

I wrote a custom validation to validate an id (Which is a path Param) as UUID in my #GET method of #RestController but this validation doesn't seem to be working. Even during the debug the control doesn't go to custom validation.
#RestController
#RequestMapping("/rateplan")
#Validated
public class RatePlanServiceController {
#RequestMapping(value = "/{ratePlanId}", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
public void get(#UUID #PathVariable("ratePlanId") String ratePlanId) {
loggerFactory.warn("Get with Rate plan id {}", ratePlanId);
loggerFactory.info("Get with Rate plan id {}", ratePlanId);
loggerFactory.error("Get with Rate plan id {}", ratePlanId);
loggerFactory.debug("Get with Rate plan id {}", ratePlanId);
// return iRatePlanService.getRatePlan(ratePlanId);
}
}
I wrote the custom annotation for validation of UUID as follow.
import org.springframework.stereotype.Component;
import javax.validation.Constraint;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
import javax.validation.Payload;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.regex.Pattern;
#Target(ElementType.PARAMETER)
#Constraint(validatedBy = {UUID.IdValidator.class})
#Retention(RetentionPolicy.RUNTIME)
public #interface UUID {
String message() default "{invalid.uuid}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
#Component
public class IdValidator implements ConstraintValidator<UUID, String> {
private static final Pattern id_PATTERN =
Pattern.compile("^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$");
public boolean isValid(String value, ConstraintValidatorContext context) {
if (!(id_PATTERN.matcher(value).matches())) {
return false;
}
return true;
}
public void initialize(UUID parameters) {
}
}
}
Can anyone let me know why is it not working. Even if I provide a garbage ratePlanId like '345%#7^34' it able to go inside GET method.
Solved this by adding a bean in Application configuration file. To validate a path Param in Spring you need to add this bean in your configuration class.
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}

How to use annotation in an annotation?

Given:
public #interface MyAnnotation(){
public SomeType[] value();
}
in Java 7 is it possible to do something like:
#MyAnnotation({
value1,
#MyAnnotation({subValue1, subvalue2, ...}) value2,
...
valueN
})
public Object someProperty;
?
You can. This is an example from Jackson library (leaving out the comments):
package com.fasterxml.jackson.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target({ElementType.ANNOTATION_TYPE, ElementType.TYPE, ElementType.FIELD,
ElementType.METHOD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
#JacksonAnnotation
public #interface JsonSubTypes {
public Type[] value();
public #interface Type {
/**
* Class of the subtype
*/
public Class<?> value();
/**
* Logical type name used as the type identifier for the class
*/
public String name() default "";
}
}
And here is an example usage:
#JsonSubTypes({
#JsonSubTypes.Type(value = TestSystemEvent.class, name = "TestEvent"),
})
public interface SystemEvent {
}
How to use annotation in an annotation?
Maybe like this
public #interface MyAnnotation(){
public SomeType[] value();
public MyAnnotation[] refine();
}
#MyAnnotation(
{value1, value2},
refine={ #MyAnnotation({valueX, valueY}), #MyAnnotation(valueM) }
)
public Object someProperty;
Also, in Java 8, you can have Repeatable annotations - so you may refine or add to your 'primary' (e.g. the first) other refinements brought in by subsequent repetitions of the same annotation.

Custom BeanParam annotations in java

I try to access BeanParam, But not possible by my side because to many events access old version jar present on classpath so I can't remove jar files from classpath.
I tried to many way but finally,
I decided create own BeanParam using these links:-
javatpoint |
Annotation_Type_BeanParam_From_Oracle |
Source_code_of_the_class_BeanParam
There are code:-
My BeanParam Interface
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface myBeanParam {
}
My Model Class
import javax.ws.rs.QueryParam;
public class ControllerFilterBean {
#QueryParam("id") String row_id;
#QueryParam("ot") String objectType;
public String getRow_id() {
return row_id;
}
public void setRow_id(String row_id) {
this.row_id = row_id;
}
public String getObjectType() {
return objectType;
}
public void setObjectType(String objectType) {
this.objectType = objectType;
}
}
my resources
public class MyResources {
#GET
#Path("/do/beanTest")
#Produces("application/json")
public Response beanTest(#myBeanParam ControllerFilterBean bean/*
#QueryParam("id") String row_id,
#QueryParam("ot") String objectType*/){
System.out.println(bean.objectType);
System.out.println(bean.row_id);
/*System.out.println(row_id);
System.out.println(objectType);*/
ResponseBuilder rBuild = Response.status(Response.Status.OK);
return Build.type(MediaType.APPLICATION_JSON).entity("OK").build();
}
}
then finally want access my resource class then its throw
WARNING: No message body reader has been found for request
I want help in my code where it's getting mistake.

Categories

Resources