Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

@Context returns Proxy instead of HttpServletRequest (No thread local value in scope for proxy)

I have a bit of a problem with understanding why @Context dependency injection returns collection of $Proxy(random number) instances instead of HttpServletRequest or HttpServletResponse.

I am using Glassfish 3.1.2.2 with it's version of Jersey(Jersey: 1.11.1) and my app is built as EAR application.

I have simple @Remote interface where I annotate my methods and REST services work without any problems, but the moment I try to access HttpServletRequest information it just causes problems.

I have annotated private fields in my session bean:

@Context
private HttpServletRequest request;
@Context
private HttpServletResponse response;

And also created method signature to include @Context as set of parameters:

@POST
@Path("authenticate")
@Consumes(MediaType.APPLICATION_FORM_URLENCODED)
@Produces(MediaType.APPLICATION_JSON)
public Response authenticate(@FormParam("username") String username, @FormParam("password") String password, @Context HttpServletRequest request, @Context HttpServletResponse response); 

When I try and debug the method just after an entry I can see that global request and response objects are null where local method instances are of $ProxyXXX type.

The problem is I can't access (or I'm not sure how) these objects. According to tutorials on the web they should be available to me, but the moment I try to access these, this is being thrown:

WARNING: StandardWrapperValve[RestDataService]: PWC1406: Servlet.service() for servlet RestDataService threw exception
java.lang.IllegalStateException: No thread local value in scope for proxy of class $Proxy245
    at com.sun.jersey.server.impl.ThreadLocalInvoker.invoke(ThreadLocalInvoker.java:93)
    at $Proxy245.getContextPath(Unknown Source)

This is how I am trying to call these (in this example I simply call getContextPath)

@Override
public Response authenticate(String username, String password, HttpServletRequest request, HttpServletResponse response) {

    System.out.println("just a test: " + request.getContextPath());

What am I missing here? Has anyone experienced similar problem?

Greg

like image 236
Greg Avatar asked Nov 02 '12 15:11

Greg


1 Answers

To understand this problem you need to understand how scopes work. To describe what happens, here is an example. Suppose you have this

@Singleton
@Path("..")
public class SomeResource {

    @Context
    private HttpServletRequest request;
}

The problem that this situation poses is that an HttpServletRequest is generated for each request, but the resource class is created only once, as it's a singleton, so there is initially no HttpServletRequest to inject into the singleton when it is created.

To solve this problem, Jersey injects a proxy. So conceptually, the result is more like

@Singleton
@Path("..")
public class SomeResource {

    @Context
    private ProxyHttpServletRequest proxyRequest;
}

When the request comes in, the actual HttpServletRequest is put into a ThreadLocal in a scope context. This is not an exact implementation but you can imagine a scope context as something like

public class RequestScopeContext {
    private static final ThreadLocal<HttpServletReqest> request
            = new ThreadLocal<>();

    public static HttpServletRequest get() { .. }
    public static void setRequest(HttpServletRequest request) { .. }
}

When the request comes in the HttpServletRequest is set into the context. And when calls are made to the HttpServletRequest inside the SomeResource, it is actually made on the proxy object which grabs the request from the ThreadLocal and forwards the calls. Conceptually, you imagine it to look something like

class ProxyHttpServletRequest {
    public String getContextPath() {
        HttpServletRequest request = RequestScopeContext.get();
        return request.getContextPath();
    }
}

Now suppose we have this instead

@Singleton
@Path("..")
public class SomeResource {

    @GET
    public Response get(@Context HttpServletRequest request) {
        ...
    }
}

The difference with this is that the request is no longer injected into a field, so no proxy is required. The HttpServletRequest is only needed when the call to the method is made. So Jersey will inject the actual request and not a proxy.

Just to note, that this pattern is not specific to just Jersey. Any framework were DI and scopes are involved, will use a similar proxy pattern when trying to inject lesser scope objects into wider scope objects.


To OP: Now this is not your exact problem. The above answer will more likely benefit the majority of people who have questions about the proxy.

In your case, you said you are trying to inject the HttpServletRequest into a @Remote EJB. For one thing I didn't even know that was or is possible. My guess is that the EJB engine never sets the ThreadLocal. So when Jersey tries to call it, there is nothing in the context.

The @Context annotation is meant for JAX-RS components, not for EJBs. I personally, don't know how to inject an HttpServletRequest into an EJBs, as I don't work much with EJBs. So anyone facing your exact problem, will need to search for how to do that. But like I said, I don't think that is the majority of people who search this question. I imagine they are simply wondering why the request is a proxy instead of the actual request. So this answer is more geared towards them.

like image 194
Paul Samsotha Avatar answered Nov 15 '22 00:11

Paul Samsotha