Crazy ChildInjector-related Exception

174 views
Skip to first unread message

Sam Berlin

unread,
Jun 11, 2009, 6:46:16 PM6/11/09
to google...@googlegroups.com
Hi All,

I have tried debugging this for a few hours, and for the life of me I can't figure out what's going wrong.  I know how to workaround it, but I don't understand why its happening, which has me scared that it may happen in other places.

The scenario is:  We create a parent injector for the core and a child injector for the UI.  The child injector has an odd set of injectable objects that have the following dependencies:  A requires B requires C requires D requires Provider<A>.  All of them are singletons.  D does not call Provider<A>.get() in the constructor.  If I change the child injector's module to have an explicit bind of A, B, C or D then the problem goes away.  I don't understand why that's necessary though -- D should be visible to child injectors even it was a jit-binding added to the parent (through use in a child).  Neither A, B or C are required by anything else.  D is required by one other class (E), but if I explicitly bind E the problem still exists.

I'm very willing to debug more... but I could use some guidance on where to look.  I've tried reproducing the problem in a smaller testcase that plays this exact scenario out, but it won't reproduce (even though this happens 100% of the time on our code).

This is the particular exception:

com.google.inject.CreationException: Guice creation errors:

1) Error in custom provider, java.lang.IllegalStateException: Constructor not ready
  while locating org.limewire.ui.swing.friends.chat.ChatFramePanel
    for parameter 0 at org.limewire.ui.swing.friends.chat.ChatModel.<init>(ChatModel.java:45)
  while locating org.limewire.ui.swing.friends.chat.ChatModel
    for parameter 1 at org.limewire.ui.swing.friends.chat.ChatFriendListPane.<init>(ChatFriendListPane.java:122)
  while locating org.limewire.ui.swing.friends.chat.ChatFriendListPane
Caused by: java.lang.IllegalStateException: Constructor not ready
    at com.google.inject.internal.Preconditions.checkState(Preconditions.java:142)
    at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:123)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:43)
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:808)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40)
    at com.google.inject.Scopes$1$1.get(Scopes.java:51)
    at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:44)
    at com.google.inject.internal.SingleParameterInjector.inject(SingleParameterInjector.java:38)
    at com.google.inject.internal.SingleParameterInjector.getAll(SingleParameterInjector.java:62)
    at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:79)
    at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:127)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:43)
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:808)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40)
    at com.google.inject.Scopes$1$1.get(Scopes.java:51)
    at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:44)
    at com.google.inject.internal.SingleParameterInjector.inject(SingleParameterInjector.java:38)
    at com.google.inject.internal.SingleParameterInjector.getAll(SingleParameterInjector.java:62)
    at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:79)
    at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:127)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:43)
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:808)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40)
    at com.google.inject.Scopes$1$1.get(Scopes.java:51)
    at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:44)
    at com.google.inject.internal.InjectorBuilder$1.call(InjectorBuilder.java:201)
    at com.google.inject.internal.InjectorBuilder$1.call(InjectorBuilder.java:197)
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:801)
    at com.google.inject.internal.InjectorBuilder.loadEagerSingletons(InjectorBuilder.java:195)
    at com.google.inject.internal.InjectorBuilder.injectDynamically(InjectorBuilder.java:177)
    at com.google.inject.internal.InjectorBuilder.build(InjectorBuilder.java:114)
    at com.google.inject.internal.InjectorImpl.createChildInjector(InjectorImpl.java:137)
    at com.google.inject.internal.InjectorImpl.createChildInjector(InjectorImpl.java:144)
    at org.limewire.ui.swing.mainframe.AppFrame.createUiInjector(AppFrame.java:312)
    at org.limewire.ui.swing.mainframe.AppFrame.startup(AppFrame.java:156)
    at org.jdesktop.application.Application$1.run(Application.java:171)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

2) Error in custom provider, java.lang.IllegalStateException: Constructor not ready
  while locating org.limewire.ui.swing.friends.chat.ChatFramePanel
    for parameter 0 at org.limewire.ui.swing.friends.chat.ChatModel.<init>(ChatModel.java:45)
  while locating org.limewire.ui.swing.friends.chat.ChatModel
    for parameter 1 at org.limewire.ui.swing.friends.chat.ChatFriendListPane.<init>(ChatFriendListPane.java:122)
  while locating org.limewire.ui.swing.friends.chat.ChatFriendListPane
    for parameter 1 at org.limewire.ui.swing.friends.chat.ChatPanel.<init>(ChatPanel.java:51)
  while locating org.limewire.ui.swing.friends.chat.ChatPanel
Caused by: java.lang.IllegalStateException: Constructor not ready
    at com.google.inject.internal.Preconditions.checkState(Preconditions.java:142)
    at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:123)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:43)
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:808)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40)
    at com.google.inject.Scopes$1$1.get(Scopes.java:51)
    at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:44)
    at com.google.inject.internal.SingleParameterInjector.inject(SingleParameterInjector.java:38)
    at com.google.inject.internal.SingleParameterInjector.getAll(SingleParameterInjector.java:62)
    at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:79)
    at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:127)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:43)
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:808)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40)
    at com.google.inject.Scopes$1$1.get(Scopes.java:51)
    at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:44)
    at com.google.inject.internal.SingleParameterInjector.inject(SingleParameterInjector.java:38)
    at com.google.inject.internal.SingleParameterInjector.getAll(SingleParameterInjector.java:62)
    at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:79)
    at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:127)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:43)
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:808)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40)
    at com.google.inject.Scopes$1$1.get(Scopes.java:51)
    at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:44)
    at com.google.inject.internal.SingleParameterInjector.inject(SingleParameterInjector.java:38)
    at com.google.inject.internal.SingleParameterInjector.getAll(SingleParameterInjector.java:62)
    at com.google.inject.internal.ConstructorInjector.construct(ConstructorInjector.java:79)
    at com.google.inject.internal.ConstructorBindingImpl$Factory.get(ConstructorBindingImpl.java:127)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter$1.call(ProviderToInternalFactoryAdapter.java:43)
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:808)
    at com.google.inject.internal.ProviderToInternalFactoryAdapter.get(ProviderToInternalFactoryAdapter.java:40)
    at com.google.inject.Scopes$1$1.get(Scopes.java:51)
    at com.google.inject.internal.InternalFactoryToProviderAdapter.get(InternalFactoryToProviderAdapter.java:44)
    at com.google.inject.internal.InjectorBuilder$1.call(InjectorBuilder.java:201)
    at com.google.inject.internal.InjectorBuilder$1.call(InjectorBuilder.java:197)
    at com.google.inject.internal.InjectorImpl.callInContext(InjectorImpl.java:801)
    at com.google.inject.internal.InjectorBuilder.loadEagerSingletons(InjectorBuilder.java:195)
    at com.google.inject.internal.InjectorBuilder.injectDynamically(InjectorBuilder.java:177)
    at com.google.inject.internal.InjectorBuilder.build(InjectorBuilder.java:114)
    at com.google.inject.internal.InjectorImpl.createChildInjector(InjectorImpl.java:137)
    at com.google.inject.internal.InjectorImpl.createChildInjector(InjectorImpl.java:144)
    at org.limewire.ui.swing.mainframe.AppFrame.createUiInjector(AppFrame.java:312)
    at org.limewire.ui.swing.mainframe.AppFrame.startup(AppFrame.java:156)
    at org.jdesktop.application.Application$1.run(Application.java:171)
    at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
    at java.awt.EventQueue.dispatchEvent(EventQueue.java:597)
    at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
    at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
    at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
    at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
    at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)

2 errors
    at com.google.inject.internal.Errors.throwCreationExceptionIfErrorsExist(Errors.java:358)
    at com.google.inject.internal.InjectorBuilder.injectDynamically(InjectorBuilder.java:180)
    at com.google.inject.internal.InjectorBuilder.build(InjectorBuilder.java:114)
    at com.google.inject.internal.InjectorImpl.createChildInjector(InjectorImpl.java:137)
    at com.google.inject.internal.InjectorImpl.createChildInjector(InjectorImpl.java:144)
    at org.limewire.ui.swing.mainframe.AppFrame.createUiInjector(AppFrame.java:312)
    at org.limewire.ui.swing.mainframe.AppFrame.startup(AppFrame.java:156)
    at org.jdesktop.application.Application$1.run(Application.java:171)
    ... 8 more

limpb...@gmail.com

unread,
Jun 11, 2009, 10:33:53 PM6/11/09
to google-guice
I'm impressed! We have an open bug for this (issue 319) but I haven't
given it much attention because you have to have all of the moving
parts in the exact wrong configuration to cause the problem. The
immediate fix is to just use explicit bindings in the child injector.

-- JIT bindings and circular dependencies --

For your entertainment, here's what's happening behind the scenes.
Suppose we have these classes:
class Foo {
@Inject Provider<Bar> bar;
@Inject Provider<Baz> baz;
}
class Bar {
@Inject Provider<Foo> foo;
}
interface Baz {}

1. You request a just-in-time (JIT) binding Foo from an Injector.
Here's a picture of our Injector:
Injector: bindings={Stage, Injector}, jitbindings={}

2. While constructing the binding for Foo, Guice eagerly adds the
partial binding for Foo to its collection of JIT bindings. That way,
Foo's dependencies can circularly depend on Foo. Later on, if the Foo
binding cannot be completed (due to an error), it will be removed from
the partial set.
Injector: bindings={Stage, Injector}, jitbindings={Foo}

3. While building Foo, there's some dependency Bar that circularly
depends on Foo. Bar's binding gets added to the JIT bindings
collection, using the partial Foo binding as a dependency.
Injector: bindings={Stage, Injector}, jitbindings={Foo, Bar}

4. Still binding Foo, we find some other dependency Baz that cannot be
resolved. So the Bar binding fails, and therefore the Foo binding
fails. We remove both from the JIT bindings collection. But it's too
late! The Bar binding is already in the collection, and it already
depends on Foo. So we've accidentally polluted the JIT bindings
collection.
Injector: bindings={Stage, Injector}, jitbindings={Bar}

-- JIT bindings and child injectors --

Without child injectors, this binding pollution problem exists, but it
is not a problem in practice. That's because although the jitBindings
collection can get polluted, whenever it is polluted there will also
be a big nasty ConfigurationException to accompany it. Something to
this effect:
"Guice configuration errors:
Cannot create binding for Baz
while resolving Foo.baz"
And so this problem has existed forever, but nobody has really paid
any attention to it.

-- JIT bindings and child injectors and circular dependencies --

Now that we have child injectors, the problem has become more
treacherous. Whenever you ask a child injector for a binding, it
follows this algorithm:
1. Is there any explicit binding? If so, return it.
2. First, ask the parent injector to create a JIT binding. If that
succeeds, return it. Otherwise ignore any errors.
3. Attempt to create the binding in the child injector.

When we combine these two reasonable behaviours, we silently pollute
the parent's JIT bindings. And so you end up with a half-baked binding
that blows up with an IllegalStateException when it's used.

So I suppose I'd best fix the pollution issue, which will fix your
problem. Sorry for the pain; hopefully you're sympathetic to the
complexities involved here!

Cheers,
Jesse

Sam Berlin

unread,
Jun 12, 2009, 12:07:21 AM6/12/09
to google...@googlegroups.com
On Thu, Jun 11, 2009 at 10:33 PM, je...@swank.ca <limpb...@gmail.com> wrote:
So I suppose I'd best fix the pollution issue, which will fix your
problem. Sorry for the pain; hopefully you're sympathetic to the
complexities involved here!

I knew it had something to do with getting caught up in the parent injector (because I saw two different bindings being created), but had no idea what the actual issue is.  I'm still confused why the testcase with the same scenario didn't fail... but will wait to see how this issue resolves and will retry.  I even tried using a PrivateModule for the child injector, but that failed with other errors (it still put some JIT bindings in the parent injector, and failed when the child injector tried to recreate those bindings).

Thanks for taking a renewed look at this!  If I can steal your attention for one more minute... take a look at http://code.google.com/p/google-guice/issues/detail?id=387 ?  It's a patch to add binding listeners, which resolves a bunch of different issues relating to JIT eager singletons (in parent or child injectors), cleans up the hacky solution for issue 373, and is useful in general!

Sam


Reply all
Reply to author
Forward
0 new messages