Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAX-RS (Jersey) ExceptionMapper - @Context injection into static/singleton class - it works, but why?

I have a single-instance class, implementing ExceptionMapper. It's not a static class, but it's a class for which I know only single instance is created (I checked - constructor is called only once).

My class uses @Context HttpServletRequest, and I can clearly observe that when my ExceptionMapper.toResponse() method is called, the @Context 'request' parameter has a value which is relevant for a request where the exception is thrown.

The doc says this is indeed by-design supported feature and that it's done by using "proxies".

I wonder how exactly this is implemented - how a single instance can have different member variable values simultaneously?

Thank you,
AG

P.S.: here's the test code:

@Provider
public class MyExceptionMapper implements ExceptionMapper<Exception> {

    public MyExceptionMapper() {
        System.out.println("CTOR!!");
    }

    @Context HttpServletRequest req;

    public static boolean done = false;  
    public Response toResponse(Exception ex) {
        if (!done) {
            done = true;
            Thread.sleep(10000);
        }
        System.out.println(req.getRequestURI());
        return null;
    }
}

My REST handler method throws exception, so when I execute the following 2 requests "in parallel" (the sleep above makes sure first one is not finished when second one arrives and IMHO should modify the one-and-only 'req' field):

- http://localhost/app/one
- http://localhost/app/two

my program prints:

CTOR!
http://localhost/app/one
http://localhost/app/two
like image 924
Wanna Know All Avatar asked Jul 20 '13 19:07

Wanna Know All


1 Answers

The simplest method of achieving the effect you observe is for the injected HttpServletRequest object to actually be a proxy object, a thread-aware delegate for the real HttpServletRequest. When you call methods on the delegate, all they do is look up the correct real object (e.g., via a thread local variable) and pass the call onto that. This strategy is relatively simple to get right, and as it is an interface we definitely don't have to worry about field accesses (which are quite a bit trickier to proxy for).

There's a few different ways to construct such a proxy object. In particular, it could be done by directly implementing the HttpServletRequest interface, or it could be done more generically via the Java general dynamic proxy mechanism (which can construct a proxy for any interface). There are other more elaborate possibilities such as runtime code generation, but they're unnecessary here. OTOH, I wouldn't be at all surprised if HttpServletRequest was directly implemented; it's a somewhat important class for a JAX-RS implementation…

like image 128
Donal Fellows Avatar answered Oct 11 '22 19:10

Donal Fellows