alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (CircularDependencyTest.java)

This example Java source code file (CircularDependencyTest.java) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

abstractmodule, annotation, bimpl, creationexception, found, inject, injector, override, provider, provides, provisionexception, simplesingleton, singleton, string, tried, util

The CircularDependencyTest.java Java example source code

/**
 * Copyright (C) 2006 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;

import static com.google.inject.Asserts.assertContains;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import com.google.common.collect.Iterables;
import com.google.common.collect.Maps;

import junit.framework.TestCase;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author crazybob@google.com (Bob Lee)
 * @author sameb@google.com (Sam Berlin)
 */
public class CircularDependencyTest extends TestCase {

  @Override
  protected void setUp() throws Exception {
    AImpl.nextId = 0;
    BImpl.nextId = 0;
  }

  public void testCircularlyDependentConstructors()
      throws CreationException {
    Injector injector = Guice.createInjector(new AbstractModule() {
      @Override
      protected void configure() {
        bind(A.class).to(AImpl.class);
        bind(B.class).to(BImpl.class);
      }
    });
    assertCircularDependencies(injector);
  }

  public void testCircularlyDependentConstructorsWithProviderMethods()
      throws CreationException {
    Injector injector = Guice.createInjector(new AbstractModule() {
      @Override
      protected void configure() {}

      @Provides @Singleton A a(B b) { return new AImpl(b); }
      @Provides B b(A a) { return new BImpl(a); }
    });
    assertCircularDependencies(injector);
  }

  public void testCircularlyDependentConstructorsWithProviderInstances()
      throws CreationException {
    Injector injector = Guice.createInjector(new AbstractModule() {
      @Override
      protected void configure() {
        bind(A.class).toProvider(new Provider<A>() {
          @Inject Provider<B> bp;
          @Override
          public A get() {
            return new AImpl(bp.get());
          }
        }).in(Singleton.class);
        bind(B.class).toProvider(new Provider<B>() {
          @Inject Provider<A> ap;
          @Override
          public B get() {
            return new BImpl(ap.get());
          }
        });
      }
    });
    assertCircularDependencies(injector);
  }

  public void testCircularlyDependentConstructorsWithProviderKeys()
      throws CreationException {
    Injector injector = Guice.createInjector(new AbstractModule() {
      @Override
      protected void configure() {
        bind(A.class).toProvider(AP.class).in(Singleton.class);
        bind(B.class).toProvider(BP.class);
      }
    });
    assertCircularDependencies(injector);
  }

  public void testCircularlyDependentConstructorsWithProvidedBy()
      throws CreationException {
    Injector injector = Guice.createInjector();
    assertCircularDependencies(injector);
  }

  private void assertCircularDependencies(Injector injector) {
    A a = injector.getInstance(A.class);
    assertNotNull(a.getB().getA());
    assertEquals(0, a.id());
    assertEquals(a.id(), a.getB().getA().id());
    assertEquals(0, a.getB().id());
    assertEquals(1, AImpl.nextId);
    assertEquals(1, BImpl.nextId);
    assertSame(a, injector.getInstance(A.class));
  }

  @ProvidedBy(AutoAP.class)
  public interface A {
    B getB();
    int id();
  }

  @Singleton
  static class AImpl implements A {
    static int nextId;
    int id = nextId++;

    final B b;
    @Inject public AImpl(B b) {
      this.b = b;
    }
    @Override
    public int id() {
      return id;
    }
    @Override
    public B getB() {
      return b;
    }
  }

  static class AP implements Provider<A> {
    @Inject Provider<B> bp;
    @Override
    public A get() {
      return new AImpl(bp.get());
    }
  }

  @Singleton
  static class AutoAP implements Provider<A> {
    @Inject Provider<B> bp;
    A a;

    @Override
    public A get() {
      if (a == null) {
        a = new AImpl(bp.get());
      }
      return a;
    }
  }

  @ProvidedBy(BP.class)
  public interface B {
    A getA();
    int id();
  }

  static class BImpl implements B {
    static int nextId;
    int id = nextId++;

    final A a;
    @Inject public BImpl(A a) {
      this.a = a;
    }
    @Override
    public int id() {
      return id;
    }
    @Override
    public A getA() {
      return a;
    }
  }

  static class BP implements Provider<B> {
    Provider<A> ap;
    @Inject BP(Provider<A> ap) {
      this.ap = ap;
    }
    @Override
    public B get() {
      return new BImpl(ap.get());
    }
  }

  public void testUnresolvableCircularDependency() {
    try {
      Guice.createInjector().getInstance(C.class);
      fail();
    } catch (ProvisionException expected) {
      assertContains(expected.getMessage(),
          "Tried proxying " + C.class.getName() + " to support a circular dependency, ",
          "but it is not an interface.");
    }
  }

  public void testUnresolvableCircularDependenciesWithProviderInstances() {
    try {
      Guice.createInjector(new AbstractModule() {
        @Override protected void configure() {}
        @Provides C c(D d) { return null; }
        @Provides D d(C c) { return null; }
      }).getInstance(C.class);
      fail();
    } catch (ProvisionException expected) {
      assertContains(expected.getMessage(),
          "Tried proxying " + C.class.getName() + " to support a circular dependency, ",
          "but it is not an interface.");
    }
  }

  public void testUnresolvableCircularDependenciesWithProviderKeys() {
    try {
      Guice.createInjector(new AbstractModule() {
        @Override protected void configure() {
          bind(C2.class).toProvider(C2P.class);
          bind(D2.class).toProvider(D2P.class);
        }
      }).getInstance(C2.class);
      fail();
    } catch (ProvisionException expected) {
      assertContains(expected.getMessage(),
          "Tried proxying " + C2.class.getName() + " to support a circular dependency, ",
          "but it is not an interface.");
    }
  }

  public void testUnresolvableCircularDependenciesWithProvidedBy() {
    try {
      Guice.createInjector().getInstance(C2.class);
      fail();
    } catch (ProvisionException expected) {
      assertContains(expected.getMessage(),
          "Tried proxying " + C2.class.getName() + " to support a circular dependency, ",
          "but it is not an interface.");
    }
  }

  static class C {
    @Inject C(D d) {}
  }
  static class D {
    @Inject D(C c) {}
  }

  static class C2P implements Provider<C2> {
    @Inject Provider<D2> dp;
    @Override
    public C2 get() {
      dp.get();
      return null;
    }
  }
  static class D2P implements Provider<D2> {
    @Inject Provider<C2> cp;
    @Override
    public D2 get() {
      cp.get();
      return null;
    }
  }
  @ProvidedBy(C2P.class)
  static class C2 {
    @Inject C2(D2 d) {}
  }
  @ProvidedBy(D2P.class)
  static class D2 {
    @Inject D2(C2 c) {}
  }

  public void testDisabledCircularDependency() {
    try {
      Guice.createInjector(new AbstractModule() {
        @Override
        protected void configure() {
          binder().disableCircularProxies();
        }
      }).getInstance(C.class);
      fail();
    } catch (ProvisionException expected) {
      assertContains(expected.getMessage(),
          "Found a circular dependency involving " + C.class.getName() + ", and circular dependencies are disabled.");
    }
  }

  public void testDisabledCircularDependenciesWithProviderInstances() {
    try {
      Guice.createInjector(new AbstractModule() {
        @Override protected void configure() {
          binder().disableCircularProxies();
        }
        @Provides C c(D d) { return null; }
        @Provides D d(C c) { return null; }
      }).getInstance(C.class);
      fail();
    } catch (ProvisionException expected) {
      assertContains(expected.getMessage(),
          "Found a circular dependency involving " + C.class.getName() + ", and circular dependencies are disabled.");
    }
  }

  public void testDisabledCircularDependenciesWithProviderKeys() {
    try {
      Guice.createInjector(new AbstractModule() {
        @Override protected void configure() {
          binder().disableCircularProxies();
          bind(C2.class).toProvider(C2P.class);
          bind(D2.class).toProvider(D2P.class);
        }
      }).getInstance(C2.class);
      fail();
    } catch (ProvisionException expected) {
      assertContains(expected.getMessage(),
          "Found a circular dependency involving " + C2.class.getName() + ", and circular dependencies are disabled.");
    }
  }

  public void testDisabledCircularDependenciesWithProvidedBy() {
    try {
      Guice.createInjector(new AbstractModule() {
        @Override
        protected void configure() {
          binder().disableCircularProxies();
        }
      }).getInstance(C2.class);
      fail();
    } catch (ProvisionException expected) {
      assertContains(expected.getMessage(),
          "Found a circular dependency involving " + C2.class.getName() + ", and circular dependencies are disabled.");
    }
  }

  /**
   * As reported by issue 349, we give a lousy trace when a class is circularly
   * dependent on itself in multiple ways.
   */
  public void testCircularlyDependentMultipleWays() {
    Injector injector = Guice.createInjector(new AbstractModule() {
      @Override
      protected void configure() {
        binder.bind(A.class).to(E.class);
        binder.bind(B.class).to(E.class);
      }
    });
    injector.getInstance(A.class);
  }

  public void testDisablingCircularDependencies() {
    Injector injector = Guice.createInjector(new AbstractModule() {
      @Override
      protected void configure() {
        binder().disableCircularProxies();
        binder.bind(A.class).to(E.class);
        binder.bind(B.class).to(E.class);
      }
    });

    try {
      injector.getInstance(A.class);
      fail("expected exception");
    } catch(ProvisionException expected) {
      assertContains(expected.getMessage(),
          "Found a circular dependency involving " + A.class.getName() + ", and circular dependencies are disabled.",
          "Found a circular dependency involving " + B.class.getName() + ", and circular dependencies are disabled.");
    }
  }

  @Singleton
  static class E implements A, B {
    @Inject
    public E(A a, B b) {}

    @Override
    public B getB() {
      return this;
    }

    @Override
    public A getA() {
      return this;
    }

    @Override
    public int id() {
      return 0;
    }
  }


  public void testCircularDependencyProxyDelegateNeverInitialized() {
    Injector injector = Guice.createInjector(new AbstractModule() {
      @Override
      protected void configure() {
        bind(F.class).to(RealF.class);
        bind(G.class).to(RealG.class);
      }
    });
    F f = injector.getInstance(F.class);
    assertEquals("F", f.g().f().toString());
    assertEquals("G", f.g().f().g().toString());

  }

  public interface F {
    G g();
  }

  @Singleton
  public static class RealF implements F {
    private final G g;
    @Inject RealF(G g) {
      this.g = g;
    }

    @Override
    public G g() {
      return g;
    }

    @Override public String toString() {
      return "F";
    }
  }

  public interface G {
    F f();
  }

  @Singleton
  public static class RealG implements G {
    private final F f;
    @Inject RealG(F f) {
      this.f = f;
    }

    @Override
    public F f() {
      return f;
    }

    @Override public String toString() {
      return "G";
    }
  }

  /**
   * Tests that ProviderInternalFactory can detect circular dependencies
   * before it gets to Scopes.SINGLETON.  This is especially important
   * because the failure in Scopes.SINGLETON doesn't have enough context to
   * provide a decent error message.
   */
  public void testCircularDependenciesDetectedEarlyWhenDependenciesHaveDifferentTypes() {
    Injector injector = Guice.createInjector(new AbstractModule() {
      @Override
      protected void configure() {
        bind(Number.class).to(Integer.class);
      }

      @Provides @Singleton Integer provideInteger(List list) {
        return new Integer(2);
      }

      @Provides List provideList(Integer integer) {
        return new ArrayList();
      }
    });
    try {
      injector.getInstance(Number.class);
      fail();
    } catch(ProvisionException expected) {
      assertContains(expected.getMessage(),
          "Tried proxying " + Integer.class.getName() + " to support a circular dependency, ",
          "but it is not an interface.");
    }
  }

  public void testPrivateModulesDontTriggerCircularErrorsInProviders() {
    Injector injector = Guice.createInjector(new AbstractModule() {
      @Override
      protected void configure() {
        install(new PrivateModule() {
          @Override
          protected void configure() {
            bind(Foo.class);
            expose(Foo.class);
          }
          @Provides String provideString(Bar bar) {
            return new String("private 1, " + bar.string);
          }
        });
        install(new PrivateModule() {
          @Override
          protected void configure() {
            bind(Bar.class);
            expose(Bar.class);
          }
          @Provides String provideString() {
            return new String("private 2");
          }
        });
      }
    });
    Foo foo = injector.getInstance(Foo.class);
    assertEquals("private 1, private 2", foo.string);
  }
  static class Foo {
    @Inject String string;
  }
  static class Bar {
    @Inject String string;
  }

  /**
   * When Scope Providers call their unscoped Provider's get() methods are
   * called, it's possible that the result is a circular proxy designed for one
   * specific parameter (not for all possible parameters). But custom scopes
   * typically cache the results without checking to see if the result is a
   * proxy. This leads to caching a result that is unsuitable for reuse for
   * other parameters.
   *
   * This means that custom proxies have to do an
   *   {@code if(Scopes.isCircularProxy(..))}
   * in order to avoid exceptions.
   */
  public void testCustomScopeCircularProxies() {
    Injector injector = Guice.createInjector(new AbstractModule() {
      @Override
      protected void configure() {
        bindScope(SimpleSingleton.class, new BasicSingleton());
        bind(H.class).to(HImpl.class);
        bind(I.class).to(IImpl.class);
        bind(J.class).to(JImpl.class);
      }
    });

    // The reason this happens is because the Scope gets these requests, in order:
    // entry: Key<IImpl> (1 - from getInstance call)
    // entry: Key<HImpl>
    // entry: Key<IImpl> (2 - circular dependency from HImpl)
    // result of 2nd Key<IImpl> - a com.google.inject.$Proxy, because it's a circular proxy
    // result of Key<HImpl> - an HImpl
    // entry: Key<JImpl>
    // entry: Key<IImpl> (3 - another circular dependency, this time from JImpl)
    // At this point, if the first Key<Impl> result was cached, our cache would have
    //  Key<IImpl> caching to an instanceof of I, but not an an instanceof of IImpl.
    // If returned this, it would result in cglib giving a ClassCastException or
    // java reflection giving an IllegalArgumentException when filling in parameters
    // for the constructor, because JImpl wants an IImpl, not an I.

    try {
      injector.getInstance(IImpl.class);
      fail();
    } catch(ProvisionException pe) {
      assertContains(Iterables.getOnlyElement(pe.getErrorMessages()).getMessage(),
          "Tried proxying " + IImpl.class.getName()
          + " to support a circular dependency, but it is not an interface.");
    }
  }

  interface H {}
  interface I {}
  interface J {}
  @SimpleSingleton
  static class HImpl implements H {
     @Inject HImpl(I i) {}
  }
  @SimpleSingleton
  static class IImpl implements I {
     @Inject IImpl(HImpl i, J j) {}
  }
  @SimpleSingleton
  static class JImpl implements J {
     @Inject JImpl(IImpl i) {}
  }

  @Target({ ElementType.TYPE, ElementType.METHOD })
  @Retention(RUNTIME)
  @ScopeAnnotation
  public @interface SimpleSingleton {}
  public static class BasicSingleton implements Scope {
    private static Map<Key cache = Maps.newHashMap();
    @Override
    public <T> Provider scope(final Key key, final Provider unscoped) {
      return new Provider<T>() {
        @Override
        @SuppressWarnings("unchecked")
        public T get() {
          if (!cache.containsKey(key)) {
            T t = unscoped.get();
            if (Scopes.isCircularProxy(t)) {
              return t;
            }
            cache.put(key, t);
          }
          return (T)cache.get(key);
        }
      };
    }
  }

  public void testDisabledNonConstructorCircularDependencies() {
    Injector injector = Guice.createInjector(new AbstractModule() {
      @Override
      protected void configure() {
        binder().disableCircularProxies();
      }
    });

    try {
      injector.getInstance(K.class);
      fail("expected exception");
    } catch(ProvisionException expected) {
      assertContains(expected.getMessage(),
          "Found a circular dependency involving " + K.class.getName() + ", and circular dependencies are disabled.");
    }

    try {
      injector.getInstance(L.class);
      fail("expected exception");
    } catch(ProvisionException expected) {
      assertContains(expected.getMessage(),
          "Found a circular dependency involving " + L.class.getName() + ", and circular dependencies are disabled.");
    }
  }

  static class K {
    @Inject L l;
  }
  static class L {
    @Inject void inject(K k) {
    }
  }
}

Other Java examples (source code examples)

Here is a short list of links related to this Java CircularDependencyTest.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.