users@jersey.java.net

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

From: Richard Wallace <rwallace1979_at_gmail.com>
Date: Fri, 14 Aug 2009 13:44:59 -0700

On Fri, Aug 14, 2009 at 9:47 AM, Craig
McClanahan<Craig.McClanahan_at_sun.com> wrote:
> Paul Sandoz wrote:
>
> Hi Craig,
> What is the scope of MyResource? have you bound it to a Scopes.SINGLETON
> ? If so i am guessing the MyResource class is instantiated by Guice outside
> the scope of the request/response.
>
> I'm not explicitly binding the root resource class used in my example ...
> GuiceComponentProviderFactory is finding and registering them.  But for my
> general purpose needs, I need to be able to bind things like UriInfo into
> custom scopes.
>
> You could do the following to bind WebApplication:
>         protected void configure() {
>             ... bindings for my application objects ...
>             bind(WebApplication.class).
>                 toInstance(WebApplicationFactory.createWebApplication()).
>                 in(Scopes.SINGLETON);
>         }
>
> You actually have to leave off the in(Scopes.SINGLETON) for this ...
> toInstance() returns void.  At any rate, this didn't change the behavior.
>
> Of course it needs to be initiated, which i presume you are doing.
> Feel free to send me a copy of your project and i can investigate as well.
>
> For correctly support for injection of UriInfo (etc.) for singletons we need
> to have the thread local proxy instance of that class so it can be reliably
> injected onto a singleton. An alternative is to inject Provider<UriInfo>
> then the existing approach should work.
>
> Isn't that basically what the @Provides uriInfo() method does?  I'll try it
> with an explicit provider class and see if that makes any difference.
>

Yes, that is what it does. I think what Paul was getting was rather
than have the UriInfo injected directly, inject it's provider and get
it when needed

   @Path("foo")
    public class MyResource {

        @Inject
        public class MyResource(final Provider<UriInfo> uriInfoProvider) {
            this.uriInfoProvider = uriInfoProvider;
        }

        private final Provider<UriInfo> uriInfoProvider;

        ...

    }

That way the resource will also work even if it is registered as a singleton.

But I'm not convinced that will solve your problem either. It sounds
more like the WebApplication instance that is being created by Guice
is not the one that is being used by Jersey. You said it's a Java SE
app and so doesn't use any of the servlet stuff. But however you are
kicking off Jersey, whether creating a GrizzlyContainer or something
else, you'll need to be sure it uses the WebApplication instance Guice
creates.

> Like Richard i too have not had any time to follow up on this.
> In Guice 2.0 it is not possible to support the following:
>     @Path("foo")
>     @Encoded
>     public class MyResource {
>
>         @Inject
>         public class MyResource(@DefaultValue("x") @QueryParam("x") String
> x) {
>             this.x = x;
>         }
>
>         private final String x;
>
>         ...
>
>     }
>
> Guice's approach to custom injections using a TypeListener has
> limitations because it means one cannot integrate cleanly with the parameters associated with @Inject on constructors, fields or methods.
> I would like to do something like:
>   MetaProvider mp = ...
>   bind(QueryParam.class).toMetaProvider(mp);
> Where a meta provider has access to the injection type, the annotations on
> the type, and the target class. The meta provider is responsible for
> creating a provider with the appropriate scope.
> I am very very tempted to modify Guice to support this concept.
> I don't think this violates anything specific to Guice's approach to
> ensuring all things are bound at module initialization (e.g. so one could
> output a dependency graph).  It does however, make things less explicit due
> to the integration of the meta provider. But i would not expect most
> developers to create meta providers and instead they would be created by the
> much smaller set of framework developers to be reused. But, i suspect the
> probability of this concept getting accepted by the Guice developers may
> well be close to zero...
>
> I suspect you might be right.
>
> I'm also going to play a little with GuiceyFruit and see if James got
> anywhere on this sort of thing.  But I suspect he's not focused on
> integration with frameworks.
>
> Paul.
>
> Craig
>
>
>
>
> On Aug 14, 2009, at 6:34 AM, Craig McClanahan wrote:
>
> 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
>
>
>
>
>
>