Registering a custom ResourceMethodInvocationHandler in Jersey - jersey

I am attempting to intercept a resource call after it's JSON has been unmarshalled. Reading through some forums and posts I discovered that I may be able to do so by implementing org.glassfish.jersey.server.spi.internal.ResourceMethodInvocationHandlerProvider. Having done so I am now stuck trying to get my CustomResourceMethodInvocationHandler provider registered so that the jersey/hk2 internals call my overridden public InvocationHandler create(Invocable invocable) method. Any help would be much appreciated!

Let's have a look at this approach:
(Tested with Jersey 2.10 and JSON serialization)
==============
1) Implement a custom ResourceMethodInvocationHandlerProvider
package com.example.handler;
import java.lang.reflect.InvocationHandler;
import org.glassfish.jersey.server.model.Invocable;
import org.glassfish.jersey.server.spi.internal.ResourceMethodInvocationHandlerProvider;
public class CustomResourceInvocationHandlerProvider implements
ResourceMethodInvocationHandlerProvider {
#Override
public InvocationHandler create(Invocable resourceMethod) {
return new MyIncovationHandler();
}
}
2) Implement a custom InvocationHandler
package com.example.handler;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyIncovationHandler implements InvocationHandler {
#Override
public Object invoke(Object obj, Method method, Object[] args)
throws Throwable {
// optionally add some logic here
Object result = method.invoke(obj, args);
return result;
}
}
3) Create a custom Binder class and register your CustomResourceInvocationHandlerProvider
package com.example.handler;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.spi.internal.ResourceMethodInvocationHandlerProvider;
public class CustomBinder extends AbstractBinder {
#Override
protected void configure() {
// this is where the magic happens!
bind(CustomResourceInvocationHandlerProvider.class).to(
ResourceMethodInvocationHandlerProvider.class);
}
}
4) Optionally: Set breakpoint in ResourceMethodInvocationHandlerFactory
Just to understand how the selection of ResourceMethodInvocationHandlerProvider in org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory works.
==============
As you can see, the most important thing is to bind your CustomResourceInvocationHandlerProvider.class to the ResourceMethodInvocationHandlerProvider.class. After doing this, HK2 knows about your Provider and also about your Handler!
Hope, I could help.

Related

How can I create a Pointcut or Around for extended classes with shared interface?

I have an abstract service class.
abstract class AbstractService<T> {
public void saveNew(T entity) {
}
}
And I have two more abstract classes extends AbstractService and implement a shared interface.
abstract class MoreAbstractService2<T extends Some2>
extends AbstractService<T>
implements SharedInterface {
}
abstract class MoreAbstractService3<T extends Some3>
extends AbstractService<T>
implements SharedInterface {
}
Now I want to validate the entity argument on these two extending services' saveNew(T) method.
How can I define a #Pointcut and (or) an #Around for following conditions?
extends the AbstractService class
implements the SharedInterface interface
you can use within as following:
within(com.somepackage.Super+)
where com.somepackage.Super is the fully qualified base class name and + means "all subclasses". Other pointcut is
execution(* com.somepackage.Super+.*(..))
R.G's solution has a few disadvantages:
The pointcut matches too many joinpoints.
Thus it needs reflection in order to filter out the unnecessary ones.
I am going to show you a stand-alone AspectJ solution (no Spring, I am not a Spring user), but the aspect would look just the same in Spring, you only need to make it a #Component or declare a #Bean factory in your configuration. But the same applies to all the classes you want to intercept, of course.
Because I prefer a full MCVE with all necessary dependency classes in order for you to be able to copy, compile and run it, and because I also added negative test cases (sub-classes only extending the abstract base class or only implementing the interface), this is a lot of code. So please bear with me:
Abstract classes, interface and helper classes:
package de.scrum_master.app;
public abstract class AbstractService<T> {
public void saveNew(T entity) {
System.out.println("Saving new entity " + entity);
}
}
package de.scrum_master.app;
public class Some2 {}
package de.scrum_master.app;
public class Some3 {}
package de.scrum_master.app;
public abstract class MoreAbstractService2<T extends Some2>
extends AbstractService<T>
implements SharedInterface {}
package de.scrum_master.app;
public abstract class MoreAbstractService3<T extends Some3>
extends AbstractService<T>
implements SharedInterface {}
package de.scrum_master.app;
public interface SharedInterface {
void doSomething();
}
Driver application (AspectJ + POJOs, not Spring):
This driver class contains some static inner classes subclassing the given base classes and/or implementing the shared interface. Two are used for positive tests (should be intercepted), two for negative tests (should not be intercepted). Each class also contains an additional method as another negative test case which should not be matched - better safe than sorry.
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
// Should be intercepted
InterceptMe1 interceptMe1 = new InterceptMe1();
interceptMe1.saveNew(new Some2());
interceptMe1.doSomething();
interceptMe1.additional();
printSeparator();
// Should be intercepted
InterceptMe2 interceptMe2 = new InterceptMe2();
interceptMe2.saveNew(new Some3());
interceptMe2.doSomething();
interceptMe2.additional();
printSeparator();
// Should NOT be intercepted
DontInterceptMe1 dontInterceptMe1 = new DontInterceptMe1();
dontInterceptMe1.saveNew(new Some2());
dontInterceptMe1.additional();
printSeparator();
// Should NOT be intercepted
DontInterceptMe2 dontInterceptMe2 = new DontInterceptMe2();
dontInterceptMe2.additional();
printSeparator();
}
private static void printSeparator() {
System.out.println("\n----------------------------------------\n");
}
static class InterceptMe1 extends MoreAbstractService2<Some2> {
#Override
public void doSomething() {
System.out.println("Doing something in MoreAbstractService2<Some2>");
}
public void additional() {
System.out.println("Additional method in MoreAbstractService2<Some2>");
}
}
static class InterceptMe2 extends MoreAbstractService3<Some3> {
#Override
public void doSomething() {
System.out.println("Doing something in MoreAbstractService3<Some3>");
}
public void additional() {
System.out.println("Additional method in MoreAbstractService3<Some3>");
}
}
static class DontInterceptMe1 extends AbstractService<Some2> {
public void additional() {
System.out.println("Additional method in AbstractService<Some2>");
}
}
static class DontInterceptMe2 implements SharedInterface {
#Override
public void doSomething() {
System.out.println("Doing something in SharedInterface"); }
public void additional() {
System.out.println("Additional method in SharedInterface");
}
}
}
Aspect:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class EntityValidationAspect {
#Before(
"execution(* saveNew(*)) && " +
"args(entity) && " +
"target(de.scrum_master.app.SharedInterface) && " +
"target(de.scrum_master.app.AbstractService)"
)
public void validateEntity(JoinPoint thisJoinPoint, Object entity) {
System.out.println("-> Pre-save entity validation: " + entity);
}
}
As you can see, the aspect uses two target() pointcuts which must both match. It also specifically targets any saveNew method with a single argument saveNew(*), binding that argument as an advice method parameter via args().
For demonstration's sake I do not validate anything (I don't know how you want to do that) but just print the entity. Thus, a #Before advice is sufficient. If in case of negative validation you want to throw an exception, this advice type is also okay. If you need to do more, such as manipulate the entity's state or replace it before passing it on to the target method, call an alternative target method instead or none at all, return a specific result (in case of non-void methods, here not applicable), handle exceptions from the target method etc., you ought to use an #Around advice instead.
Console log:
-> Pre-save entity validation: de.scrum_master.app.Some2#28a418fc
Saving new entity de.scrum_master.app.Some2#28a418fc
Doing something in MoreAbstractService2<Some2>
Additional method in MoreAbstractService2<Some2>
----------------------------------------
-> Pre-save entity validation: de.scrum_master.app.Some3#5305068a
Saving new entity de.scrum_master.app.Some3#5305068a
Doing something in MoreAbstractService3<Some3>
Additional method in MoreAbstractService3<Some3>
----------------------------------------
Saving new entity de.scrum_master.app.Some2#1f32e575
Additional method in AbstractService<Some2>
----------------------------------------
Additional method in SharedInterface
----------------------------------------
Et voilĂ  - the aspect does exactly what you asked for, as far as I understand your requirement. :-) More specifically, it does not get triggered in the third case when saveNew is being called, but the class does not implement the interface.
Following code can be used for the validation mentioned.
The point cut is to intercept on the execution of a specific method for the subclasses of AbstractService and the code logic is to only validate if SharedInterface is a superinterface of the target bean.
The use of isAssignableFrom() is required as the interfaces proxied by the AOP proxy does not include SharedInterface. As per my understanding , a pointcut expression to match the second criteria will not be possible for the same reason and hence handled the requirement in the code logic.
Hope this helps
#Aspect
#Component
public class ValidationAspect {
#Pointcut("execution(* package.to.AbstractService+.saveNew(..))")
public void isAbstractServiceType() {
}
#Around("isAbstractServiceType() && args(entity) && target(bean)")
public void validateEntityArugments(ProceedingJoinPoint pjp, Object entity, Object bean) throws Throwable {
if (SharedInterface.class.isAssignableFrom(bean.getClass())) {
System.out.println(entity);
// ..validate
}
pjp.proceed();
}
}

Spring JsonExceptionMapper ExceptionMapper handling.

I am getting following error in my weblogic console when i am starting my server.
SEVERE: Missing dependency for constructor
public com.test.mine.exception.JsonExceptionMapper(java.lang.String,com.fasterxml.jackson.core.JsonLocation) at parameter index 0
SEVERE: Missing dependency for constructor public com.test.mine.exception.JsonExceptionMapper(java.lang.String,com.fasterxml.jackson.core.JsonLocation) at parameter index 1
Below is my java code.
package com.test.mine.exception;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import com.fasterxml.jackson.core.JsonLocation;
import com.fasterxml.jackson.core.JsonParseException;
#Provider
#Service
public class JsonExceptionMapper extends JsonParseException implements ExceptionMapper {
public JsonExceptionMapper(String msg, JsonLocation loc) {
super(msg, loc);
// TODO Auto-generated constructor stub
}
private static final Logger LOGGER = LoggerFactory.getLogger(JsonExceptionMapper.class);
protected Logger getLogger() {
return LOGGER;
}
public Status getStatus(JsonParseException thr) {
return Status.BAD_REQUEST;
}
#Override
public Response toResponse(Throwable arg0) {
// TODO Auto-generated method stub
return Response.status(Status.BAD_REQUEST).type(MediaType.APPLICATION_JSON_TYPE).build();
}
}
The annotation #Service tells spring to create a singleton of the annotated class. At startup spring tries to create that instance and to provide the required constructor args String msg, JsonLocation loc which it does not find, so the exception.
JsonExceptionMapper does not look like a service, and it should not be a singleton. Instead it must be created whenever an exception is created.
I have never worked with that class, so sorry, cannot give you any advice on how to do that.
I bumped into a similar problem while configuring swagger to work with Jersey. After searching various forums found that Jersey scanning require a constructor without parameters. I added a a constructor and it worked for me.

How to access wicket session from Jersey-2 request filter?

In Jersey 1.x we accessed the Wicket session from a (Jersey) session attribute, as described here https://stackoverflow.com/a/15767824/1399659.
In moving to Jersey 2.x it seems the proper pattern to use a ContainerRequestFilter, which also allows Spring bean injection as well. We have this working successfully by including
<param-name>jersey.config.server.provider.packages</param-name>
as an init-param to the ServletContainer and using the #Provider annotation on a ContainerRequestFilter implementation. But this container filter is a singleton, and it's not possible to inject the HttpServletRequest into this (see JERSEY-2114)
In the filter() method we have access to the ContainerRequestContext but can't access the HttpServletRequest from there.
So is there a way to either:
Enable Spring bean injection within a servlet filter (with Jersey too)?
Access the servlet request from within a ContainerRequestFilter?
Access wicket session from Spring-bean-aware object with Jersey filtering ability some other way?
`
import java.io.IOException;
import javax.servlet.http.HttpSession;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.Response.Status;
import javax.ws.rs.ext.Provider;
import org.apache.wicket.injection.Injector;
#Provider
public class SecurityContextFilter implements ContainerRequestFilter {
//#Context
//HttpServletRequest webRequest;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
//HttpSession httpSession = webRequest.getSession();
//MyWicketSession mySession = (MyWicketSession) httpSession.getAttribute("wicket:" + BaseConstants.WICKET_FILTER_NAME + ":session");
//doAuthCheck(mySession, requestContext);
}
...
}
`
Thanks in advance
Fixed in Jersey 2.4:
import javax.annotation.Priority;
import javax.ws.rs.Priorities;
#Provider
#Priority(Priorities.AUTHENTICATION)
public class AuthRequestFilter implements ContainerRequestFilter {
#Context
HttpServletRequest webRequest;
#Override
public void filter(ContainerRequestContext requestContext) throws IOException {
final HttpSession session = webRequest.getSession();
requestContext.setSecurityContext(new SecurityContext() {
#Override
public Principal getUserPrincipal() {
return new PrincipalImpl((String)session.getAttribute("USER_NAME"));
}
#Override
public boolean isUserInRole(String s) {
return false;
}
#Override
public boolean isSecure() {
return false;
}
#Override
public String getAuthenticationScheme() {
return null;
}
});
}
}
You can also register the filter without using #Provider annotation:
import org.glassfish.jersey.server.ResourceConfig;
import javax.ws.rs.ApplicationPath;
/**
* Root REST resource class.
*/
#ApplicationPath("/rest")
public class RootResource extends ResourceConfig {
/**
* Initializes all resources from REST package.
*/
public RootResource() {
packages("com.example.rest");
register(AuthRequestFilter.class);
}
}
Note: Glassfish 4.0.0 uses old Jersey 2.0.
You will have to upgrade Jersey using these tips (it's not proven to work well). Or the better way is to download nightly build of Glassfish 4.0.1. but it's not completely stable at the moment. I hope the new version will be released soon.

How do I pass arguments to Spring AOP advice with annotated parameters?

I am using Spring 3.1.2.RELEASE with cglib load-time weaving and I am trying to get advice to work with a method that has custom annotations and annotated parameters.
Advice:
#Aspect
public class MyAdvice
{
#Around("execution(#com.mycompany.locking.Lock * *(#com.mycompany.locking.LockVal(*), ..)) " +
"&& args(batch) && #args(propertyToLock)"
public Object lockAndProceed(ProceedingJoinPoint pjp, Object batch, LockVal propertyToLock) throws Throwable {
//Do stuff....
pjp.proceed();
}
}
Here is the class that I am testing:
public interface UpdateManager
{
public void processUpdate(MyBatchObject batch);
}
public class UpdateManagerImpl implements UpdateManager
{
#Lock
public void processUpdate(#LockVal("lockValue") MyBatchObject batch)
{
//Do stuff...
}
}
The problem is that I can't get the advice to execute. If I remove the #args and args conditions in the pointcut, the advice fires, but then I have to dig through the ProceedingJoinPoint to get the parameter that I need.
Why isn't the advice firing? Did I do something wrong?
Edit: The following pointcut DOES WORK as a standalone program with Spring:
#Aspect
public class MyAdvice
{
#Around("execution(#com.mycompany.locking.Lock * *(#com.mycompany.locking.LockVal(*), ..)) " +
"&& args(batch)"
public Object lockAndProceed(ProceedingJoinPoint pjp, Object batch) throws Throwable {
//Do stuff....
pjp.proceed();
}
}
However, it does NOT work under JBoss 6 using load-time weaving. I suppose my question should be, then, why does it work as a standalone program but not under JBoss 6?
Update: I forgot to mention that #args() is not meant to match a parameter's annotation, but a parameter type's annotation, which is not what you want and which thus I do not use here.
You cannot bind a parameter's annotation via args(), only the parameter itself. This means that you can only access the parameter's annotation via reflection. You need to determine the method signature, create a Method object from it and then iterate over the method parameters' annotations. Here is a full code sample:
package com.mycompany.locking;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Lock {}
package com.mycompany.locking;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface LockVal {
String value() default "";
}
package com.mycompany;
public class MyBatchObject {}
package com.mycompany;
public interface UpdateManager {
public void processUpdate(MyBatchObject batch);
}
package com.mycompany;
import com.mycompany.locking.Lock;
import com.mycompany.locking.LockVal;
public class UpdateManagerImpl implements UpdateManager {
#Lock
#Override
public void processUpdate(#LockVal("lockValue") MyBatchObject batch) {
System.out.println("Processing update");
}
public static void main(String[] args) {
UpdateManager updateManager = new UpdateManagerImpl();
updateManager.processUpdate(new MyBatchObject());
}
}
package com.mycompany.aop;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import com.mycompany.MyBatchObject;
import com.mycompany.locking.LockVal;
#Aspect
public class MyAspect {
#Pointcut("execution(#com.mycompany.locking.Lock * *(#com.mycompany.locking.LockVal (*), ..)) && args(batch)")
public void lockedMethod(MyBatchObject batch) {}
#Around("lockedMethod(batch)")
public Object lockAndProceed(ProceedingJoinPoint pjp, MyBatchObject batch) throws Throwable {
System.out.println(pjp);
System.out.println(batch);
MethodSignature methodSignature = (MethodSignature) pjp.getSignature();
Class<?> clazz = methodSignature.getDeclaringType();
Method method = clazz.getDeclaredMethod(methodSignature.getName(), methodSignature.getParameterTypes());
LockVal propertyToLock;
for (Annotation ann : method.getParameterAnnotations()[0]) {
if(LockVal.class.isInstance(ann)) {
propertyToLock = (LockVal) ann;
System.out.println(propertyToLock.value());
}
}
return pjp.proceed();
}
}
When I run UpdateManagerImpl.main, I see the following output, just as expected:
execution(void com.mycompany.UpdateManagerImpl.processUpdate(MyBatchObject))
com.mycompany.MyBatchObject#86f241
lockValue
Processing update
Disclaimer: I am not a Spring guy, I just tested this with plain AspectJ, not Spring AOP.
This is not a solution, but should take you a step further:
I am assuming you made a typo in your annotations, you probably meant #Aspect and not #Advice?
The suggestion that I have would be to try out these:
a. Separate out into named point cuts and the advice that you want to apply on the pointcut:
#PointCut("execution(#com.mycompany.locking.Lock * *(#com.mycompany.locking.LockVal(*), ..)) && args(batch) && #args(propertyToLock)")
public void mypointcut(Object batch, LockVal propertyToLock){}
#Around("mypointcut(batch, propertyToLock)"
public Object lockAndProceed(ProceedingJoinPoint pjp, Object batch, LockVal propertyToLock) throws Throwable {
//Do stuff....
pjp.proceed();
}
b. It could be that either args expression or #args expression is causing the issue - try keeping one and removing other and seeing which combination works.
c. If this does not narrow things down, one more option could be to explicitly add an argNames expression also, it could be that the argument names are being cleaned out and not being matched up by name at runtime:
#PointCut("execution(#com.mycompany.locking.Lock * *(#com.mycompany.locking.LockVal(*), ..)) && args(batch) && #args(propertyToLock) && argNames="batch,test1,test2")
public void mypointcut(Object batch, LockVal propertyToLock){}

Spring Pre/Post method security annotations not working

I can't seem to get Spring Pre/Post method security annotations to work. I've read every related stackoverflow question on the topic, and the main suggestion is to ensure that global-method-security is enabled in the same context as the beans which you wish to secure. I have the following my dispatcher-servlet.xml:
<context:component-scan base-package="com.package.path" />
<context:annotation-config />
<security:global-method-security pre-post-annotations="enabled" />
The beans in question are in "com.package.path". I know that Spring is creating instances of them correctly, as injection is working just fine and requests are being serviced by the intended classes.
So, here's an example service class in "com.package.path":
#Controller
#RequestMapping("/article")
public class ArticleServiceImpl extends GWTController implements ArticleService {
#Autowired
public ArticleServiceImpl(DataSource ds) {
}
#Override
#PreAuthorize("hasRole('ROLE_BASIC_USER')")
public Article save(Article article) {
}
}
The annotation on the save method does not work. A few important notes:
I'm using GWT, though from what I've read, that shouldn't matter much.
I have method security working perfectly well in another, similar project. The only difference is that there is a DAO layer in the other project, which is not present in this one. It's in this layer that I have annotation security working. However, it shouldn't matter what "layer" this is, as long as Spring is responsible for creation of the beans, right?
The interface "ArticleService" above is a GWT service interface. I've tried putting the annotation there, but that doesn't work either.
Here's my GWTController class, referenced above, if needed:
package com.areahomeschoolers.baconbits.server.spring;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.context.ServletConfigAware;
import org.springframework.web.context.ServletContextAware;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import com.areahomeschoolers.baconbits.server.util.ServerContext;
import com.google.gwt.user.client.rpc.RemoteService;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
/**
* Spring controller class that handles all requests and passes them on to GWT. Also initializes server context.
*/
public class GWTController extends RemoteServiceServlet implements ServletConfigAware, ServletContextAware, Controller, RemoteService {
private static final long serialVersionUID = 1L;
protected ServletContext servletContext;
#Override
public ServletContext getServletContext() {
return servletContext;
}
// Call GWT's RemoteService doPost() method and return null.
#Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
// load our ServerContext with current request, response, session, user, appContext, etc.
ServerContext.loadContext(request, response, servletContext);
try {
doPost(request, response);
} finally {
ServerContext.unloadContext();
}
return null; // response handled by GWT RPC over XmlHttpRequest
}
#Override
public void setServletConfig(ServletConfig conf) {
try {
super.init(conf);
} catch (ServletException e) {
e.printStackTrace();
}
}
#Override
public void setServletContext(ServletContext servletContext) {
this.servletContext = servletContext;
}
#Override
protected void checkPermutationStrongName() throws SecurityException {
return;
}
#Override
protected void doUnexpectedFailure(Throwable e) {
e.printStackTrace();
super.doUnexpectedFailure(e);
}
}
Security aspect provided by Spring Security inherits all limitations of Spring Framework proxy-based AOP support. In particular, aspects are not applied to calls that happen "inside" the objects (unless you use AspectJ weaving), see 7.6.1 Understanding AOP proxies.
So, if you want to use security aspect this way, you need to use GWT integration mechanism that make calls to your service from the outside, i.e. a mechanism that doesn't require your services to extend RemoteServiceServlet.
Something like spring4gwt should work fine.

Resources