dev@jersey.java.net

Re: [Jersey] GuiceComponentProviderFactory seems to ignore Guice Scope for object bound in parent injector

From: Paul Sandoz <Paul.Sandoz_at_Sun.COM>
Date: Thu, 14 May 2009 10:16:44 +0200

On 05/13/09 11:25 PM, Jae Lee wrote:
> Cool,
>
> In your GuiceChildParentNoScopeSingletonTest, does ResourceContext
> represent a request scope?

No, the scope will be defined by the scope on the Guice component
obtained from the resource context.


(i.e. each subsequent request will get
> different instance of ResourceContext).
>

ResourceContext is the means to get instances of resources. It is a
singleton, one instance per application, thus the same for multiple
requests. It essentially does what you were mocking up.



> 1. testNoScope reads like for 'NO_SCOPE', you get new instance of
> resource every time requested in a given request.

Yes, specifically for NO_SCOPE you get a new instance every time you get
an instance. So within the scope of the HTTP request you will get one or
more instances for one or more calls to ResourceContext.

If you use the Guice request scope then it is guranteed than one
instance will be available within the scope of the HTTP request.


> 2. testSingleton reads like for 'SINGLETON', you get the same
> instance of resource every time requested in a given request.

Yes.

> 3. Can I assume that it will work the same way in other scenarios?
>
> In the same session, you will get new instance of resource for
> 'NO_SCOPE'.
> In a different request, you will get the same instance of resource
> for 'SINGLETON'
>

Yes. For session based scoped you will need to use the Guice servlet
support for sessions.

>
> It's been very helpful to get this resolved.
>

Great, glad to be of help,
Paul.

> Thanks Paul,
> J
>
> On Wed, May 13, 2009 at 4:24 PM, Paul Sandoz <Paul.Sandoz_at_sun.com
> <mailto:Paul.Sandoz_at_sun.com>> wrote:
>
> Hi,
>
> On May 13, 2009, at 1:44 AM, Jae Lee wrote:
>
>> Hi Paul,
>>
>> I made some attempts on writing some tests for the scenarios that
>> I described...
>>
>> Through some debugging the tests that I've written, I've learned a
>> bit more about Jersey, where for explicit Guice bound object it's
>> mapping
>>
>> 1. Scopes.SINGLETON ----> ComponentScope.Singleton
>> 2. Scopes.NO_SCOPE ----> ComponentScope.PerRequest
> Yes. Plus:
>
> com.google.inject.servlet.RequestScoped -> ComponentScope.PerRequest
>
> if using the ServletModule.
>
>
>> It was a bit of surprise that (if my test is written correct... I
>> need some confirmation on whether I'm building ResourceFactory in
>> the right way and also the way I'm modeling multiple Resource
>> instantiation in the same request and different requests is
>> reasonable)
>>
>> 1. for objects in child injector; Scopes.NO_SCOPE, it seems to
>> generate new instance in the same request
>
> Yes, because Jersey defers to Guice for instantiation. In this
> respect it just means that Jersey's Per request is compatible (i.e.
> wider) than Guices NO_SCOPE, so Jersey can inject per request resources.
>
>> 2. for objects in parent injector; Scopes.NO_SCOPE, it seems to
>> generate same instance in the same request
> I think this is because the bindings in the parent are not being
> traversed, so Jersey cannot find it in the child injector, and
> is deferring to Guice to instantiate an instance but Jersey is
> managing the life-cycle using the GuiceInstantiatedComponentProvider.
>
> Can you verify via the logs what component provider class is being
> used to manage BoundInParentInjector and BoundInChildInjector?
>
>> 3. for objects in child/parent injector; Scopes.SINGLETON, it
>> seems to generate same instance all the time
> For the parent object it should be the same as 2.
>
> For the child object that does make sense because it is a singleton.
>
>
> I am going to commit a fix, java.net <http://java.net> SVN is down
> so i cannot do it at the moment, for traversing parent injectors.
> The fix includes a test based on your code.
>
> BTW i cannot hierarchical injectors to work with the ServletModel as
> a child. The bindings in the servlet module are not picked up so i
> am guessing the Injector injected into the Jersey GuiceContainter is
> the parent, which might be a bug.
>
> Paul.
>>
>> 3.
>>
>>
>> Regards
>> J
>>
>> On Mon, May 11, 2009 at 1:49 PM, Paul Sandoz <Paul.Sandoz_at_sun.com
>> <mailto:Paul.Sandoz_at_sun.com>> wrote:
>>
>> Hi Jae,
>>
>> BTW i am still finding my away around with Guice and am by no
>> means an expert so providing use cases like this is very valuable.
>>
>> I notice that from here:
>>
>> http://code.google.com/p/google-guice/source/browse/trunk/src/com/google/inject/Injector.java
>>
>> /**
>> * Returns all explicit bindings.
>> *
>> * <p>The returned map does not include bindings inherited
>> from a {_at_link #getParent() parent
>> * injector}, should one exist. The returned map is
>> guaranteed to iterate (for example, with
>> * its {_at_link java.util.Map#entrySet()} iterator) in the
>> order of insertion. In other words,
>> * the order in which bindings appear in user Modules.
>> *
>> * <p>This method is part of the Guice SPI and is intended
>> for use by tools and extensions.
>> */
>> Map<Key<?>, Binding<?>> getBindings();
>>
>>
>> So the Injector.getBindings does not return bindings from the
>> parent injector (if any). It was not the intention to ignore
>> bindings of the parent.
>>
>> So IIUC the fix would be to traverse up the injector
>> hierarchy, to find the injector that contains an explicit
>> binding for the class:
>>
>> private Injector findInjector(Key<?> key) {
>> Injector i = injector;
>> while (i != null) {
>> if (i.getBindings().containsKey(key))
>> return i;
>>
>> i = i.getParent();
>> }
>> return null;
>> }
>>
>> public IoCComponentProvider
>> getComponentProvider(ComponentContext cc, Class clazz) {
>> if (LOGGER.isLoggable(Level.FINE)) {
>> LOGGER.fine("getComponentProvider(" +
>> clazz.getName() + ")");
>> }
>>
>> Key<?> key = Key.get(clazz);
>> Injector i = findInjector(key);
>> // If there is no explicit binding
>> if (i == null) {
>> // If an @Inject is explicitly declared
>> if (!isInjectPresent(clazz)) {
>> return null;
>> }
>> ...
>>
>>
>> Would that work for you?
>>
>> Would it be possible to provide a simple test case that
>> represents your use-case and i can add that as a unit test.
>>
>> Paul.
>>
>>
>> On May 10, 2009, at 2:23 PM, Jae Lee wrote:
>>
>>> Hi all,
>>>
>>> I've got a newbie question really...
>>>
>>> From brief look through GuiceComponentProviderFactory source
>>> code (1.1.0.ea), it looks like if you built
>>> GuiceComponentProviderFactory with an Injector Guice Scope is
>>> ignored for an object explicitly bound by its parent
>>> Injector... is that an intended behaviour?
>>>
>>> In my current project, we've got an Application and many
>>> Components underneath. Component being loosely related
>>> resources, defines an independent context. It is achieved by
>>> having a child Injector for each Component under an Application.
>>>
>>> Naturally we would have common class/object bound in Injector
>>> in Application (a bit like application scope) and component
>>> specific class/object bound in child Injector in Component (a
>>> bit like component scope)
>>>
>>> It looks like GuiceComponentProviderFactory uses
>>> GuiceInstantiatedComponentProvider which ignores Guice Scope,
>>> for any explicitly bound class in parent Injector. Which is
>>> bit of problem when it comes to Singleton scoped class.
>>>
>>> Injector.getBinding(key) does return binding from parent
>>> Injector if possible then fall back to Just In Time binding
>>> with Scopes.NO_SCOPE.
>>>
>>> Given that even for Just In Time binding, there's a scope
>>> (NO_SCOPE) that can be mapped to Jersey, is it possible to
>>> use GuiceManagedComponentProvider all the time? Would that
>>> makes it functionally different from current implementation?
>>> Benefit would be that then it can support Singleton Guice
>>> object in parent Injector properly.
>>>
>>> regards
>>> J
>>
>>
>> <GuiceComponentProviderFactoryTest.java>---------------------------------------------------------------------
>> To unsubscribe, e-mail: dev-unsubscribe_at_jersey.dev.java.net
>> <mailto:dev-unsubscribe_at_jersey.dev.java.net>
>> For additional commands, e-mail: dev-help_at_jersey.dev.java.net
>> <mailto:dev-help_at_jersey.dev.java.net>
>
>