Tweaking error handling.
There was a bug where we weren't handling an ErrorsException that wasn't related to a passed-in Errors object. This Errors approach is good 'cause the messages are good. But it still isn't particularly easy to code against because we need to both pass-in an Errors object and throw an ErrorsException if a return value cannot be computed.
Also changing how error message merging works, so that if we get a problem looking up say, a cached constructor, we use the code that wants that constructor as a source.
git-svn-id: https://google-guice.googlecode.com/svn/trunk@557 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/ClassBindingImpl.java b/src/com/google/inject/ClassBindingImpl.java
index a177607..b61ee4f 100644
--- a/src/com/google/inject/ClassBindingImpl.java
+++ b/src/com/google/inject/ClassBindingImpl.java
@@ -43,7 +43,7 @@
}
@Override void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
- lateBoundConstructor.bind(injector, getBoundClass());
+ lateBoundConstructor.bind(injector, getBoundClass(), errors);
}
public void accept(BindingVisitor<? super T> visitor) {
diff --git a/src/com/google/inject/ConstructorInjector.java b/src/com/google/inject/ConstructorInjector.java
index 4cff625..25dc08e 100644
--- a/src/com/google/inject/ConstructorInjector.java
+++ b/src/com/google/inject/ConstructorInjector.java
@@ -42,7 +42,8 @@
this.implementation = implementation;
constructionProxy = injector.reflection.getConstructionProxy(errors, implementation);
parameterInjectors = createParameterInjector(errors, injector, constructionProxy);
- List<SingleMemberInjector> memberInjectorsList = injector.getMemberInjectors(implementation);
+ List<SingleMemberInjector> memberInjectorsList
+ = injector.getMemberInjectors(implementation, errors);
memberInjectors = memberInjectorsList.toArray(
new SingleMemberInjector[memberInjectorsList.size()]);
}
diff --git a/src/com/google/inject/CreationTimeMemberInjector.java b/src/com/google/inject/CreationTimeMemberInjector.java
index 1c9519c..f34e82b 100644
--- a/src/com/google/inject/CreationTimeMemberInjector.java
+++ b/src/com/google/inject/CreationTimeMemberInjector.java
@@ -54,7 +54,7 @@
void validateOustandingInjections(Errors errors) {
for (Object toInject : outstandingInjections) {
try {
- injector.getMemberInjectors(toInject.getClass());
+ injector.getMemberInjectors(toInject.getClass(), errors);
} catch (ErrorsException e) {
errors.merge(e.getErrors());
}
diff --git a/src/com/google/inject/InjectorImpl.java b/src/com/google/inject/InjectorImpl.java
index e8629dd..3e9d1cb 100644
--- a/src/com/google/inject/InjectorImpl.java
+++ b/src/com/google/inject/InjectorImpl.java
@@ -464,8 +464,8 @@
static class LateBoundConstructor<T> implements InternalFactory<T> {
ConstructorInjector<T> constructorInjector;
- void bind(InjectorImpl injector, Class<T> implementation) throws ErrorsException {
- constructorInjector = injector.getConstructor(implementation);
+ void bind(InjectorImpl injector, Class<T> implementation, Errors errors) throws ErrorsException {
+ constructorInjector = injector.getConstructor(implementation, errors);
}
@SuppressWarnings("unchecked")
@@ -627,7 +627,7 @@
}
};
- public List<SingleMemberInjector> getMemberInjectors(Class<?> type)
+ public List<SingleMemberInjector> getMemberInjectors(Class<?> type, Errors errors)
throws ErrorsException {
Object injectorsOrError = injectors.get(type);
if (injectorsOrError instanceof List) {
@@ -635,7 +635,8 @@
List<SingleMemberInjector> result = (List<SingleMemberInjector>) injectorsOrError;
return result;
} else if (injectorsOrError instanceof Errors) {
- throw ((Errors) injectorsOrError).copy().toException();
+ errors.merge((Errors) injectorsOrError);
+ throw errors.toException();
} else {
throw new AssertionError();
}
@@ -894,7 +895,7 @@
final Map<Class<?>, Object> constructors = new ReferenceCache<Class<?>, Object>() {
@SuppressWarnings("unchecked")
protected Object create(Class<?> implementation) {
- Errors errors = new Errors(StackTraceElements.forType(implementation));
+ Errors errors = new Errors();
try {
ConstructorInjector result = new ConstructorInjector(
errors, InjectorImpl.this, implementation);
@@ -955,7 +956,7 @@
return;
}
- for (SingleMemberInjector injector : getMemberInjectors(o.getClass())) {
+ for (SingleMemberInjector injector : getMemberInjectors(o.getClass(), errors)) {
injector.inject(errors, context, o);
}
}
@@ -1067,10 +1068,12 @@
/** Gets a constructor function for a given implementation class. */
@SuppressWarnings("unchecked")
- <T> ConstructorInjector<T> getConstructor(Class<T> implementation) throws ErrorsException {
+ <T> ConstructorInjector<T> getConstructor(Class<T> implementation, Errors errors)
+ throws ErrorsException {
Object o = constructors.get(implementation);
if (o instanceof Errors) {
- throw ((Errors) o).copy().toException();
+ errors.merge((Errors) o);
+ throw errors.toException();
}
else if (o instanceof ConstructorInjector<?>) {
return (ConstructorInjector<T>) o;
@@ -1088,10 +1091,12 @@
List<InjectionPoint<?>> getModifiableFieldAndMethodInjectionsFor(Class<?> clazz)
throws ErrorsException {
+ Errors errors = new Errors();
List<InjectionPoint<?>> dependencies = Lists.newArrayList();
- for (SingleMemberInjector singleMemberInjector : getMemberInjectors(clazz)) {
+ for (SingleMemberInjector singleMemberInjector : getMemberInjectors(clazz, errors)) {
dependencies.addAll(singleMemberInjector.getDependencies());
}
+ errors.throwIfNecessary();
return dependencies;
}
diff --git a/src/com/google/inject/ProxyFactory.java b/src/com/google/inject/ProxyFactory.java
index f43cc9f..d0da0ca 100644
--- a/src/com/google/inject/ProxyFactory.java
+++ b/src/com/google/inject/ProxyFactory.java
@@ -24,7 +24,6 @@
import com.google.inject.internal.Errors;
import com.google.inject.internal.ErrorsException;
import com.google.inject.internal.ReferenceCache;
-import com.google.inject.internal.StackTraceElements;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -58,12 +57,12 @@
@SuppressWarnings("unchecked")
public <T> ConstructionProxy<T> get(Errors errors, Constructor<T> constructor)
throws ErrorsException {
- return (ConstructionProxy<T>) getConstructionProxy(constructor);
+ return (ConstructionProxy<T>) getConstructionProxy(constructor, errors);
}
Map<Constructor<?>, Object> constructionProxies = new ReferenceCache<Constructor<?>, Object>() {
protected Object create(Constructor<?> constructor) {
- Errors errors = new Errors(StackTraceElements.forMember(constructor));
+ Errors errors = new Errors();
try {
ConstructionProxy<?> result = createConstructionProxy(errors, constructor);
errors.throwIfNecessary();
@@ -74,14 +73,15 @@
}
};
- public ConstructionProxy<?> getConstructionProxy(Constructor<?> constructor)
+ public ConstructionProxy<?> getConstructionProxy(Constructor<?> constructor, Errors errors)
throws ErrorsException {
Object constructionProxyOrErrors = constructionProxies.get(constructor);
if (constructionProxyOrErrors instanceof ConstructionProxy<?>) {
return (ConstructionProxy<?>) constructionProxyOrErrors;
} else if (constructionProxyOrErrors instanceof Errors) {
- throw ((Errors) constructionProxyOrErrors).copy().toException();
+ errors.merge((Errors) constructionProxyOrErrors);
+ throw errors.toException();
} else {
throw new AssertionError();
}
diff --git a/src/com/google/inject/internal/Errors.java b/src/com/google/inject/internal/Errors.java
index f034abb..ee94dc9 100644
--- a/src/com/google/inject/internal/Errors.java
+++ b/src/com/google/inject/internal/Errors.java
@@ -332,8 +332,10 @@
List<InjectionPoint> injectionPoints = Lists.newArrayList();
injectionPoints.addAll(this.injectionPoints);
injectionPoints.addAll(message.getInjectionPoints());
- errors.add(new Message(message.getSource(), message.getMessage(), injectionPoints,
- message.getCause()));
+ Object source = message.getSource() != SourceProvider.UNKNOWN_SOURCE
+ ? message.getSource()
+ : this.source;
+ errors.add(new Message(source, message.getMessage(), injectionPoints, message.getCause()));
}
}
diff --git a/test/com/google/inject/BinderTest.java b/test/com/google/inject/BinderTest.java
index 655deb6..30c9878 100644
--- a/test/com/google/inject/BinderTest.java
+++ b/test/com/google/inject/BinderTest.java
@@ -92,23 +92,23 @@
}
}
- public void testGetInstanceOfAnAbstractClass() {
- Injector injector = Guice.createInjector();
+ public void testMissingDependency() {
try {
- injector.getInstance(Runnable.class);
- fail();
- } catch(ProvisionException expected) {
- assertContains(expected.getMessage(),
+ Guice.createInjector(new AbstractModule() {
+ public void configure() {
+ bind(NeedsRunnable.class);
+ }
+ });
+ } catch (CreationException e) {
+ assertEquals(1, e.getErrorMessages().size());
+ assertContains(e.getMessage(),
+ "1) Error at " + getClass().getName(),
"No implementation for java.lang.Runnable was bound.");
}
+ }
- try {
- injector.getInstance(Key.get(Date.class, Names.named("date")));
- fail();
- } catch(ProvisionException expected) {
- assertContains(expected.getMessage(), "No implementation for java.util.Date "
- + "annotated with @com.google.inject.name.Named(value=date) was bound.");
- }
+ static class NeedsRunnable {
+ @Inject Runnable runnable;
}
public void testDanglingConstantBinding() {
@@ -120,7 +120,9 @@
});
fail();
} catch (CreationException expected) {
- assertContains(expected.getMessage(), "Missing constant value.");
+ assertContains(expected.getMessage(),
+ "1) Error at " + getClass().getName(),
+ "Missing constant value. Please call to(...).");
}
}
@@ -260,6 +262,26 @@
assertSame(JustAClass.class, injector.getInstance(JustAClass.class).getClass());
}
+ public void testPartialInjectorGetInstance() {
+ Injector injector = Guice.createInjector();
+ try {
+ injector.getInstance(MissingParameter.class);
+ fail();
+ } catch (ProvisionException expected) {
+ assertContains(expected.getMessage(),
+ "1) Error at " + MissingParameter.class.getName() + ".<init>(BinderTest.java:",
+ "Could not find a suitable constructor in " + NoInjectConstructor.class.getName());
+ }
+ }
+
+ static class MissingParameter {
+ @Inject MissingParameter(NoInjectConstructor noInjectConstructor) {}
+ }
+
+ static class NoInjectConstructor {
+ private NoInjectConstructor() {}
+ }
+
@ProvidedBy(HasProvidedBy1Provider.class)
interface HasProvidedBy1 {}
diff --git a/test/com/googlecode/guice/BytecodeGenTest.java b/test/com/googlecode/guice/BytecodeGenTest.java
index d47d83f..2f54375 100644
--- a/test/com/googlecode/guice/BytecodeGenTest.java
+++ b/test/com/googlecode/guice/BytecodeGenTest.java
@@ -119,6 +119,26 @@
}
}
+ /** as loaded by another class loader */
+ private Class<ProxyTest> proxyTestClass;
+ private Class<ProxyTestImpl> realClass;
+ private Module testModule;
+
+ @SuppressWarnings("unchecked")
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ ClassLoader testClassLoader = new TestVisibilityClassLoader();
+ proxyTestClass = (Class<ProxyTest>) testClassLoader.loadClass(ProxyTest.class.getName());
+ realClass = (Class<ProxyTestImpl>) testClassLoader.loadClass(ProxyTestImpl.class.getName());
+
+ testModule = new AbstractModule() {
+ public void configure() {
+ bind(proxyTestClass).to(realClass);
+ }
+ };
+ }
+
interface ProxyTest {
String sayHello();
}
@@ -141,33 +161,13 @@
}
}
- @SuppressWarnings("unchecked")
- public void testProxyClassLoading() {
- final ClassLoader testClassLoader = new TestVisibilityClassLoader();
+ public void testProxyClassLoading() throws Exception {
+ Object testObject = Guice.createInjector(interceptorModule, testModule)
+ .getInstance(proxyTestClass);
- final Class<ProxyTest> testAPIClazz;
- final Class<ProxyTest> testImplClazz;
-
- try {
- testAPIClazz = (Class<ProxyTest>) testClassLoader.loadClass(ProxyTest.class.getName());
- testImplClazz = (Class<ProxyTest>) testClassLoader.loadClass(ProxyTestImpl.class.getName());
- } catch (final Exception e) {
- throw new RuntimeException(e);
- }
-
- final Object testObject = Guice.createInjector(interceptorModule, new Module() {
- public void configure(Binder binder) {
- binder.bind(testAPIClazz).to(testImplClazz);
- }
- }).getInstance(testAPIClazz);
-
- try {
- // verify method interception still works
- Method m = testImplClazz.getMethod("sayHello");
- assertEquals("HELLO WORLD", m.invoke(testObject));
- } catch (final Exception e) {
- throw new RuntimeException(e);
- }
+ // verify method interception still works
+ Method m = realClass.getMethod("sayHello");
+ assertEquals("HELLO WORLD", m.invoke(testObject));
}
public void testProxyClassUnloading() {