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: Wed, 13 May 2009 17:24:25 +0200

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
> Scopes.SINGLETON ----> ComponentScope.Singleton
> 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)
> 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.

> 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?

> 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 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.
>
> Regards
> J
>
> On Mon, May 11, 2009 at 1:49 PM, Paul Sandoz <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
> For additional commands, e-mail: dev-help_at_jersey.dev.java.net