CDI validation on received parameter - java

I am trying to understand CDI, with very few results at the moment. :(
Actually I am stucked with a web service facade like this example:
#GET
#Path("/greeting/{name}")
#produces(mediatype.text_plain)
public String sayHello(#PathParam("name") String name)
{
return "Hello " + name;
}
Because using CDI annotation I'd like to make a check on server side every time a string is sent as parameter, to have comething like : (note #NameValidator annotation)
#GET
#Path("/greeting/{name}")
#Produces(mediatype.text_plain)
public String sayHello( #NameValidator #PathParam("name") String name)
{
return "Hello " + name;
}
The validaton can be anything like that, in an external class:
if (name == "Andrea") {
throw new Exception();}
How does it work? Is it possible? If not, what are alternatives?
Thank you!
Andrea

You could use CDI's interceptor annotations and do something like:
...
#Validated
public String sayHello(#Validator(MyValidator.class) String name) {
...
Where #Validated binds the method to a ValidationInterceptor class or so, where you use an #AroundInvoke method and its InvocationContext parameter to inspect the passed parameters and validate them.
One "difficulty" with this approach is that you will have to use reflection to get the #Validator annotation and specified validator class for each parameter, then create/retrieve corresponding instances before you can finally do the actual validation.
A slightly different approach would be to inject an Instance<T> of a common superclass/interface of your validators into the ValidationInterceptor and annotate the sayHello parameters with validator qualifiers:
// in ValidationInterceptor:
#Inject
private Instance<Validator> validatorInstance;
#AroundInvoke
public Object validate(InvocationContext context) {
// get Annotation instances for target method parameters
// ...
Validator validator =
validatorInstance.select(annotations).get();
// ... validator.validate(parameter); ...
}
// sayHello:
...
#Validated
public String sayHello(#NameValidator String name) {
...
To get parameter annotations from InvocationContext, do something like:
Annotation[][] annotations = context.getMethod().getParameterAnnotations();
You may also consider pre-processing these annotations in an #AfterBeanDiscovery event handler.

For that validation issue I would propose Seam Validation, which bridges CDI to Hibernate-Validator. That hooks you on the well-defined validation-API of Hibernate end enables you to write code like this:
public void registerUser(#Valid UserData data) {...};
If you can't live with the Seam 3 dependency you can easily take the relevant source code of the CDI-extension, it's just a few dozens lines of code.

Related

Spring - Pass parameters in custom annotation

I have the following:
Aspect:
#Aspect
#Component
public class AuthorizeUserAspect {
#Autowired
PermissionService permissionService;
#Around("#annotation(AuthorizeUser)")
public Object authorize(ProceedingJoinPoint joinPoint) throws Throwable {
...
}
}
Interface:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface AuthorizeUser {
}
Controller:
#GetMapping("/businesses/{businessId}/test")
public ResponseEntity<List<BusinessDto>> getBusinessListAsClient(Principal jwtAuthUser, #PathVariable String businessId) throws Exception {
...
}
I need to pass in the named parameters from the method signature, plus an additional custom permission parameter (READ / WRITE), so something like this:
#AuthorizeUser(Principal jwtAuthUser, String businessId, permission = <Enum or String>)
#GetMapping("/businesses/{businessId}/test")
Is this possible? I don't really care how it's done as long as I can get these values in my authorize function to use them.
I know I can do something like:
joinPoint.getArgs();
to get the parameters by position, but I want this to be more generic, and certain controllers can have different params as 1st and second for example, so that would not work, plus I have no idea
how to get the last value.
Note: The authorize is just an example, but I'd like to do this for other custom annotation parameters as well.
Maybe you should read the Spring AOP manual. How about this?
#Around("#annotation(authorizeUser)")
public Object authorize(ProceedingJoinPoint joinPoint, AuthorizeUser authorizeUser)

Can I explicitly call custom validator from service in Spring Boot?

I have a custom validator class that implements Validator, like this:
public class MyCustomValidator implements Validator
I want to be able to call its validate() method from a Service.
This is how this method looks:
#Override
public void validate(Object target, Errors errors) {
// validation goes here
MyClass request = (MyClass) target;
if (request.getId() == null) {
errors.reject("content.id", "Id is missing";
}
}
I don't want to have this validator in my endpoint, because I need to fetch the object to be validated from the database and then call the validation on it, so I need to do it from my service.
Can you please guide me on how to achieve this?
Use validation annotations in class but don't use #Valid on request body, then spring won't validate your class.
public class MyClass{
#NotNull
private Integer id;
#NotBlank
private String data;
}
Autowired Validator first
#Autowired
private final Validator validator;
Then for class validate using the validator conditionally when needed.
if(isValidate) {
Set<ConstraintViolation<MyClass>> violations = validator.validate(myClassObj);
if (!violations.isEmpty()) {
throw new ConstraintViolationException(new HashSet<ConstraintViolation<?>>(violations));
}
}
The Validator interface is, as far as i understand it, called as soon as a matching object (determined by the public boolean Validator.supports(Class clazz) method).
However, your goal seems to be to validate an object of MyClass only at a specific time, coming from your persistence layer to your service layer.
There are multiple ways to achieve this.
The first and most obvious one is to not extend any classes, but to use a custom component with some notion of a validation function:
#Component
public class CustomValidator{
public void validate(MyClass target) throws ValidationException {
// validation goes here
if (target.getId() == null) {
throw new ValidationException("Id is missing");
}
}
}
And inject/autowire it into your service object:
#Component
public class MyClassService{
// will be injected in first instance of this component
#Autowired
private CustomValidator validator
public MyClass get(MyClass target) {
try {
validator.validate(target);
return dao.retrieve(target);
} catch (ValidationException) {
// handle validation error
} catch (DataAccessException) {
// handle dao exception
}
}
}
This has the benefit that you yourself can control the validation, and error handling.
The negative side is the relatively high boilerplate.
However, if you want different Validators for different CRUD-Operations (or Service Methods), you may be interested in the Spring Validation Groups Feature.
First, you create a simple marker interface for each Operation you want to differ:
interface OnCreate {};
interface OnUpdate {};
Then, all you need to do is use the marker interfaces in the fields of your entity class,
using the Bean Validation Annotations:
public class MyClass{
#Null(groups = OnCreate.class)
#NotNull(groups = OnUpdate.class)
String id;
}
In order to use those groups in your Service Class, you will have to use the #Validated annotation.
#Validated
#Service
public class MyService {
#Validated(OnCreate.class)
void validateForCreate(#Valid InputWithGroups input){
// do something
}
#Validated(OnUpdate.class)
void validateForUpdate(#Valid InputWithGroups input){
// do something
}
}
Note that #Validated is applied to the service class as well as the methods. You can also set the group for the whole service, if you plan on using multiple services.
I for once mostly use the built-in Jakarta Bean Validation annotations in combination with marker interfaces, because of their ease of use and almost no boilerplate, while staying somewhat flexible and adjustable.
You could inject Validator and call validate
#Autowired
Validator validator;
And then call validate:
Set<ConstraintViolation<Driver>> violations = validator.validate(yourObjectToValidate);

Why is there a different result of an method when modified its object with a portable extension and applied an interceptor on the same object too?

Maybe you can give a hint, where to find a solution for this problem.
Currently I discover CDI portable extensions, like the sample shown here Wrapping an InjectionTarget.
This CDI portable extension reads values from properties files and configures fields of a Java object.
Here is a snippet of the extension:
public <T> void checkForPropertyFileAnnotation(
final #Observes ProcessInjectionTarget<T> pit) {
AnnotatedType<T> at = pit.getAnnotatedType();
if (!at.isAnnotationPresent(PropertyFile.class)) {
return;
}
// found annotation
[...load properties...]
[...assign properties to fields...]
[...create new wrapped InjectionTarget...]
pit.setInjectionTarget([created InjectionTarget]);
}
Running this extension on an example class does what it should do. But when I apply a, for example LoggingInterceptor like shown here simple cdi interceptor, the extension seems not working.
The logging interceptor:
#Log #Interceptor
public class LoggingInterceptor {
#AroundInvoke
public Object log(InvocationContext ctx) throws Exception {
Logger logger = Logger.getLogger(ctx.getTarget().getClass().getName());
logger.info("before");
Object result = ctx.proceed();
logger.info("after");
return result;
}
}
The sample-class looks like:
#Named
#Model // to use EL in jsp/jsf
#Log // the interceptor annotation
#PropertyFile("myprops.txt") // the annotation used within the extension
public class MyProperties {
#Property("version")
Integer version;
#Property("appname")
String appname;
public Integer getVersion() {
return version;
}
public String getAppname() {
return appname;
}
}
The content of the result-page:
<h:body bgcolor="white">
#{myProperties.appname} v#{myProperties.version}
</h:body>
It's not really true, the extension works, means it injects the appropriate values into the desired fields as I can see in the log-file, but after the interceptor gets applied, the injected values are gone.
Do you have any idea why this can happen? Maybe the extension handles a different instance of the class than the interceptor.
Thanx in advance for your reply!
You're twiddling fields on a proxy class, and then the getters are getting the values from the underlying instance.
I had the same problem. Both the interceptor and extension work separately, but when used together, the interceptor stops working. This is due to a bug in OpenWebBeans CDI implementation where interceptors are only processed when the injection target is an instance of an OpenWebBeans specific InjectionTargetImpl.
See: https://issues.apache.org/jira/browse/OWB-897

Spring #Validated in service layer

Hej,
I want to use the #Validated(group=Foo.class) annotation to validate an argument before executing a method like following:
public void doFoo(Foo #Validated(groups=Foo.class) foo){}
When i put this method in the Controller of my Spring application, the #Validated is executed and throws an error when the Foo object is not valid. However if I put the same thing in a method in the Service layer of my application, the validation is not executed and the method just runs even when the Foo object isn't valid.
Can't you use the #Validated annotation in the service layer ? Or do I have to do configure something extra to make it work ?
Update:
I have added the following two beans to my service.xml:
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean"/>
<bean class="org.springframework.validation.beanvalidation.MethodValidationPostProcessor"/>
and replaced the #Validate with #Null like so:
public void doFoo(Foo #Null(groups=Foo.class) foo){}
I know it is a pretty silly annotation to do but I wanted to check that if I call the method now and passing null it would throw an violation exception which it does. So why does it execute the #Null annotation and not the #Validate annotation ? I know one is from javax.validation and the other is from Spring but I do not think that has anything to do with it ?
In the eyes of a Spring MVC stack, there is no such thing as a service layer. The reason it works for #Controller class handler methods is that Spring uses a special HandlerMethodArgumentResolver called ModelAttributeMethodProcessor which performs validation before resolving the argument to use in your handler method.
The service layer, as we call it, is just a plain bean with no additional behavior added to it from the MVC (DispatcherServlet) stack. As such you cannot expect any validation from Spring. You need to roll your own, probably with AOP.
With MethodValidationPostProcessor, take a look at the javadoc
Applicable methods have JSR-303 constraint annotations on their
parameters and/or on their return value (in the latter case specified
at the method level, typically as inline annotation).
Validation groups can be specified through Spring's Validated
annotation at the type level of the containing target class, applying
to all public service methods of that class. By default, JSR-303 will
validate against its default group only.
The #Validated annotation is only used to specify a validation group, it doesn't itself force any validation. You need to use one of the javax.validation annotations like #Null or #Valid. Remember that you can use as many annotations as you would like on a method parameter.
As a side note on Spring Validation for methods:
Since Spring uses interceptors in its approach, the validation itself is only performed when you're talking to a Bean's method:
When talking to an instance of this bean through the Spring or JSR-303 Validator interfaces, you'll be talking to the default Validator of the underlying ValidatorFactory. This is very convenient in that you don't have to perform yet another call on the factory, assuming that you will almost always use the default Validator anyway.
This is important because if you're trying to implement a validation in such a way for method calls within the class, it won't work. E.g.:
#Autowired
WannaValidate service;
//...
service.callMeOutside(new Form);
#Service
public class WannaValidate {
/* Spring Validation will work fine when executed from outside, as above */
#Validated
public void callMeOutside(#Valid Form form) {
AnotherForm anotherForm = new AnotherForm(form);
callMeInside(anotherForm);
}
/* Spring Validation won't work for AnotherForm if executed from inner method */
#Validated
public void callMeInside(#Valid AnotherForm form) {
// stuff
}
}
Hope someone finds this helpful. Tested with Spring 4.3, so things might be different for other versions.
#pgiecek You don't need to create a new Annotation. You can use:
#Validated
public class MyClass {
#Validated({Group1.class})
public myMethod1(#Valid Foo foo) { ... }
#Validated({Group2.class})
public myMethod2(#Valid Foo foo) { ... }
...
}
Be careful with rubensa's approach.
This only works when you declare #Valid as the only annotation. When you combine it with other annotations like #NotNull everything except the #Valid will be ignored.
The following will not work and the #NotNull will be ignored:
#Validated
public class MyClass {
#Validated(Group1.class)
public void myMethod1(#NotNull #Valid Foo foo) { ... }
#Validated(Group2.class)
public void myMethod2(#NotNull #Valid Foo foo) { ... }
}
In combination with other annotations you need to declare the javax.validation.groups.Default Group as well, like this:
#Validated
public class MyClass {
#Validated({ Default.class, Group1.class })
public void myMethod1(#NotNull #Valid Foo foo) { ... }
#Validated({ Default.class, Group2.class })
public void myMethod2(#NotNull #Valid Foo foo) { ... }
}
As stated above to specify validation groups is possible only through #Validated annotation at class level. However, it is not very convenient since sometimes you have a class containing several methods with the same entity as a parameter but each of which requiring different subset of properties to validate. It was also my case and below you can find several steps to take to solve it.
1) Implement custom annotation that enables to specify validation groups at method level in addition to groups specified through #Validated at class level.
#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
public #interface ValidatedGroups {
Class<?>[] value() default {};
}
2) Extend MethodValidationInterceptor and override determineValidationGroups method as follows.
#Override
protected Class<?>[] determineValidationGroups(MethodInvocation invocation) {
final Class<?>[] classLevelGroups = super.determineValidationGroups(invocation);
final ValidatedGroups validatedGroups = AnnotationUtils.findAnnotation(
invocation.getMethod(), ValidatedGroups.class);
final Class<?>[] methodLevelGroups = validatedGroups != null ? validatedGroups.value() : new Class<?>[0];
if (methodLevelGroups.length == 0) {
return classLevelGroups;
}
final int newLength = classLevelGroups.length + methodLevelGroups.length;
final Class<?>[] mergedGroups = Arrays.copyOf(classLevelGroups, newLength);
System.arraycopy(methodLevelGroups, 0, mergedGroups, classLevelGroups.length, methodLevelGroups.length);
return mergedGroups;
}
3) Implement your own MethodValidationPostProcessor (just copy the Spring one) and in the method afterPropertiesSet use validation interceptor implemented in step 2.
#Override
public void afterPropertiesSet() throws Exception {
Pointcut pointcut = new AnnotationMatchingPointcut(Validated.class, true);
Advice advice = (this.validator != null ? new ValidatedGroupsAwareMethodValidationInterceptor(this.validator) :
new ValidatedGroupsAwareMethodValidationInterceptor());
this.advisor = new DefaultPointcutAdvisor(pointcut, advice);
}
4) Register your validation post processor instead of Spring one.
<bean class="my.package.ValidatedGroupsAwareMethodValidationPostProcessor"/>
That's it. Now you can use it as follows.
#Validated(groups = Group1.class)
public class MyClass {
#ValidatedGroups(Group2.class)
public myMethod1(Foo foo) { ... }
public myMethod2(Foo foo) { ... }
...
}

Jersey: Error when a class has both JAX-RS and JAX-WS annotations

Using Jersey 1.7, JAX-WS 2.2.3, Tomcat 6.0.30 and the following method declaration prevents Jersey servlet to start:
#POST
#Produces("text/plain")
public void postIt(#WebParam(name = "paramOne") final String paramOne,
final String paramTwo) {
// ...
}
The generated exception is:
SEVERE: Missing dependency for method public
java.lang.String com.sun.jersey.issue.MyResource.postIt(
java.lang.String,java.lang.String) at parameter at index 0
SEVERE: Method, public void
com.sun.jersey.issue.MyResource.postIt(
java.lang.String,java.lang.String),
annotated with POST of resource,
class com.sun.jersey.issue.MyResource,
is not recognized as valid resource method.
If the #WebParam annotation is removed, it all works fine.
Now, please have in mind that I am not trying to work with mere strings, rather, I am migrating complicated Objects that got marshalled/unmarshalled using SOAP to RESTful services, but I must provide both interfaces for a while, without breaking the previous WASDs. The method is just a minimalistic scenario.
Has any of you any idea of the status of this? Has it been fixed? Suggestions?
The specification is clear on this. Section 3.3.2.1 tells us that:
Resource methods MUST NOT have more
than one parameter that is not
annotated with one of the above listed
annotations.
The above listed annotations are the JAX-RS parameter annotations: #QueryParam, #MatrixParam, etc.
There is, however, a Jersey specific way to solve this problem. Using InjectableProvider. So, a method that defines two non-JAX-RS parameters:
#POST
public void postIt(#CustomInjectable final Customer customer,
final Transaction transaction) {
// ...
}
Of course, we have to code the annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface CustomInjectable {
}
An implementation of InjectableProvider that knows how to provide Customers:
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.InjectableProvider;
import com.sun.jersey.api.model.Parameter;
#Provider
public class CustomerInjectableProvider implements
InjectableProvider<CustomInjectable, Parameter> {
// you can use #Context variables, as in any Provider/Resource
#Context
private Request request;
public ComponentScope getScope() {
// ComponentScope.Singleton, Request or Undefined
}
public Injectable getInjectable(ComponentContext i,
CustomInjectable annotation,
Parameter param) {
Injectable injectable = null;
if (Customer.getClass().isAssignableFrom(param.getParameterClass()) {
injectable = getInjectable();
}
return injectable;
}
private Injectable getInjectable() {
return new Injectable<Customer>() {
public Customer getValue() {
// parse the customer from request... or session... or whatever...
}
};
}
}
But, Jersey considers only the last annotation (see JERSEY-ISSUE-731), so be careful.
And, a more portable way (if you do care about that, anyway):
// simple bean
public class CustomerWithTransaction {
private Customer customer;
private Transaction transaction;
// getters and setters
}
Then change the method to:
#POST
public void postIt(CustomerWithTransaction customerWithTransaction) {
// ...
}
Then create your own MessageBodyReader for CustomerWithTransaction, where you can also access any context variables (request, headers, etc.).

Categories

Resources