xiaodududu / google-guice

Automatically exported from code.google.com/p/google-guice
Apache License 2.0
0 stars 0 forks source link

ComputationException due to StackOverflowError during injection is hard to track down #681

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
When using Guice on Android, it's relatively easy to construct dependency 
chains that overflow the available stack.  In my testing I was able to do it 
with a chain of length seven on the Android 2.3 emulator (with similar results 
on Android 2.2 devices as well).

When this happens, it can be extraordinarily difficult to fix the problem 
because the error message does not include sufficient information to know what 
Guice was attempting to construct at the time.  For example, see the following 
for a typical example:

11-01 14:28:30.555 E/AndroidRuntime( 4325): Caused by: 
com.google.inject.internal.util.$ComputationException: 
com.google.inject.internal.util.$ComputationException: 
com.google.inject.internal.util.$ComputationException: 
com.google.inject.internal.util.$ComputationException: 
com.google.inject.internal.util.$ComputationException: 
com.google.inject.internal.util.$ComputationException: 
com.google.inject.internal.util.$ComputationException: 
com.google.inject.internal.util.$ComputationException: 
com.google.inject.internal.util.$ComputationException: 
com.google.inject.internal.util.$ComputationException: 
com.google.inject.internal.util.$ComputationException: 
com.google.inject.internal.util.$ComputationException: 
java.lang.StackOverflowError
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.internal.util.$MapMaker$StrategyImpl.compute(MapMaker.java:553
)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.internal.util.$MapMaker$StrategyImpl.compute(MapMaker.java:419
)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.internal.util.$CustomConcurrentHashMap$ComputingImpl.get(Custo
mConcurrentHashMap.java:2041)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.internal.FailableCache.get(FailableCache.java:50)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.internal.ConstructorInjectorStore.get(ConstructorInjectorStore
.java:49)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.internal.ConstructorBindingImpl.initialize(ConstructorBindingI
mpl.java:125)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.internal.InjectorImpl.initializeJitBinding(InjectorImpl.java:5
21)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.internal.InjectorImpl.createJustInTimeBinding(InjectorImpl.jav
a:847)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.internal.InjectorImpl.createJustInTimeBindingRecursive(Injecto
rImpl.java:772)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.internal.InjectorImpl.getJustInTimeBinding(InjectorImpl.java:2
56)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.internal.InjectorImpl.getBindingOrThrow(InjectorImpl.java:205)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.internal.InjectorImpl.getInternalFactory(InjectorImpl.java:853
)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.internal.BoundProviderFactory.notify(BoundProviderFactory.java
:44)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.internal.ProcessedBindingData.runCreationListeners(ProcessedBi
ndingData.java:50)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.internal.InternalInjectorCreator.initializeStatically(Internal
InjectorCreator.java:133)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.internal.InternalInjectorCreator.build(InternalInjectorCreator
.java:106)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.Guice.createInjector(Guice.java:95)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.google.inject.Guice.createInjector(Guice.java:83)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
roboguice.RoboGuice.setBaseApplicationInjector(RoboGuice.java:92)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.groupon.GrouponApplication.setGrouponApplicationInjector(GrouponApplication.
java:179)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at 
com.groupon.GrouponApplication.onCreate(GrouponApplication.java:44)
11-01 14:28:30.555 E/AndroidRuntime( 4325):     at android.app.Instrumentation.

We can see that there are 12 ComputationExceptions, but we can't see what type 
was being computed at the time.

We can use the debugger (ala 
http://code.google.com/p/roboguice/wiki/ComputationExceptionStackOverflow ) to 
figure out the types, but this process would be significantly easier if the 
ComputationException message included the type key that was being generated.

Original issue reported on code.google.com by mbur...@gmail.com on 29 Jan 2012 at 6:46

GoogleCodeExporter commented 9 years ago
There's unfortunately no way we can insert information into the error messages, 
because the exceptions are all outside of Guice's control.  (They're coming 
from the JVM & Guava, not Guice.  The classes just appear inside Guice's 
packages because we repackage Guava at build time.)

However, you can get more information in Guice 4.0 by using a ProvisionListener.

In one of your modules, call:
   bindListener(Matchers.any(), new ProvisionListener() {
       public void onProvision(ProvisionInvocation provision) {
         System.out.println("Provisioning: " + provision.getBinding());
       }
    });

... that will add some more overhead to the object creation graph, so you won't 
be able to recursively create as many objects as you would without it, but it 
will give you some insight into what's going on, at least.

Original comment by sberlin on 10 Oct 2013 at 2:06