Revision: 1553
Author: sberlin
Date: Sun Jun 12 14:54:43 2011
Log: issue 631 -- expose dependency hierarchy in ProvisionListener.
http://code.google.com/p/google-guice/source/detail?r=1553
Added:
/trunk/core/src/com/google/inject/spi/DependencyAndSource.java
Modified:
/trunk/core/src/com/google/inject/internal/BindingProcessor.java
/trunk/core/src/com/google/inject/internal/BoundProviderFactory.java
/trunk/core/src/com/google/inject/internal/ConstructorInjector.java
/trunk/core/src/com/google/inject/internal/FactoryProxy.java
/trunk/core/src/com/google/inject/internal/Initializer.java
/trunk/core/src/com/google/inject/internal/InjectionRequestProcessor.java
/trunk/core/src/com/google/inject/internal/InjectorImpl.java
/trunk/core/src/com/google/inject/internal/InternalContext.java
/trunk/core/src/com/google/inject/internal/InternalFactoryToProviderAdapter.java
/trunk/core/src/com/google/inject/internal/InternalInjectorCreator.java
/trunk/core/src/com/google/inject/internal/MembersInjectorImpl.java
/trunk/core/src/com/google/inject/internal/ProvidedByInternalFactory.java
/trunk/core/src/com/google/inject/internal/ProviderInternalFactory.java
/trunk/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java
/trunk/core/src/com/google/inject/internal/SingleFieldInjector.java
/trunk/core/src/com/google/inject/internal/SingleParameterInjector.java
/trunk/core/src/com/google/inject/spi/ProvisionListener.java
/trunk/core/test/com/google/inject/ProvisionListenerTest.java
=======================================
--- /dev/null
+++ /trunk/core/src/com/google/inject/spi/DependencyAndSource.java Sun Jun
12 14:54:43 2011
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2011 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may
not
+ * use this file except in compliance with the License. You may obtain a
copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
under
+ * the License.
+ */
+
+package com.google.inject.spi;
+
+import java.lang.reflect.Member;
+
+import com.google.inject.Binding;
+import com.google.inject.internal.util.StackTraceElements;
+
+/**
+ * A combination of a {@link Dependency} and the {@link Binding#getSource()
+ * source} where the dependency was bound.
+ *
+ * @author [email protected] (Sam Berlin)
+ */
+public final class DependencyAndSource {
+ private final Dependency<?> dependency;
+ private final Object source;
+
+ public DependencyAndSource(Dependency<?> dependency, Object source) {
+ this.dependency = dependency;
+ this.source = source;
+ }
+
+ /**
+ * Returns the Dependency, if one exists. For anything that can be
referenced
+ * by {@link Injector#getBinding}, a dependency exists. A dependency
will not
+ * exist (and this will return null) for types initialized with
+ * {@link Binder#requestInjection} or {@link
Injector#injectMembers(Object),
+ * nor will it exist for objects injected into Providers bound with
+ * LinkedBindingBuilder#toProvider(Provider).
+ */
+ public Dependency<?> getDependency() {
+ return dependency;
+ }
+
+ /**
+ * Returns a string describing where this dependency was bound. If the
binding
+ * was just-in-time, there is no valid binding source, so this describes
the
+ * class in question.
+ */
+ public String getBindingSource() {
+ if (source instanceof Class) {
+ return StackTraceElements.forType((Class) source).toString();
+ } else if (source instanceof Member) {
+ return StackTraceElements.forMember((Member) source).toString();
+ } else {
+ return source.toString();
+ }
+ }
+
+ @Override
+ public String toString() {
+ Dependency<?> dep = getDependency();
+ Object source = getBindingSource();
+ if (dep != null) {
+ return "Dependency: " + dep + ", source: " + source;
+ } else {
+ return "Source: " + source;
+ }
+ }
+}
=======================================
--- /trunk/core/src/com/google/inject/internal/BindingProcessor.java Sun
Jun 5 11:32:05 2011
+++ /trunk/core/src/com/google/inject/internal/BindingProcessor.java Sun
Jun 12 14:54:43 2011
@@ -85,7 +85,7 @@
Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
T instance = binding.getInstance();
Initializable<T> ref = initializer.requestInjection(
- injector, instance, source, injectionPoints);
+ injector, instance, key, source, injectionPoints);
ConstantFactory<? extends T> factory = new ConstantFactory<T>(ref);
InternalFactory<? extends T> scopedFactory
= Scoping.scope(key, injector, factory, source, scoping);
@@ -99,7 +99,7 @@
Provider<? extends T> provider = binding.getProviderInstance();
Set<InjectionPoint> injectionPoints = binding.getInjectionPoints();
Initializable<Provider<? extends T>> initializable = initializer
- .<Provider<? extends T>>requestInjection(injector, provider,
source, injectionPoints);
+ .<Provider<? extends T>>requestInjection(injector, provider,
null, source, injectionPoints);
InternalFactory<T> factory = new
InternalFactoryToInitializableAdapter<T>(
initializable,
source, !injector.options.disableCircularProxies,
injector.provisionListenerStore.get(key));
=======================================
--- /trunk/core/src/com/google/inject/internal/BoundProviderFactory.java
Sun Jun 5 11:32:05 2011
+++ /trunk/core/src/com/google/inject/internal/BoundProviderFactory.java
Sun Jun 12 14:54:43 2011
@@ -52,9 +52,14 @@
public T get(Errors errors, InternalContext context, Dependency<?>
dependency, boolean linked)
throws ErrorsException {
- errors = errors.withSource(providerKey);
- javax.inject.Provider<? extends T> provider =
providerFactory.get(errors, context, dependency, true);
- return circularGet(provider, errors, context, dependency, linked);
+ context.pushState(providerKey, source);
+ try {
+ errors = errors.withSource(providerKey);
+ javax.inject.Provider<? extends T> provider =
providerFactory.get(errors, context, dependency, true);
+ return circularGet(provider, errors, context, dependency, linked);
+ } finally {
+ context.popState();
+ }
}
@Override
=======================================
--- /trunk/core/src/com/google/inject/internal/ConstructorInjector.java Sun
Jun 5 11:32:05 2011
+++ /trunk/core/src/com/google/inject/internal/ConstructorInjector.java Sun
Jun 12 14:54:43 2011
@@ -85,7 +85,7 @@
if (!provisionCallback.hasListeners()) {
return provision(errors, context, constructionContext);
} else {
- return provisionCallback.provision(errors, new
ProvisionCallback<T>() {
+ return provisionCallback.provision(errors, context, new
ProvisionCallback<T>() {
public T call() throws ErrorsException {
return provision(errors, context, constructionContext);
}
=======================================
--- /trunk/core/src/com/google/inject/internal/FactoryProxy.java Thu Mar 10
19:02:39 2011
+++ /trunk/core/src/com/google/inject/internal/FactoryProxy.java Sun Jun 12
14:54:43 2011
@@ -51,7 +51,12 @@
public T get(Errors errors, InternalContext context, Dependency<?>
dependency, boolean linked)
throws ErrorsException {
- return targetFactory.get(errors.withSource(targetKey), context,
dependency, true);
+ context.pushState(targetKey, source);
+ try {
+ return targetFactory.get(errors.withSource(targetKey), context,
dependency, true);
+ } finally {
+ context.popState();
+ }
}
@Override public String toString() {
=======================================
--- /trunk/core/src/com/google/inject/internal/Initializer.java Sat Jul 3
08:51:31 2010
+++ /trunk/core/src/com/google/inject/internal/Initializer.java Sun Jun 12
14:54:43 2011
@@ -16,6 +16,7 @@
package com.google.inject.internal;
+import com.google.inject.Key;
import com.google.inject.Stage;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.Lists;
@@ -48,10 +49,11 @@
*
* @param instance an instance that optionally has members to be
injected (each annotated with
* @Inject).
+ * @param key a key to use for keeping the state of the dependency chain
* @param source the source location that this injection was requested
*/
- <T> Initializable<T> requestInjection(InjectorImpl injector, T instance,
Object source,
- Set<InjectionPoint> injectionPoints) {
+ <T> Initializable<T> requestInjection(InjectorImpl injector, T instance,
Key<T> key,
+ Object source, Set<InjectionPoint> injectionPoints) {
checkNotNull(source);
// short circuit if the object has no injections
@@ -60,7 +62,7 @@
return Initializables.of(instance);
}
- InjectableReference<T> initializable = new
InjectableReference<T>(injector, instance, source);
+ InjectableReference<T> initializable = new
InjectableReference<T>(injector, instance, key, source);
pendingInjection.put(instance, initializable);
return initializable;
}
@@ -106,10 +108,12 @@
private final InjectorImpl injector;
private final T instance;
private final Object source;
+ private final Key<T> key;
private MembersInjectorImpl<T> membersInjector;
- public InjectableReference(InjectorImpl injector, T instance, Object
source) {
+ public InjectableReference(InjectorImpl injector, T instance, Key<T>
key, Object source) {
this.injector = injector;
+ this.key = key; // possibly null!
this.instance = checkNotNull(instance, "instance");
this.source = checkNotNull(source, "source");
}
@@ -144,7 +148,8 @@
if (pendingInjection.remove(instance) != null) {
// if in Stage.TOOL, we only want to inject & notify toolable
injection points.
// (otherwise we'll inject all of them)
- membersInjector.injectAndNotify(instance,
errors.withSource(source), injector.options.stage == Stage.TOOL);
+ membersInjector.injectAndNotify(instance,
errors.withSource(source), key, source,
+ injector.options.stage == Stage.TOOL);
}
return instance;
=======================================
---
/trunk/core/src/com/google/inject/internal/InjectionRequestProcessor.java
Sat Jul 3 08:51:31 2010
+++
/trunk/core/src/com/google/inject/internal/InjectionRequestProcessor.java
Sun Jun 12 14:54:43 2011
@@ -58,7 +58,7 @@
}
initializer.requestInjection(
- injector, request.getInstance(), request.getSource(),
injectionPoints);
+ injector, request.getInstance(), null, request.getSource(),
injectionPoints);
return true;
}
=======================================
--- /trunk/core/src/com/google/inject/internal/InjectorImpl.java Sun Jun 5
11:32:05 2011
+++ /trunk/core/src/com/google/inject/internal/InjectorImpl.java Sun Jun 12
14:54:43 2011
@@ -716,8 +716,13 @@
InternalFactory<T> internalFactory = new InternalFactory<T>() {
public T get(Errors errors, InternalContext context, Dependency<?>
dependency, boolean linked)
throws ErrorsException {
- return targetBinding.getInternalFactory().get(
- errors.withSource(targetKey), context, dependency, true);
+ context.pushState(targetKey, targetBinding.getSource());
+ try {
+ return targetBinding.getInternalFactory().get(
+ errors.withSource(targetKey), context, dependency, true);
+ } finally {
+ context.popState();
+ }
}
};
@@ -904,8 +909,8 @@
<T> SingleParameterInjector<T> createParameterInjector(final
Dependency<T> dependency,
final Errors errors) throws ErrorsException {
- InternalFactory<? extends T> factory =
getInternalFactory(dependency.getKey(), errors, JitLimitation.NO_JIT);
- return new SingleParameterInjector<T>(dependency, factory);
+ BindingImpl<? extends T> binding =
getBindingOrThrow(dependency.getKey(), errors, JitLimitation.NO_JIT);
+ return new SingleParameterInjector<T>(dependency, binding);
}
/** Invokes a method. */
@@ -947,9 +952,7 @@
}
<T> Provider<T> getProviderOrThrow(final Key<T> key, Errors errors)
throws ErrorsException {
-
-
- final InternalFactory<? extends T> factory = getInternalFactory(key,
errors, JitLimitation.NO_JIT);
+ final BindingImpl<? extends T> binding = getBindingOrThrow(key,
errors, JitLimitation.NO_JIT);
final Dependency<T> dependency = Dependency.get(key);
return new Provider<T>() {
@@ -958,11 +961,11 @@
try {
T t = callInContext(new ContextualCallable<T>() {
public T call(InternalContext context) throws ErrorsException {
- Dependency previous = context.setDependency(dependency);
+ Dependency previous = context.pushDependency(dependency,
binding.getSource());
try {
- return factory.get(errors, context, dependency, false);
+ return binding.getInternalFactory().get(errors, context,
dependency, false);
} finally {
- context.setDependency(previous);
+ context.popStateAndSetDependency(previous);
}
}
});
@@ -974,7 +977,7 @@
}
@Override public String toString() {
- return factory.toString();
+ return binding.getInternalFactory().toString();
}
};
}
=======================================
--- /trunk/core/src/com/google/inject/internal/InternalContext.java Thu Oct
21 21:13:25 2010
+++ /trunk/core/src/com/google/inject/internal/InternalContext.java Sun Jun
12 14:54:43 2011
@@ -16,8 +16,14 @@
package com.google.inject.internal;
+import com.google.inject.Key;
+import com.google.inject.internal.util.ImmutableList;
import com.google.inject.internal.util.Maps;
import com.google.inject.spi.Dependency;
+import com.google.inject.spi.DependencyAndSource;
+
+import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
/**
@@ -29,7 +35,10 @@
final class InternalContext {
private Map<Object, ConstructionContext<?>> constructionContexts =
Maps.newHashMap();
+ /** Keeps track of the type that is currently being requested for
injection. */
private Dependency dependency;
+ /** Keeps track of the hierarchy of types needed during injection. */
+ private LinkedList<DependencyAndSource> state = new
LinkedList<DependencyAndSource>();
@SuppressWarnings("unchecked")
public <T> ConstructionContext<T> getConstructionContext(Object key) {
@@ -46,9 +55,32 @@
return dependency;
}
- public Dependency setDependency(Dependency dependency) {
+ /** Sets the new current dependency & adds it to the state. */
+ public Dependency pushDependency(Dependency dependency, Object source) {
Dependency previous = this.dependency;
this.dependency = dependency;
+ state.addLast(new DependencyAndSource(dependency, source));
return previous;
}
-}
+
+ /** Pops the current state & sets the new dependency. */
+ public void popStateAndSetDependency(Dependency newDependency) {
+ popState();
+ this.dependency = newDependency;
+ }
+
+ /** Adds to the state without setting the dependency. */
+ public void pushState(Key<?> key, Object source) {
+ state.addLast(new DependencyAndSource(key == null ? null :
Dependency.get(key), source));
+ }
+
+ /** Pops from the state without setting a dependency. */
+ public void popState() {
+ state.removeLast();
+ }
+
+ /** Returns the current dependency chain (all the state). */
+ public List<DependencyAndSource> getDependencyChain() {
+ return ImmutableList.copyOf(state);
+ }
+}
=======================================
---
/trunk/core/src/com/google/inject/internal/InternalFactoryToProviderAdapter.java
Sat Apr 30 08:38:05 2011
+++
/trunk/core/src/com/google/inject/internal/InternalFactoryToProviderAdapter.java
Sun Jun 12 14:54:43 2011
@@ -35,6 +35,7 @@
public T get(Errors errors, InternalContext context, Dependency<?>
dependency, boolean linked)
throws ErrorsException {
+ // TODO(sameb): Does this need to push state into the context?
try {
return errors.checkForNull(provider.get(), source, dependency);
} catch (RuntimeException userException) {
=======================================
--- /trunk/core/src/com/google/inject/internal/InternalInjectorCreator.java
Thu Mar 10 19:02:39 2011
+++ /trunk/core/src/com/google/inject/internal/InternalInjectorCreator.java
Sun Jun 12 14:54:43 2011
@@ -198,14 +198,14 @@
injector.callInContext(new ContextualCallable<Void>() {
Dependency<?> dependency = Dependency.get(binding.getKey());
public Void call(InternalContext context) {
- Dependency previous = context.setDependency(dependency);
+ Dependency previous = context.pushDependency(dependency,
binding.getSource());
Errors errorsForBinding = errors.withSource(dependency);
try {
binding.getInternalFactory().get(errorsForBinding,
context, dependency, false);
} catch (ErrorsException e) {
errorsForBinding.merge(e.getErrors());
} finally {
- context.setDependency(previous);
+ context.popStateAndSetDependency(previous);
}
return null;
=======================================
--- /trunk/core/src/com/google/inject/internal/MembersInjectorImpl.java Sat
Jul 3 08:51:31 2010
+++ /trunk/core/src/com/google/inject/internal/MembersInjectorImpl.java Sun
Jun 12 14:54:43 2011
@@ -16,6 +16,7 @@
package com.google.inject.internal;
+import com.google.inject.Key;
import com.google.inject.MembersInjector;
import com.google.inject.TypeLiteral;
import com.google.inject.internal.util.ImmutableList;
@@ -57,7 +58,7 @@
public void injectMembers(T instance) {
Errors errors = new Errors(typeLiteral);
try {
- injectAndNotify(instance, errors, false);
+ injectAndNotify(instance, errors, null, typeLiteral, false);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
@@ -65,14 +66,21 @@
errors.throwProvisionExceptionIfErrorsExist();
}
- void injectAndNotify(final T instance, final Errors errors, final
boolean toolableOnly) throws ErrorsException {
+ void injectAndNotify(final T instance, final Errors errors,
+ final Key<T> key, final Object source, final boolean toolableOnly)
+ throws ErrorsException {
if (instance == null) {
return;
}
injector.callInContext(new ContextualCallable<Void>() {
public Void call(InternalContext context) throws ErrorsException {
- injectMembers(instance, errors, context, toolableOnly);
+ context.pushState(key, source);
+ try {
+ injectMembers(instance, errors, context, toolableOnly);
+ } finally {
+ context.popState();
+ }
return null;
}
});
=======================================
---
/trunk/core/src/com/google/inject/internal/ProvidedByInternalFactory.java
Sun Jun 5 11:32:05 2011
+++
/trunk/core/src/com/google/inject/internal/ProvidedByInternalFactory.java
Sun Jun 12 14:54:43 2011
@@ -60,10 +60,15 @@
throws ErrorsException {
checkState(providerBinding != null, "not initialized");
- errors = errors.withSource(providerKey);
- Provider provider = providerBinding.getInternalFactory().get(
- errors, context, dependency, true);
- return circularGet(provider, errors, context, dependency, linked);
+ context.pushState(providerKey, providerBinding.getSource());
+ try {
+ errors = errors.withSource(providerKey);
+ Provider provider = providerBinding.getInternalFactory().get(
+ errors, context, dependency, true);
+ return circularGet(provider, errors, context, dependency, linked);
+ } finally {
+ context.popState();
+ }
}
protected T provision(javax.inject.Provider<? extends T> provider,
Errors errors,
=======================================
--- /trunk/core/src/com/google/inject/internal/ProviderInternalFactory.java
Sun Jun 5 11:32:05 2011
+++ /trunk/core/src/com/google/inject/internal/ProviderInternalFactory.java
Sun Jun 12 14:54:43 2011
@@ -65,7 +65,7 @@
if (!provisionCallback.hasListeners()) {
return provision(provider, errors, dependency, constructionContext);
} else {
- return provisionCallback.provision(errors, new
ProvisionCallback<T>() {
+ return provisionCallback.provision(errors, context, new
ProvisionCallback<T>() {
public T call() throws ErrorsException {
return provision(provider, errors, dependency,
constructionContext);
}
=======================================
---
/trunk/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java
Sun Jun 5 11:32:05 2011
+++
/trunk/core/src/com/google/inject/internal/ProvisionListenerStackCallback.java
Sun Jun 12 14:54:43 2011
@@ -21,6 +21,7 @@
import com.google.inject.Key;
import com.google.inject.ProvisionException;
import com.google.inject.spi.ProvisionListener;
+import com.google.inject.spi.DependencyAndSource;
/**
* Intercepts provisions with a stack of listeners.
@@ -46,8 +47,9 @@
return listeners.length > 0;
}
- public T provision(Errors errors, ProvisionCallback<T> callable) throws
ErrorsException {
- Provision provision = new Provision(errors, callable);
+ public T provision(Errors errors, InternalContext context,
ProvisionCallback<T> callable)
+ throws ErrorsException {
+ Provision provision = new Provision(errors, context, callable);
RuntimeException caught = null;
try {
provision.provision();
@@ -77,14 +79,16 @@
private class Provision extends ProvisionListener.ProvisionInvocation<T>
{
final Errors errors;
+ final InternalContext context;
final ProvisionCallback<T> callable;
int index = -1;
T result;
ErrorsException exceptionDuringProvision;
ProvisionListener erredListener;
- public Provision(Errors errors, ProvisionCallback<T> callable) {
+ public Provision(Errors errors, InternalContext context,
ProvisionCallback<T> callable) {
this.callable = callable;
+ this.context = context;
this.errors = errors;
}
@@ -120,5 +124,10 @@
public Key<T> getKey() {
return key;
}
+
+ @Override
+ public List<DependencyAndSource> getDependencyChain() {
+ return context.getDependencyChain();
+ }
}
}
=======================================
--- /trunk/core/src/com/google/inject/internal/SingleFieldInjector.java Thu
Feb 11 14:07:07 2010
+++ /trunk/core/src/com/google/inject/internal/SingleFieldInjector.java Sun
Jun 12 14:54:43 2011
@@ -28,7 +28,7 @@
final Field field;
final InjectionPoint injectionPoint;
final Dependency<?> dependency;
- final InternalFactory<?> factory;
+ final BindingImpl<?> binding;
public SingleFieldInjector(InjectorImpl injector, InjectionPoint
injectionPoint, Errors errors)
throws ErrorsException {
@@ -38,7 +38,7 @@
// Ewwwww...
field.setAccessible(true);
- factory = injector.getInternalFactory(dependency.getKey(), errors,
JitLimitation.NO_JIT);
+ binding = injector.getBindingOrThrow(dependency.getKey(), errors,
JitLimitation.NO_JIT);
}
public InjectionPoint getInjectionPoint() {
@@ -48,16 +48,16 @@
public void inject(Errors errors, InternalContext context, Object o) {
errors = errors.withSource(dependency);
- Dependency previous = context.setDependency(dependency);
+ Dependency previous = context.pushDependency(dependency,
binding.getSource());
try {
- Object value = factory.get(errors, context, dependency, false);
+ Object value = binding.getInternalFactory().get(errors, context,
dependency, false);
field.set(o, value);
} catch (ErrorsException e) {
errors.withSource(injectionPoint).merge(e.getErrors());
} catch (IllegalAccessException e) {
throw new AssertionError(e); // a security manager is blocking us,
we're hosed
} finally {
- context.setDependency(previous);
+ context.popStateAndSetDependency(previous);
}
}
}
=======================================
--- /trunk/core/src/com/google/inject/internal/SingleParameterInjector.java
Thu Feb 11 14:07:07 2010
+++ /trunk/core/src/com/google/inject/internal/SingleParameterInjector.java
Sun Jun 12 14:54:43 2011
@@ -25,19 +25,19 @@
private static final Object[] NO_ARGUMENTS = {};
private final Dependency<T> dependency;
- private final InternalFactory<? extends T> factory;
-
- SingleParameterInjector(Dependency<T> dependency, InternalFactory<?
extends T> factory) {
+ private final BindingImpl<? extends T> binding;
+
+ SingleParameterInjector(Dependency<T> dependency, BindingImpl<? extends
T> binding) {
this.dependency = dependency;
- this.factory = factory;
+ this.binding = binding;
}
private T inject(Errors errors, InternalContext context) throws
ErrorsException {
- Dependency previous = context.setDependency(dependency);
+ Dependency previous = context.pushDependency(dependency,
binding.getSource());
try {
- return factory.get(errors.withSource(dependency), context,
dependency, false);
+ return
binding.getInternalFactory().get(errors.withSource(dependency), context,
dependency, false);
} finally {
- context.setDependency(previous);
+ context.popStateAndSetDependency(previous);
}
}
=======================================
--- /trunk/core/src/com/google/inject/spi/ProvisionListener.java Sun Jun 5
11:32:05 2011
+++ /trunk/core/src/com/google/inject/spi/ProvisionListener.java Sun Jun 12
14:54:43 2011
@@ -16,6 +16,8 @@
package com.google.inject.spi;
+import java.util.List;
+
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.Scope;
@@ -53,5 +55,8 @@
/** Performs the provision, returning the object provisioned. */
public abstract T provision();
+
+ /** Returns the dependency chain that led to this object being
provisioned. */
+ public abstract List<DependencyAndSource> getDependencyChain();
}
}
=======================================
--- /trunk/core/test/com/google/inject/ProvisionListenerTest.java Sun Jun
5 11:32:05 2011
+++ /trunk/core/test/com/google/inject/ProvisionListenerTest.java Sun Jun
12 14:54:43 2011
@@ -21,6 +21,7 @@
import static com.google.inject.name.Names.named;
import java.util.List;
+import java.util.concurrent.atomic.AtomicBoolean;
import junit.framework.TestCase;
@@ -29,6 +30,7 @@
import com.google.inject.matcher.Matcher;
import com.google.inject.matcher.Matchers;
import com.google.inject.name.Named;
+import com.google.inject.spi.DependencyAndSource;
import com.google.inject.spi.ProvisionListener;
/**
@@ -314,7 +316,7 @@
public <T> void onProvision(ProvisionInvocation<T> provision) {
keys.add(provision.getKey());
T provisioned = provision.provision();
- assertEquals(provision.getKey().getTypeLiteral().getRawType(),
provisioned.getClass());
+ assertEquals(provision.getKey().getRawType(),
provisioned.getClass());
}
List<Key> getAndClear() {
@@ -350,4 +352,179 @@
provision.provision();
}
}
-}
+
+ private static class ChainAsserter implements ProvisionListener {
+ private final List<Class<?>> provisionList;
+ private final List<Class<?>> expected;
+
+ public ChainAsserter(List<Class<?>> provisionList, Iterable<Class<?>>
expected) {
+ this.provisionList = provisionList;
+ this.expected = ImmutableList.copyOf(expected);
+ }
+
+ public <T> void onProvision(ProvisionInvocation<T> provision) {
+ List<Class<?>> actual = Lists.newArrayList();
+ for (DependencyAndSource dep : provision.getDependencyChain()) {
+ actual.add(dep.getDependency().getKey().getRawType());
+ }
+ assertEquals(expected, actual);
+ provisionList.add(provision.getKey().getRawType());
+ }
+ }
+
+ private static Matcher<Object> keyMatcher(Class<?> clazz) {
+ return Matchers.only(Key.get(clazz));
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testDependencyChain() {
+ final List<Class<?>> pList = Lists.newArrayList();
+ final List<Class<?>> totalList = Lists.newArrayList();
+ Injector injector = Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Instance.class).toInstance(new Instance());
+ bind(B.class).to(BImpl.class);
+ bind(D.class).toProvider(DP.class);
+
+ bindListener(Matchers.any(), new ProvisionListener() {
+ public <T> void onProvision(ProvisionInvocation<T> provision) {
+ totalList.add(provision.getKey().getRawType());
+ }
+ });
+
+ // Build up a list of asserters for our dependency chains.
+ ImmutableList.Builder<Class<?>> chain = ImmutableList.builder();
+
+ chain.add(Instance.class).add(A.class);
+ bindListener(keyMatcher(A.class), new ChainAsserter(pList,
chain.build()));
+
+ chain.add(B.class).add(BImpl.class);
+ bindListener(keyMatcher(BImpl.class), new ChainAsserter(pList,
chain.build()));
+
+ chain.add(C.class);
+ bindListener(keyMatcher(C.class), new ChainAsserter(pList,
chain.build()));
+
+ // the chain has D before DP even though DP is provisioned &
notified first
+ // because we do DP because of D, and need DP to provision D.
+ chain.add(D.class).add(DP.class);
+ bindListener(keyMatcher(D.class), new ChainAsserter(pList,
chain.build()));
+ bindListener(keyMatcher(DP.class), new ChainAsserter(pList,
chain.build()));
+
+ chain.add(E.class);
+ bindListener(keyMatcher(E.class), new ChainAsserter(pList,
chain.build()));
+
+ chain.add(F.class);
+ bindListener(keyMatcher(F.class), new ChainAsserter(pList,
chain.build()));
+ }
+ @Provides C c(D d) {
+ return new C() {};
+ }
+ });
+ Instance instance = injector.getInstance(Instance.class);
+ // make sure we're checking all of the chain asserters..
+ assertEquals(of(A.class, BImpl.class, C.class, DP.class, D.class,
E.class, F.class),
+ pList);
+ // and make sure that nothing else was notified that we didn't expect.
+ assertEquals(totalList, pList);
+ }
+
+ public void testModuleRequestInjection() {
+ final AtomicBoolean notified = new AtomicBoolean();
+ Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ requestInjection(new Object() {
+ @Inject Foo foo;
+ });
+ bindListener(Matchers.any(),
+ new SpecialChecker(Foo.class, getClass().getName()
+ ".configure(", notified));
+ }
+ });
+ assertTrue(notified.get());
+ }
+
+ public void testToProviderInstance() {
+ final AtomicBoolean notified = new AtomicBoolean();
+ Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bind(Object.class).toProvider(new Provider<Object>() {
+ @Inject Foo foo;
+ public Object get() {
+ return null;
+ }
+ });
+ bindListener(Matchers.any(),
+ new SpecialChecker(Foo.class, getClass().getName()
+ ".configure(", notified));
+ }
+ });
+ assertTrue(notified.get());
+ }
+
+ public void testInjectorInjectMembers() {
+ final Object object = new Object() {
+ @Inject Foo foo;
+ };
+ final AtomicBoolean notified = new AtomicBoolean();
+ Guice.createInjector(new AbstractModule() {
+ @Override
+ protected void configure() {
+ bindListener(Matchers.any(),
+ new SpecialChecker(Foo.class, object.getClass().getName(),
notified));
+ }
+ }).injectMembers(object);
+ assertTrue(notified.get());
+ }
+
+ private static class SpecialChecker implements ProvisionListener {
+ private final Class<?> notifyType;
+ private final String firstSource;
+ private final AtomicBoolean notified;
+
+ public SpecialChecker(Class<?> notifyType, String firstSource,
AtomicBoolean notified) {
+ this.notifyType = notifyType;
+ this.firstSource = firstSource;
+ this.notified = notified;
+ }
+
+ public <T> void onProvision(ProvisionInvocation<T> provision) {
+ notified.set(true);
+ assertEquals(notifyType, provision.getKey().getRawType());
+ assertEquals(2, provision.getDependencyChain().size());
+
+ assertEquals(null,
provision.getDependencyChain().get(0).getDependency());
+
assertContains(provision.getDependencyChain().get(0).getBindingSource(),
firstSource);
+
+ assertEquals(notifyType,
+
provision.getDependencyChain().get(1).getDependency().getKey().getRawType());
+
assertContains(provision.getDependencyChain().get(1).getBindingSource(),
+ notifyType.getName() + ".class(");
+ }
+ }
+
+ private static class Instance {
+ @Inject A a;
+ }
+ private static class A {
+ @Inject A(B b) {}
+ }
+ private interface B {}
+ private static class BImpl implements B {
+ @Inject void inject(C c) {}
+ }
+ private interface C {}
+ private interface D {}
+ private static class DP implements Provider<D> {
+ @Inject Provider<E> ep;
+ public D get() {
+ ep.get();
+ return new D() {};
+ }
+ }
+ private static class E {
+ @SuppressWarnings("unused")
+ @Inject F f;
+ }
+ private static class F {}
+}
--
You received this message because you are subscribed to the Google Groups
"google-guice-dev" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to
[email protected].
For more options, visit this group at
http://groups.google.com/group/google-guice-dev?hl=en.