introduces InjectorBuilder.disableCircularProxies, to forcibly prevent guice from creating a proxy to resolve circular dependencies.

git-svn-id: https://google-guice.googlecode.com/svn/trunk@1151 d779f126-a31b-0410-b53b-1d3aecad763e
diff --git a/src/com/google/inject/InjectorBuilder.java b/src/com/google/inject/InjectorBuilder.java
index 8a1beef..57586a8 100644
--- a/src/com/google/inject/InjectorBuilder.java
+++ b/src/com/google/inject/InjectorBuilder.java
@@ -1,5 +1,6 @@
 package com.google.inject;
 
+import java.lang.reflect.Proxy;
 import java.util.Arrays;
 
 import com.google.inject.internal.InternalInjectorCreator;
@@ -43,7 +44,8 @@
   private final InternalInjectorCreator creator = new InternalInjectorCreator();
   
   private Stage stage = Stage.DEVELOPMENT;
-  private boolean jitDisabled;
+  private boolean jitDisabled = false;
+  private boolean allowCircularProxy = true;
   
   /**
    * Sets the stage for the injector. If the stage is {@link Stage#PRODUCTION}, 
@@ -71,6 +73,15 @@
     this.jitDisabled = true;
     return this;
   }
+  
+  /**
+   * Prevents Guice from constructing a {@link Proxy} when a circular dependency
+   * is found.
+   */
+  public InjectorBuilder disableCircularProxies() {
+    this.allowCircularProxy = false;
+    return this;
+  }
 
   /** Adds more modules that will be used when the Injector is created. */
   public InjectorBuilder addModules(Iterable<? extends Module> modules) {
@@ -86,7 +97,7 @@
 
   /** Builds the injector. */
   public Injector build() {
-    creator.injectorOptions(new InternalInjectorCreator.InjectorOptions(stage, jitDisabled));
+    creator.injectorOptions(new InternalInjectorCreator.InjectorOptions(stage, jitDisabled, allowCircularProxy));
     return creator.build();
   }
 
diff --git a/src/com/google/inject/internal/ConstructorBindingImpl.java b/src/com/google/inject/internal/ConstructorBindingImpl.java
index 94c0847..b363b50 100644
--- a/src/com/google/inject/internal/ConstructorBindingImpl.java
+++ b/src/com/google/inject/internal/ConstructorBindingImpl.java
@@ -117,6 +117,7 @@
 
   @SuppressWarnings("unchecked") // the result type always agrees with the ConstructorInjector type
   public void initialize(InjectorImpl injector, Errors errors) throws ErrorsException {
+    factory.allowCircularProxy = injector.options.allowCircularProxy;
     factory.constructorInjector
         = (ConstructorInjector<T>) injector.constructors.get(constructorInjectionPoint, errors);
   }
@@ -177,6 +178,7 @@
   private static class Factory<T> implements InternalFactory<T> {
     private final boolean failIfNotLinked;
     private final Key<?> key;
+    private boolean allowCircularProxy;
     private ConstructorInjector<T> constructorInjector;
     
     Factory(boolean failIfNotLinked, Key<?> key) {
@@ -196,7 +198,7 @@
       // This may not actually be safe because it could return a super type of T (if that's all the
       // client needs), but it should be OK in practice thanks to the wonders of erasure.
       return (T) constructorInjector.construct(errors, context,
-          dependency.getKey().getTypeLiteral().getRawType());
+          dependency.getKey().getTypeLiteral().getRawType(), allowCircularProxy);
     }
   }
 }
diff --git a/src/com/google/inject/internal/ConstructorInjector.java b/src/com/google/inject/internal/ConstructorInjector.java
index 9e9e377..1f8d65d 100644
--- a/src/com/google/inject/internal/ConstructorInjector.java
+++ b/src/com/google/inject/internal/ConstructorInjector.java
@@ -55,14 +55,18 @@
    * Construct an instance. Returns {@code Object} instead of {@code T} because
    * it may return a proxy.
    */
-  Object construct(Errors errors, InternalContext context, Class<?> expectedType)
+  Object construct(Errors errors, InternalContext context, Class<?> expectedType, boolean allowProxy)
       throws ErrorsException {
     ConstructionContext<T> constructionContext = context.getConstructionContext(this);
 
     // We have a circular reference between constructors. Return a proxy.
     if (constructionContext.isConstructing()) {
-      // TODO (crazybob): if we can't proxy this object, can we proxy the other object?
-      return constructionContext.createProxy(errors, expectedType);
+      if(!allowProxy) {
+        throw errors.circularProxiesDisabled(expectedType).toException();
+      } else {      
+        // TODO (crazybob): if we can't proxy this object, can we proxy the other object?
+        return constructionContext.createProxy(errors, expectedType);
+      }
     }
 
     // If we're re-entering this factory while injecting fields or methods,
diff --git a/src/com/google/inject/internal/Errors.java b/src/com/google/inject/internal/Errors.java
index ca09096..99c7732 100644
--- a/src/com/google/inject/internal/Errors.java
+++ b/src/com/google/inject/internal/Errors.java
@@ -370,6 +370,12 @@
         "Tried proxying %s to support a circular dependency, but it is not an interface.",
         expectedType);
   }
+  
+  public Errors circularProxiesDisabled(Class<?> expectedType) {
+    return addMessage(
+        "Tried proxying %s to support a circular dependency, but circular proxies are disabled.",
+        expectedType);
+  }
 
   public void throwCreationExceptionIfErrorsExist() {
     if (!hasErrors()) {
diff --git a/src/com/google/inject/internal/InternalInjectorCreator.java b/src/com/google/inject/internal/InternalInjectorCreator.java
index d018d21..3e491c0 100644
--- a/src/com/google/inject/internal/InternalInjectorCreator.java
+++ b/src/com/google/inject/internal/InternalInjectorCreator.java
@@ -67,10 +67,12 @@
   public static class InjectorOptions {
     final Stage stage;
     final boolean jitDisabled;
+    final boolean allowCircularProxy;
     
-    public InjectorOptions(Stage stage, boolean jitDisabled) {
+    public InjectorOptions(Stage stage, boolean jitDisabled, boolean allowCircularProxy) {
       this.stage = stage;
       this.jitDisabled = jitDisabled;
+      this.allowCircularProxy = allowCircularProxy;
     }
   }
 
diff --git a/test/com/google/inject/CircularDependencyTest.java b/test/com/google/inject/CircularDependencyTest.java
index aed3984..1cc4e74 100644
--- a/test/com/google/inject/CircularDependencyTest.java
+++ b/test/com/google/inject/CircularDependencyTest.java
@@ -104,6 +104,26 @@
     });
     injector.getInstance(A.class);
   }
+  
+  public void testDisablingCircularProxies() {
+    Injector injector = new InjectorBuilder()
+      .disableCircularProxies()
+      .addModules(new AbstractModule() {
+      protected void configure() {
+        binder.bind(A.class).to(E.class);
+        binder.bind(B.class).to(E.class);
+      }
+    }).build();
+    
+    try {
+      injector.getInstance(A.class);
+      fail("expected exception");
+    } catch(ProvisionException expected) {
+      assertContains(expected.getMessage(),
+          "Tried proxying " + A.class.getName() + " to support a circular dependency, but circular proxies are disabled", 
+          "Tried proxying " + B.class.getName() + " to support a circular dependency, but circular proxies are disabled");
+    }
+  }
 
   @Singleton
   static class E implements A, B {