users@jersey.java.net

Re: [Jersey] Guice access to Jersey-created objects

From: Craig McClanahan <Craig.McClanahan_at_Sun.COM>
Date: Thu, 13 Aug 2009 21:34:51 -0700

Craig McClanahan wrote:
> Richard Wallace wrote:
>> On Thu, Aug 13, 2009 at 11:32 AM, Craig
>> McClanahan<Craig.McClanahan_at_sun.com> wrote:
>>
>>> Furthering my explorations with integrating Jersey and Guice, I'd like to be
>>> able to inject Jersey-provided components into a Guice-instantiated service
>>> class. As a particular example, I have a "transformer" class that turns my
>>> internal data model object into a suitable external representation,
>>> including some computed URIs. For that, I need to have Guice able to inject
>>> things like the UriInfo object Jersey injects into resource classes.
>>>
>>> OK, I can bind a Provider<UriInfo> into Guice's injector ... but where does
>>> the implementation of this acquire the UriInfo instance from Jersey? Any
>>> ideas on a strategy to make these kinds of things visible to Guice's
>>> injector?
>>>
>>
>> What I've been doing is allowing Guice to create my WebApplication
>> instance and injecting that into my own GuiceServletContainer which
>> overrides the create() method to return the injected WebApplication.
>> If you do that, then you can do something like what I have here
>> <http://pastie.org/583188>. It's in Scala, but you should be able to
>> get the idea.
>>
>> I also talked with Paul once about taking it a bit further. It
>> requires exposing more of the Jersey services so they could be
>> injected using Guice 2.0's custom integrations. The biggest drawback
>> is that they wouldn't be injectable into the constructor that way,
>> only on fields and methods. But it would be better than nothing I
>> figure. Unfortunately, I haven't found the time to follow up on this.
>> Maybe you can?
>>
>>
> Thanks for the info ... this will really help. I'll see if I can come
> up with something we could consider adding to jersey-guice.
>
OK, thanks to Rich's head start I've made some progress, but also run
into a roadblock that is probably due to me misunderstanding some Jersey
internals. I'm also undoubtedly running into some of the same issues
Rich saw.

To start with, I'm leveraging the new Guice 2.0 "@Provides" annotation
to let your Guice module definition supply Provider<XXX> instances that
operate like factories for some key Jersey internal constructs. An
excerpt of my module file looks like this (note -- I'm using Jersey in a
Java SE app, not a webapp, so I'm not using the servlet related stuff):

    public class ServerModule extends AbstractModule {

        protected void configure() {
            ... bindings for my application objects ...
            bind(WebApplication.class).
                to(WebApplicationImpl.class).
                in(Scopes.SINGLETON);
        }

        @Provides
        public HttpContext httpContext(WebApplication webApplication) {
            return webApplication.getThreadLocalHttpContext();
        }

        @Provides
        public UriInfo uriInfo(HttpContext httpContext) {
            return httpContext.getUriInfo();
        }

        ...

    }

To the non-Guice-savvy, what we're doing is declaring:

* If Guice is asked to inject something of type WebApplication,
  create a singleton instance of the Jersey internal class
  WebApplicationImpl and return that. Yes, this is a real hack,
  but dont' know how else to hook into Jersey's own
  component injection machinery.

* If Guice is asked to inject something of type HttpContext,
  go get the WebApplication instance, then call its
  getThreadLocalHttpContext() method, and return its return value.

* If Guice is asked to inject something of type UriInfo,
  go get the HttpContext instance (which will go get the
  WebApplication instance if needed), call getUriInfo(),
  and return its return value.

Now, in theory, I should be able to let Guice inject an UriInfo into a
resource class (yes, I know Jersey can do that already, but only for
root resource classes and providers -- I need UriInfo to be injectable
into any Guice-created instance):

    @Path("foo")
    public class MyResource {

        @Inject
        public class MyResource(final UriInfo uriInfo) {
            this.uriInfo = uriInfo;
        }

        private final UriInfo uriInfo;

        ...

    }

However, when I trigger a call to this resource, the
httpContext.getUriInfo() method throws an IllegalStateException
(ultimately caused by an NPE) which implies that the set() method was
never set to populate the ThreadLocal instance for this thread.

Phew, that's a lot of Jersey internals ... but can anyone (Paul :-)
figure out why this is happening? AFAICT I'm in the normal Jersey
handleRequest() loop, including the execution of a couple of Jersey
filters, so I would have expected this to work. Note that it also
fails, but differently, when I try to do field injection instead ... it
just injects null.

> Craig
>> Rich
>>
>> [1] http://code.google.com/docreader/#p=google-guice&s=google-guice&t=CustomInjections
>> [2] http://n2.nabble.com/Enhanced-Guice-Jersey-integration-td3140841.html#a3140841
>>
>>
Craig

>> ---------------------------------------------------------------------
>> To unsubscribe, e-mail: users-unsubscribe_at_jersey.dev.java.net
>> For additional commands, e-mail: users-help_at_jersey.dev.java.net
>>
>>
>