Home | History | Annotate | Download | only in inject
      1 /**
      2  * Copyright (C) 2006 Google Inc.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.inject;
     18 
     19 import static com.google.inject.Asserts.asModuleChain;
     20 import static com.google.inject.Asserts.assertContains;
     21 import static com.google.inject.Asserts.getDeclaringSourcePart;
     22 import static com.google.inject.name.Names.named;
     23 import static java.lang.annotation.RetentionPolicy.RUNTIME;
     24 
     25 import com.google.common.collect.ImmutableMap;
     26 import com.google.common.collect.Lists;
     27 import com.google.common.collect.Maps;
     28 import com.google.inject.name.Named;
     29 import com.google.inject.spi.Element;
     30 import com.google.inject.spi.Elements;
     31 import com.google.inject.spi.PrivateElements;
     32 import com.google.inject.util.Providers;
     33 
     34 import junit.framework.TestCase;
     35 
     36 import java.io.IOException;
     37 import java.lang.annotation.ElementType;
     38 import java.lang.annotation.Retention;
     39 import java.lang.annotation.Target;
     40 import java.util.ArrayList;
     41 import java.util.Arrays;
     42 import java.util.Collections;
     43 import java.util.Comparator;
     44 import java.util.HashSet;
     45 import java.util.Iterator;
     46 import java.util.List;
     47 import java.util.Map;
     48 import java.util.concurrent.Callable;
     49 import java.util.concurrent.CyclicBarrier;
     50 import java.util.concurrent.ExecutionException;
     51 import java.util.concurrent.Executors;
     52 import java.util.concurrent.Future;
     53 import java.util.concurrent.TimeUnit;
     54 
     55 /**
     56  * @author crazybob (at) google.com (Bob Lee)
     57  */
     58 public class ScopesTest extends TestCase {
     59 
     60   static final long DEADLOCK_TIMEOUT_SECONDS = 1;
     61 
     62   private final AbstractModule singletonsModule = new AbstractModule() {
     63     @Override protected void configure() {
     64       bind(BoundAsSingleton.class).in(Scopes.SINGLETON);
     65       bind(AnnotatedSingleton.class);
     66       bind(EagerSingleton.class).asEagerSingleton();
     67       bind(LinkedSingleton.class).to(RealLinkedSingleton.class);
     68       bind(DependsOnJustInTimeSingleton.class);
     69       bind(NotASingleton.class);
     70       bind(ImplementedBySingleton.class).in(Scopes.SINGLETON);
     71       bind(ProvidedBySingleton.class).in(Scopes.SINGLETON);
     72     }
     73   };
     74 
     75   @Override protected void setUp() throws Exception {
     76     AnnotatedSingleton.nextInstanceId = 0;
     77     BoundAsSingleton.nextInstanceId = 0;
     78     EagerSingleton.nextInstanceId = 0;
     79     RealLinkedSingleton.nextInstanceId = 0;
     80     JustInTimeSingleton.nextInstanceId = 0;
     81     NotASingleton.nextInstanceId = 0;
     82     Implementation.nextInstanceId = 0;
     83     ProvidedBySingleton.nextInstanceId = 0;
     84     ThrowingSingleton.nextInstanceId = 0;
     85   }
     86 
     87   public void testSingletons() {
     88     Injector injector = Guice.createInjector(singletonsModule);
     89 
     90     assertSame(
     91         injector.getInstance(BoundAsSingleton.class),
     92         injector.getInstance(BoundAsSingleton.class));
     93 
     94     assertSame(
     95         injector.getInstance(AnnotatedSingleton.class),
     96         injector.getInstance(AnnotatedSingleton.class));
     97 
     98     assertSame(
     99         injector.getInstance(EagerSingleton.class),
    100         injector.getInstance(EagerSingleton.class));
    101 
    102     assertSame(
    103         injector.getInstance(LinkedSingleton.class),
    104         injector.getInstance(LinkedSingleton.class));
    105 
    106     assertSame(
    107         injector.getInstance(JustInTimeSingleton.class),
    108         injector.getInstance(JustInTimeSingleton.class));
    109 
    110     assertNotSame(
    111         injector.getInstance(NotASingleton.class),
    112         injector.getInstance(NotASingleton.class));
    113 
    114     assertSame(
    115         injector.getInstance(ImplementedBySingleton.class),
    116         injector.getInstance(ImplementedBySingleton.class));
    117 
    118     assertSame(
    119         injector.getInstance(ProvidedBySingleton.class),
    120         injector.getInstance(ProvidedBySingleton.class));
    121   }
    122 
    123   public void testJustInTimeAnnotatedSingleton() {
    124     Injector injector = Guice.createInjector();
    125 
    126     assertSame(
    127         injector.getInstance(AnnotatedSingleton.class),
    128         injector.getInstance(AnnotatedSingleton.class));
    129   }
    130 
    131   public void testSingletonIsPerInjector() {
    132     assertNotSame(
    133         Guice.createInjector().getInstance(AnnotatedSingleton.class),
    134         Guice.createInjector().getInstance(AnnotatedSingleton.class));
    135   }
    136 
    137   public void testOverriddingAnnotation() {
    138     Injector injector = Guice.createInjector(new AbstractModule() {
    139       @Override protected void configure() {
    140         bind(AnnotatedSingleton.class).in(Scopes.NO_SCOPE);
    141       }
    142     });
    143 
    144     assertNotSame(
    145         injector.getInstance(AnnotatedSingleton.class),
    146         injector.getInstance(AnnotatedSingleton.class));
    147   }
    148 
    149   public void testScopingAnnotationsOnAbstractTypeViaBind() {
    150     try {
    151       Guice.createInjector(new AbstractModule() {
    152         @Override protected void configure() {
    153           bind(A.class).to(AImpl.class);
    154         }
    155       });
    156       fail();
    157     } catch (CreationException expected) {
    158       assertContains(expected.getMessage(),
    159           A.class.getName() + " is annotated with " + Singleton.class.getName(),
    160           "but scope annotations are not supported for abstract types.",
    161           "at " + A.class.getName() + ".class(ScopesTest.java:");
    162     }
    163   }
    164 
    165   @Singleton
    166   interface A {}
    167   static class AImpl implements A {}
    168 
    169   @Retention(RUNTIME)
    170   @interface Component {}
    171 
    172   @Component
    173   @Singleton
    174   interface ComponentAnnotationTest {}
    175   static class ComponentAnnotationTestImpl implements ComponentAnnotationTest {}
    176 
    177   public void testScopingAnnotationsOnAbstractTypeIsValidForComponent() {
    178     Guice.createInjector(new AbstractModule() {
    179       @Override protected void configure() {
    180         bind(ComponentAnnotationTest.class).to(ComponentAnnotationTestImpl.class);
    181       }
    182     });
    183   }
    184 
    185   public void testScopingAnnotationsOnAbstractTypeViaImplementedBy() {
    186     try {
    187       Guice.createInjector().getInstance(D.class);
    188       fail();
    189     } catch (ConfigurationException expected) {
    190       assertContains(expected.getMessage(),
    191           D.class.getName() + " is annotated with " + Singleton.class.getName(),
    192           "but scope annotations are not supported for abstract types.",
    193           "at " + D.class.getName() + ".class(ScopesTest.java:");
    194     }
    195   }
    196 
    197   @Singleton @ImplementedBy(DImpl.class)
    198   interface D {}
    199   static class DImpl implements D {}
    200 
    201   public void testScopingAnnotationsOnAbstractTypeViaProvidedBy() {
    202     try {
    203       Guice.createInjector().getInstance(E.class);
    204       fail();
    205     } catch (ConfigurationException expected) {
    206       assertContains(expected.getMessage(),
    207           E.class.getName() + " is annotated with " + Singleton.class.getName(),
    208           "but scope annotations are not supported for abstract types.",
    209           "at " + E.class.getName() + ".class(ScopesTest.java:");
    210     }
    211   }
    212 
    213   @Singleton @ProvidedBy(EProvider.class)
    214   interface E {}
    215   static class EProvider implements Provider<E> {
    216     public E get() {
    217       return null;
    218     }
    219   }
    220 
    221   public void testScopeUsedButNotBound() {
    222     try {
    223       Guice.createInjector(new AbstractModule() {
    224         @Override protected void configure() {
    225           bind(B.class).in(CustomScoped.class);
    226           bind(C.class);
    227         }
    228       });
    229       fail();
    230     } catch (CreationException expected) {
    231       assertContains(expected.getMessage(),
    232           "1) No scope is bound to " + CustomScoped.class.getName(),
    233           "at " + getClass().getName(), getDeclaringSourcePart(getClass()),
    234           "2) No scope is bound to " + CustomScoped.class.getName(),
    235           "at " + C.class.getName() + ".class");
    236     }
    237   }
    238 
    239   static class B {}
    240 
    241   @CustomScoped
    242   static class C {}
    243 
    244   public void testSingletonsInProductionStage() {
    245     Guice.createInjector(Stage.PRODUCTION, singletonsModule);
    246 
    247     assertEquals(1, AnnotatedSingleton.nextInstanceId);
    248     assertEquals(1, BoundAsSingleton.nextInstanceId);
    249     assertEquals(1, EagerSingleton.nextInstanceId);
    250     assertEquals(1, RealLinkedSingleton.nextInstanceId);
    251     assertEquals(1, JustInTimeSingleton.nextInstanceId);
    252     assertEquals(0, NotASingleton.nextInstanceId);
    253   }
    254 
    255   public void testSingletonsInDevelopmentStage() {
    256     Guice.createInjector(Stage.DEVELOPMENT, singletonsModule);
    257 
    258     assertEquals(0, AnnotatedSingleton.nextInstanceId);
    259     assertEquals(0, BoundAsSingleton.nextInstanceId);
    260     assertEquals(1, EagerSingleton.nextInstanceId);
    261     assertEquals(0, RealLinkedSingleton.nextInstanceId);
    262     assertEquals(0, JustInTimeSingleton.nextInstanceId);
    263     assertEquals(0, NotASingleton.nextInstanceId);
    264   }
    265 
    266   public void testSingletonScopeIsNotSerializable() throws IOException {
    267     Asserts.assertNotSerializable(Scopes.SINGLETON);
    268   }
    269 
    270   public void testNoScopeIsNotSerializable() throws IOException {
    271     Asserts.assertNotSerializable(Scopes.NO_SCOPE);
    272   }
    273 
    274   public void testUnscopedProviderWorksOutsideOfRequestedScope() {
    275     final RememberProviderScope scope = new RememberProviderScope();
    276 
    277     Injector injector = Guice.createInjector(new AbstractModule() {
    278       @Override protected void configure() {
    279         bindScope(CustomScoped.class, scope);
    280         bind(List.class).to(ArrayList.class).in(CustomScoped.class);
    281       }
    282     });
    283 
    284     injector.getInstance(List.class);
    285     Provider<?> listProvider = scope.providers.get(Key.get(List.class));
    286 
    287     // this line fails with a NullPointerException because the Providers
    288     // passed to Scope.scope() don't work outside of the scope() method.
    289     assertTrue(listProvider.get() instanceof ArrayList);
    290   }
    291 
    292   static class OuterRuntimeModule extends AbstractModule {
    293     @Override protected void configure() {
    294       install(new InnerRuntimeModule());
    295     }
    296   }
    297   static class InnerRuntimeModule extends AbstractModule {
    298     @Override protected void configure() {
    299       bindScope(NotRuntimeRetainedScoped.class, Scopes.NO_SCOPE);
    300     }
    301   }
    302   public void testScopeAnnotationWithoutRuntimeRetention() {
    303     try {
    304       Guice.createInjector(new OuterRuntimeModule());
    305       fail();
    306     } catch (CreationException expected) {
    307       assertContains(expected.getMessage(),
    308           "1) Please annotate " + NotRuntimeRetainedScoped.class.getName()
    309               + " with @Retention(RUNTIME).",
    310           "at " + InnerRuntimeModule.class.getName() + getDeclaringSourcePart(getClass()),
    311           asModuleChain(OuterRuntimeModule.class, InnerRuntimeModule.class));
    312     }
    313   }
    314 
    315   static class OuterDeprecatedModule extends AbstractModule {
    316     @Override protected void configure() {
    317       install(new InnerDeprecatedModule());
    318     }
    319   }
    320   static class InnerDeprecatedModule extends AbstractModule {
    321     @Override protected void configure() {
    322       bindScope(Deprecated.class, Scopes.NO_SCOPE);
    323     }
    324   }
    325   public void testBindScopeToAnnotationWithoutScopeAnnotation() {
    326     try {
    327       Guice.createInjector(new OuterDeprecatedModule());
    328       fail();
    329     } catch (CreationException expected) {
    330       assertContains(expected.getMessage(),
    331           "1) Please annotate " + Deprecated.class.getName() + " with @ScopeAnnotation.",
    332           "at " + InnerDeprecatedModule.class.getName() + getDeclaringSourcePart(getClass()),
    333           asModuleChain(OuterDeprecatedModule.class, InnerDeprecatedModule.class));
    334     }
    335   }
    336 
    337   static class OuterScopeModule extends AbstractModule {
    338     @Override protected void configure() {
    339       install(new CustomNoScopeModule());
    340       install(new CustomSingletonModule());
    341     }
    342   }
    343   static class CustomNoScopeModule extends AbstractModule {
    344     @Override protected void configure() {
    345       bindScope(CustomScoped.class, Scopes.NO_SCOPE);
    346     }
    347   }
    348   static class CustomSingletonModule extends AbstractModule {
    349     @Override protected void configure() {
    350       bindScope(CustomScoped.class, Scopes.SINGLETON);
    351     }
    352   }
    353 
    354   public void testBindScopeTooManyTimes() {
    355     try {
    356       Guice.createInjector(new OuterScopeModule());
    357       fail();
    358     } catch (CreationException expected) {
    359       assertContains(expected.getMessage(),
    360           "1) Scope Scopes.NO_SCOPE is already bound to " + CustomScoped.class.getName()
    361               + " at " + CustomNoScopeModule.class.getName() + getDeclaringSourcePart(getClass()),
    362           asModuleChain(OuterScopeModule.class, CustomNoScopeModule.class),
    363           "Cannot bind Scopes.SINGLETON.",
    364           "at " + ScopesTest.class.getName(), getDeclaringSourcePart(getClass()),
    365           asModuleChain(OuterScopeModule.class, CustomSingletonModule.class));
    366     }
    367   }
    368 
    369   public void testBindDuplicateScope() {
    370     Injector injector = Guice.createInjector(new AbstractModule() {
    371       @Override protected void configure() {
    372         bindScope(CustomScoped.class, Scopes.SINGLETON);
    373         bindScope(CustomScoped.class, Scopes.SINGLETON);
    374       }
    375     });
    376 
    377     assertSame(
    378         injector.getInstance(AnnotatedCustomScoped.class),
    379         injector.getInstance(AnnotatedCustomScoped.class));
    380   }
    381 
    382   public void testDuplicateScopeAnnotations() {
    383     Injector injector = Guice.createInjector(new AbstractModule() {
    384       @Override protected void configure() {
    385         bindScope(CustomScoped.class, Scopes.NO_SCOPE);
    386       }
    387     });
    388 
    389     try {
    390       injector.getInstance(SingletonAndCustomScoped.class);
    391       fail();
    392     } catch (ConfigurationException expected) {
    393       assertContains(expected.getMessage(),
    394           "1) More than one scope annotation was found: ",
    395           "while locating " + SingletonAndCustomScoped.class.getName());
    396     }
    397   }
    398 
    399   public void testNullScopedAsASingleton() {
    400     Injector injector = Guice.createInjector(new AbstractModule() {
    401       @Override
    402       protected void configure() {}
    403 
    404       final Iterator<String> values = Arrays.asList(null, "A").iterator();
    405 
    406       @Provides @Singleton String provideString() {
    407          return values.next();
    408       }
    409     });
    410 
    411     assertNull(injector.getInstance(String.class));
    412     assertNull(injector.getInstance(String.class));
    413     assertNull(injector.getInstance(String.class));
    414   }
    415 
    416   class RememberProviderScope implements Scope {
    417     final Map<Key<?>, Provider<?>> providers = Maps.newHashMap();
    418     public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
    419       providers.put(key, unscoped);
    420       return unscoped;
    421     }
    422   }
    423 
    424   public void testSingletonAnnotationOnParameterizedType() {
    425     Injector injector = Guice.createInjector();
    426     assertSame(injector.getInstance(new Key<Injected<String>>() {}),
    427         injector.getInstance(new Key<Injected<String>>() {}));
    428     assertSame(injector.getInstance(new Key<In<Integer>>() {}),
    429         injector.getInstance(new Key<In<Short>>() {}));
    430   }
    431 
    432   @ImplementedBy(Injected.class) public interface In<T> {}
    433   @Singleton public static class Injected<T>  implements In<T> {}
    434 
    435   @Target({ ElementType.TYPE, ElementType.METHOD })
    436   @Retention(RUNTIME)
    437   @ScopeAnnotation
    438   public @interface CustomScoped {}
    439 
    440   static final Scope CUSTOM_SCOPE = new Scope() {
    441     public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
    442       return Scopes.SINGLETON.scope(key, unscoped);
    443     }
    444   };
    445 
    446   @Target({ ElementType.TYPE, ElementType.METHOD })
    447   @ScopeAnnotation
    448   public @interface NotRuntimeRetainedScoped {}
    449 
    450   @CustomScoped
    451   static class AnnotatedCustomScoped {}
    452 
    453   @Singleton
    454   static class AnnotatedSingleton {
    455     static int nextInstanceId;
    456     final int instanceId = nextInstanceId++;
    457   }
    458 
    459   static class BoundAsSingleton {
    460     static int nextInstanceId;
    461     final int instanceId = nextInstanceId++;
    462   }
    463 
    464   static class EagerSingleton {
    465     static int nextInstanceId;
    466     final int instanceId = nextInstanceId++;
    467   }
    468 
    469   interface LinkedSingleton {}
    470 
    471   @Singleton
    472   static class RealLinkedSingleton implements LinkedSingleton {
    473     static int nextInstanceId;
    474     final int instanceId = nextInstanceId++;
    475   }
    476 
    477   static class DependsOnJustInTimeSingleton {
    478     @Inject JustInTimeSingleton justInTimeSingleton;
    479   }
    480 
    481   @Singleton
    482   static class JustInTimeSingleton {
    483     static int nextInstanceId;
    484     final int instanceId = nextInstanceId++;
    485   }
    486 
    487   static class NotASingleton {
    488     static int nextInstanceId;
    489     final int instanceId = nextInstanceId++;
    490   }
    491 
    492   @SuppressWarnings("MoreThanOneScopeAnnotationOnClass") // suppress compiler error for testing
    493   @Singleton
    494   @CustomScoped
    495   static class SingletonAndCustomScoped {}
    496 
    497   @ImplementedBy(Implementation.class)
    498   static interface ImplementedBySingleton {}
    499 
    500   @ProvidedBy(ImplementationProvider.class)
    501   static class ProvidedBySingleton {
    502     static int nextInstanceId;
    503     final int instanceId = nextInstanceId++;
    504   }
    505 
    506   static class Implementation implements ImplementedBySingleton {
    507     static int nextInstanceId;
    508     final int instanceId = nextInstanceId++;
    509   }
    510 
    511   static class ImplementationProvider implements Provider<ProvidedBySingleton> {
    512     public ProvidedBySingleton get() {
    513       return new ProvidedBySingleton();
    514     }
    515   }
    516 
    517   public void testScopeThatGetsAnUnrelatedObject() {
    518     Injector injector = Guice.createInjector(new AbstractModule() {
    519       @Override protected void configure() {
    520         bind(B.class);
    521         bind(C.class);
    522         ProviderGetScope providerGetScope = new ProviderGetScope();
    523         requestInjection(providerGetScope);
    524         bindScope(CustomScoped.class, providerGetScope);
    525       }
    526     });
    527 
    528     injector.getInstance(C.class);
    529   }
    530 
    531   class ProviderGetScope implements Scope {
    532     @Inject Provider<B> bProvider;
    533 
    534     public <T> Provider<T> scope(Key<T> key, final Provider<T> unscoped) {
    535       return new Provider<T>() {
    536         public T get() {
    537           bProvider.get();
    538           return unscoped.get();
    539         }
    540       };
    541     }
    542   }
    543 
    544   public void testIsSingletonPositive() {
    545     final Key<String> a = Key.get(String.class, named("A"));
    546     final Key<String> b = Key.get(String.class, named("B"));
    547     final Key<String> c = Key.get(String.class, named("C"));
    548     final Key<String> d = Key.get(String.class, named("D"));
    549     final Key<String> e = Key.get(String.class, named("E"));
    550     final Key<String> f = Key.get(String.class, named("F"));
    551     final Key<String> g = Key.get(String.class, named("G"));
    552     final Key<Object> h = Key.get(Object.class, named("H"));
    553     final Key<String> i = Key.get(String.class, named("I"));
    554 
    555     Module singletonBindings = new AbstractModule() {
    556       @Override protected void configure() {
    557         bind(a).to(b);
    558         bind(b).to(c);
    559         bind(c).toProvider(Providers.of("c")).in(Scopes.SINGLETON);
    560         bind(d).toInstance("d");
    561         bind(e).toProvider(Providers.of("e")).asEagerSingleton();
    562         bind(f).toProvider(Providers.of("f")).in(Singleton.class);
    563         bind(h).to(AnnotatedSingleton.class);
    564         install(new PrivateModule() {
    565           @Override protected void configure() {
    566             bind(i).toProvider(Providers.of("i")).in(Singleton.class);
    567             expose(i);
    568           }
    569         });
    570       }
    571 
    572       @Provides @Named("G") @Singleton String provideG() {
    573         return "g";
    574       }
    575     };
    576 
    577     @SuppressWarnings("unchecked") // we know the module contains only bindings
    578     List<Element> moduleBindings = Elements.getElements(singletonBindings);
    579     ImmutableMap<Key<?>, Binding<?>> map = indexBindings(moduleBindings);
    580     assertFalse(Scopes.isSingleton(map.get(a))); // linked bindings are not followed by modules
    581     assertFalse(Scopes.isSingleton(map.get(b)));
    582     assertTrue(Scopes.isSingleton(map.get(c)));
    583     assertTrue(Scopes.isSingleton(map.get(d)));
    584     assertTrue(Scopes.isSingleton(map.get(e)));
    585     assertTrue(Scopes.isSingleton(map.get(f)));
    586     assertTrue(Scopes.isSingleton(map.get(g)));
    587     assertFalse(Scopes.isSingleton(map.get(h))); // annotated classes are not followed by modules
    588     assertTrue(Scopes.isSingleton(map.get(i)));
    589 
    590     Injector injector = Guice.createInjector(singletonBindings);
    591     assertTrue(Scopes.isSingleton(injector.getBinding(a)));
    592     assertTrue(Scopes.isSingleton(injector.getBinding(b)));
    593     assertTrue(Scopes.isSingleton(injector.getBinding(c)));
    594     assertTrue(Scopes.isSingleton(injector.getBinding(d)));
    595     assertTrue(Scopes.isSingleton(injector.getBinding(e)));
    596     assertTrue(Scopes.isSingleton(injector.getBinding(f)));
    597     assertTrue(Scopes.isSingleton(injector.getBinding(g)));
    598     assertTrue(Scopes.isSingleton(injector.getBinding(h)));
    599     assertTrue(Scopes.isSingleton(injector.getBinding(i)));
    600   }
    601 
    602   public void testIsSingletonNegative() {
    603     final Key<String> a = Key.get(String.class, named("A"));
    604     final Key<String> b = Key.get(String.class, named("B"));
    605     final Key<String> c = Key.get(String.class, named("C"));
    606     final Key<String> d = Key.get(String.class, named("D"));
    607     final Key<String> e = Key.get(String.class, named("E"));
    608     final Key<String> f = Key.get(String.class, named("F"));
    609 
    610     Module singletonBindings = new AbstractModule() {
    611       @Override protected void configure() {
    612         bind(a).to(b);
    613         bind(b).to(c);
    614         bind(c).toProvider(Providers.of("c")).in(Scopes.NO_SCOPE);
    615         bind(d).toProvider(Providers.of("d")).in(CustomScoped.class);
    616         bindScope(CustomScoped.class, Scopes.NO_SCOPE);
    617         install(new PrivateModule() {
    618           @Override protected void configure() {
    619             bind(f).toProvider(Providers.of("f")).in(CustomScoped.class);
    620             expose(f);
    621           }
    622         });
    623       }
    624 
    625       @Provides @Named("E") @CustomScoped String provideE() {
    626         return "e";
    627       }
    628     };
    629 
    630     @SuppressWarnings("unchecked") // we know the module contains only bindings
    631     List<Element> moduleBindings = Elements.getElements(singletonBindings);
    632     ImmutableMap<Key<?>, Binding<?>> map = indexBindings(moduleBindings);
    633     assertFalse(Scopes.isSingleton(map.get(a)));
    634     assertFalse(Scopes.isSingleton(map.get(b)));
    635     assertFalse(Scopes.isSingleton(map.get(c)));
    636     assertFalse(Scopes.isSingleton(map.get(d)));
    637     assertFalse(Scopes.isSingleton(map.get(e)));
    638     assertFalse(Scopes.isSingleton(map.get(f)));
    639 
    640     Injector injector = Guice.createInjector(singletonBindings);
    641     assertFalse(Scopes.isSingleton(injector.getBinding(a)));
    642     assertFalse(Scopes.isSingleton(injector.getBinding(b)));
    643     assertFalse(Scopes.isSingleton(injector.getBinding(c)));
    644     assertFalse(Scopes.isSingleton(injector.getBinding(d)));
    645     assertFalse(Scopes.isSingleton(injector.getBinding(e)));
    646     assertFalse(Scopes.isSingleton(injector.getBinding(f)));
    647   }
    648 
    649   public void testIsScopedPositive() {
    650     final Key<String> a = Key.get(String.class, named("A"));
    651     final Key<String> b = Key.get(String.class, named("B"));
    652     final Key<String> c = Key.get(String.class, named("C"));
    653     final Key<String> d = Key.get(String.class, named("D"));
    654     final Key<String> e = Key.get(String.class, named("E"));
    655     final Key<Object> f = Key.get(Object.class, named("F"));
    656     final Key<String> g = Key.get(String.class, named("G"));
    657 
    658     Module customBindings = new AbstractModule() {
    659       @Override protected void configure() {
    660         bindScope(CustomScoped.class, CUSTOM_SCOPE);
    661         bind(a).to(b);
    662         bind(b).to(c);
    663         bind(c).toProvider(Providers.of("c")).in(CUSTOM_SCOPE);
    664         bind(d).toProvider(Providers.of("d")).in(CustomScoped.class);
    665         bind(f).to(AnnotatedCustomScoped.class);
    666         install(new PrivateModule() {
    667           @Override protected void configure() {
    668             bind(g).toProvider(Providers.of("g")).in(CustomScoped.class);
    669             expose(g);
    670           }
    671         });
    672       }
    673 
    674       @Provides @Named("E") @CustomScoped String provideE() {
    675         return "e";
    676       }
    677     };
    678 
    679     @SuppressWarnings("unchecked") // we know the module contains only bindings
    680     List<Element> moduleBindings = Elements.getElements(customBindings);
    681     ImmutableMap<Key<?>, Binding<?>> map = indexBindings(moduleBindings);
    682     assertFalse(isCustomScoped(map.get(a))); // linked bindings are not followed by modules
    683     assertFalse(isCustomScoped(map.get(b)));
    684     assertTrue(isCustomScoped(map.get(c)));
    685     assertTrue(isCustomScoped(map.get(d)));
    686     assertTrue(isCustomScoped(map.get(e)));
    687     assertFalse(isCustomScoped(map.get(f))); // annotated classes are not followed by modules
    688     assertTrue(isCustomScoped(map.get(g)));
    689 
    690     Injector injector = Guice.createInjector(customBindings);
    691     assertTrue(isCustomScoped(injector.getBinding(a)));
    692     assertTrue(isCustomScoped(injector.getBinding(b)));
    693     assertTrue(isCustomScoped(injector.getBinding(c)));
    694     assertTrue(isCustomScoped(injector.getBinding(d)));
    695     assertTrue(isCustomScoped(injector.getBinding(e)));
    696     assertTrue(isCustomScoped(injector.getBinding(f)));
    697     assertTrue(isCustomScoped(injector.getBinding(g)));
    698   }
    699 
    700   public void testIsScopedNegative() {
    701     final Key<String> a = Key.get(String.class, named("A"));
    702     final Key<String> b = Key.get(String.class, named("B"));
    703     final Key<String> c = Key.get(String.class, named("C"));
    704     final Key<String> d = Key.get(String.class, named("D"));
    705     final Key<String> e = Key.get(String.class, named("E"));
    706     final Key<String> f = Key.get(String.class, named("F"));
    707     final Key<String> g = Key.get(String.class, named("G"));
    708     final Key<String> h = Key.get(String.class, named("H"));
    709 
    710     Module customBindings = new AbstractModule() {
    711       @Override protected void configure() {
    712         bind(a).to(b);
    713         bind(b).to(c);
    714         bind(c).toProvider(Providers.of("c")).in(Scopes.NO_SCOPE);
    715         bind(d).toProvider(Providers.of("d")).in(Singleton.class);
    716         install(new PrivateModule() {
    717           @Override
    718           protected void configure() {
    719             bind(f).toProvider(Providers.of("f")).in(Singleton.class);
    720             expose(f);
    721           }
    722         });
    723         bind(g).toInstance("g");
    724         bind(h).toProvider(Providers.of("h")).asEagerSingleton();
    725       }
    726 
    727       @Provides @Named("E") @Singleton String provideE() {
    728         return "e";
    729       }
    730     };
    731 
    732     @SuppressWarnings("unchecked") // we know the module contains only bindings
    733     List<Element> moduleBindings = Elements.getElements(customBindings);
    734     ImmutableMap<Key<?>, Binding<?>> map = indexBindings(moduleBindings);
    735     assertFalse(isCustomScoped(map.get(a)));
    736     assertFalse(isCustomScoped(map.get(b)));
    737     assertFalse(isCustomScoped(map.get(c)));
    738     assertFalse(isCustomScoped(map.get(d)));
    739     assertFalse(isCustomScoped(map.get(e)));
    740     assertFalse(isCustomScoped(map.get(f)));
    741     assertFalse(isCustomScoped(map.get(g)));
    742     assertFalse(isCustomScoped(map.get(h)));
    743 
    744     Injector injector = Guice.createInjector(customBindings);
    745     assertFalse(isCustomScoped(injector.getBinding(a)));
    746     assertFalse(isCustomScoped(injector.getBinding(b)));
    747     assertFalse(isCustomScoped(injector.getBinding(c)));
    748     assertFalse(isCustomScoped(injector.getBinding(d)));
    749     assertFalse(isCustomScoped(injector.getBinding(e)));
    750     assertFalse(isCustomScoped(injector.getBinding(f)));
    751     assertFalse(isCustomScoped(injector.getBinding(g)));
    752     assertFalse(isCustomScoped(injector.getBinding(h)));
    753   }
    754 
    755   private boolean isCustomScoped(Binding<?> binding) {
    756     return Scopes.isScoped(binding, CUSTOM_SCOPE, CustomScoped.class);
    757   }
    758 
    759   ImmutableMap<Key<?>, Binding<?>> indexBindings(Iterable<Element> elements) {
    760     ImmutableMap.Builder<Key<?>, Binding<?>> builder = ImmutableMap.builder();
    761     for (Element element : elements) {
    762       if (element instanceof Binding) {
    763         Binding<?> binding = (Binding<?>) element;
    764         builder.put(binding.getKey(), binding);
    765       } else if (element instanceof PrivateElements) {
    766         PrivateElements privateElements = (PrivateElements)element;
    767         Map<Key<?>, Binding<?>> privateBindings = indexBindings(privateElements.getElements());
    768         for(Key<?> exposed : privateElements.getExposedKeys()) {
    769           builder.put(exposed, privateBindings.get(exposed));
    770         }
    771       }
    772     }
    773     return builder.build();
    774   }
    775 
    776   @Singleton
    777   static class ThrowingSingleton {
    778     static int nextInstanceId;
    779     final int instanceId = nextInstanceId++;
    780 
    781     ThrowingSingleton() {
    782       if (instanceId == 0) {
    783         throw new RuntimeException();
    784       }
    785     }
    786   }
    787 
    788   public void testSingletonConstructorThrows() {
    789     Injector injector = Guice.createInjector();
    790 
    791     try {
    792       injector.getInstance(ThrowingSingleton.class);
    793       fail();
    794     } catch (ProvisionException expected) {
    795     }
    796 
    797     // this behaviour is unspecified. If we change Guice to re-throw the exception, this test
    798     // should be changed
    799     injector.getInstance(ThrowingSingleton.class);
    800     assertEquals(2, ThrowingSingleton.nextInstanceId);
    801   }
    802 
    803   /**
    804    * Should only be created by {@link SBarrierProvider}.
    805    *
    806    * <p>{@code S} stands for synchronization.
    807    *
    808    * @see SBarrierProvider
    809    */
    810   static class S {
    811 
    812     private S(int preventInjectionWithoutProvider) {
    813     }
    814   }
    815 
    816   /**
    817    * Provides all the instances of S simultaneously using {@link CyclicBarrier} with {@code
    818    * nThreads}. Intended to be used for threads synchronization during injection.
    819    */
    820   static class SBarrierProvider implements Provider<S> {
    821 
    822     final CyclicBarrier barrier;
    823     volatile boolean barrierPassed = false;
    824 
    825     SBarrierProvider(int nThreads) {
    826       barrier = new CyclicBarrier(nThreads, new Runnable() {
    827         public void run() {
    828           // would finish before returning from await() for any thread
    829           barrierPassed = true;
    830         }
    831       });
    832     }
    833 
    834     public S get() {
    835       try {
    836         if (!barrierPassed) {
    837           // only if we're triggering barrier for the first time
    838           barrier.await(DEADLOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS);
    839         }
    840       } catch (Exception e) {
    841         throw new RuntimeException(e);
    842       }
    843       return new S(0);
    844     }
    845   }
    846 
    847   /**
    848    * Tests that different injectors should not affect each other.
    849    *
    850    * <p>This creates a second thread to work in parallel, to create two instances of
    851    * {@link S} as the same time. If the lock if not granular enough (i.e. JVM-wide)
    852    * then they would block each other creating a deadlock and await timeout.
    853    */
    854 
    855   public void testInjectorsDontDeadlockOnSingletons() throws Exception {
    856     final Provider<S> provider = new SBarrierProvider(2);
    857     final Injector injector = Guice.createInjector(new AbstractModule() {
    858       @Override
    859       protected void configure() {
    860         Thread.currentThread().setName("S.class[1]");
    861         bind(S.class).toProvider(provider).in(Scopes.SINGLETON);
    862       }
    863     });
    864     final Injector secondInjector = Guice.createInjector(new AbstractModule() {
    865       @Override
    866       protected void configure() {
    867         Thread.currentThread().setName("S.class[2]");
    868         bind(S.class).toProvider(provider).in(Scopes.SINGLETON);
    869       }
    870     });
    871 
    872     Future<S> secondThreadResult = Executors.newSingleThreadExecutor().submit(new Callable<S>() {
    873       public S call() {
    874         return secondInjector.getInstance(S.class);
    875       }
    876     });
    877 
    878     S firstS = injector.getInstance(S.class);
    879     S secondS = secondThreadResult.get();
    880 
    881     assertNotSame(firstS, secondS);
    882   }
    883 
    884   @ImplementedBy(GImpl.class)
    885   interface G {
    886 
    887   }
    888 
    889   @Singleton
    890   static class GImpl implements G {
    891 
    892     final H h;
    893 
    894     /**
    895      * Relies on Guice implementation to inject S first and H later, which provides a barrier .
    896      */
    897     @Inject
    898     GImpl(S synchronizationBarrier, H h) {
    899       this.h = h;
    900     }
    901   }
    902 
    903   @ImplementedBy(HImpl.class)
    904   interface H {
    905 
    906   }
    907 
    908   @Singleton
    909   static class HImpl implements H {
    910 
    911     final G g;
    912 
    913     /**
    914      * Relies on Guice implementation to inject S first and G later, which provides a barrier .
    915      */
    916     @Inject
    917     HImpl(S synchronizationBarrier, G g) throws Exception {
    918       this.g = g;
    919     }
    920   }
    921 
    922   /**
    923    * Tests that injector can create two singletons with circular dependency in parallel.
    924    *
    925    * <p>This creates two threads to work in parallel, to create instances of
    926    * {@link G} and {@link H}. Creation is synchronized by injection of {@link S},
    927    * first thread would block until second would be inside a singleton creation as well.
    928    *
    929    * <p>Both instances are created by sibling injectors, that share singleton scope.
    930    * Verifies that exactly one circular proxy object is created.
    931    */
    932 
    933   public void testSiblingInjectorGettingCircularSingletonsOneCircularProxy() throws Exception {
    934     final Provider<S> provider = new SBarrierProvider(2);
    935     final Injector injector = Guice.createInjector(new AbstractModule() {
    936       @Override
    937       protected void configure() {
    938         bind(S.class).toProvider(provider);
    939       }
    940     });
    941 
    942     Future<G> firstThreadResult = Executors.newSingleThreadExecutor().submit(new Callable<G>() {
    943       public G call() {
    944         Thread.currentThread().setName("G.class");
    945         return injector.createChildInjector().getInstance(G.class);
    946       }
    947     });
    948     Future<H> secondThreadResult = Executors.newSingleThreadExecutor().submit(new Callable<H>() {
    949       public H call() {
    950         Thread.currentThread().setName("H.class");
    951         return injector.createChildInjector().getInstance(H.class);
    952       }
    953     });
    954 
    955     // using separate threads to avoid potential deadlock on the main thread
    956     // waiting twice as much to be sure that both would time out in their respective barriers
    957     GImpl g = (GImpl) firstThreadResult.get(DEADLOCK_TIMEOUT_SECONDS * 3, TimeUnit.SECONDS);
    958     HImpl h = (HImpl) secondThreadResult.get(DEADLOCK_TIMEOUT_SECONDS * 3, TimeUnit.SECONDS);
    959 
    960     // Check that G and H created are not proxied
    961     assertTrue(!Scopes.isCircularProxy(g) && !Scopes.isCircularProxy(h));
    962 
    963     // Check that we have no more than one circular proxy created
    964     assertFalse(Scopes.isCircularProxy(g.h) && Scopes.isCircularProxy(h.g));
    965 
    966     // Check that non-proxy variable points to another singleton
    967     assertTrue(g.h == h || h.g == g);
    968 
    969     // Check correct proxy initialization as default equals implementation would
    970     assertEquals(g.h, h);
    971     assertEquals(h.g, g);
    972   }
    973 
    974   @Singleton
    975   static class I0 {
    976 
    977     /**
    978      * Relies on Guice implementation to inject S first, which provides a barrier .
    979      */
    980     @Inject
    981     I0(I1 i) {
    982     }
    983   }
    984 
    985   @Singleton
    986   static class I1 {
    987 
    988     /**
    989      * Relies on Guice implementation to inject S first, which provides a barrier .
    990      */
    991     @Inject
    992     I1(S synchronizationBarrier, I2 i) {
    993     }
    994   }
    995 
    996   @Singleton
    997   static class I2 {
    998 
    999     /**
   1000      * Relies on Guice implementation to inject S first, which provides a barrier .
   1001      */
   1002     @Inject
   1003     I2(J1 j) {
   1004     }
   1005   }
   1006 
   1007   @Singleton
   1008   static class J0 {
   1009 
   1010     /**
   1011      * Relies on Guice implementation to inject S first, which provides a barrier .
   1012      */
   1013     @Inject
   1014     J0(J1 j) {
   1015     }
   1016   }
   1017 
   1018   @Singleton
   1019   static class J1 {
   1020 
   1021     /**
   1022      * Relies on Guice implementation to inject S first, which provides a barrier .
   1023      */
   1024     @Inject
   1025     J1(S synchronizationBarrier, J2 j) {
   1026     }
   1027   }
   1028 
   1029   @Singleton
   1030   static class J2 {
   1031 
   1032     /**
   1033      * Relies on Guice implementation to inject S first, which provides a barrier .
   1034      */
   1035     @Inject
   1036     J2(K1 k) {
   1037     }
   1038   }
   1039 
   1040   @Singleton
   1041   static class K0 {
   1042 
   1043     /**
   1044      * Relies on Guice implementation to inject S first, which provides a barrier .
   1045      */
   1046     @Inject
   1047     K0(K1 k) {
   1048     }
   1049   }
   1050 
   1051   @Singleton
   1052   static class K1 {
   1053 
   1054     /**
   1055      * Relies on Guice implementation to inject S first, which provides a barrier .
   1056      */
   1057     @Inject
   1058     K1(S synchronizationBarrier, K2 k) {
   1059     }
   1060   }
   1061 
   1062   @Singleton
   1063   static class K2 {
   1064 
   1065     /**
   1066      * Relies on Guice implementation to inject S first, which provides a barrier .
   1067      */
   1068     @Inject
   1069     K2(I1 i) {
   1070     }
   1071   }
   1072 
   1073   /**
   1074    * Check that circular dependencies on non-interfaces are correctly resolved in multi-threaded
   1075    * case. And that an error message constructed is a good one.
   1076    *
   1077    * <p>I0 -> I1 -> I2 -> J1 and J0 -> J1 -> J2 -> K1 and K0 -> K1 -> K2,
   1078    * where I1, J1 and K1 are created in parallel.
   1079    *
   1080    * <p>Creation is synchronized by injection of {@link S}, first thread would block until second
   1081    * would be inside a singleton creation as well.
   1082    *
   1083    * <p>Verifies that provision results in an error, that spans two threads and
   1084    * has a dependency cycle.
   1085    */
   1086 
   1087   public void testUnresolvableSingletonCircularDependencyErrorMessage() throws Exception {
   1088     final Provider<S> provider = new SBarrierProvider(3);
   1089     final Injector injector = Guice.createInjector(new AbstractModule() {
   1090       @Override
   1091       protected void configure() {
   1092         bind(S.class).toProvider(provider);
   1093       }
   1094     });
   1095 
   1096     Future<I0> firstThreadResult = Executors.newSingleThreadExecutor().submit(new Callable<I0>() {
   1097       public I0 call() {
   1098         Thread.currentThread().setName("I0.class");
   1099         return injector.getInstance(I0.class);
   1100       }
   1101     });
   1102     Future<J0> secondThreadResult = Executors.newSingleThreadExecutor().submit(new Callable<J0>() {
   1103       public J0 call() {
   1104         Thread.currentThread().setName("J0.class");
   1105         return injector.getInstance(J0.class);
   1106       }
   1107     });
   1108     Future<K0> thirdThreadResult = Executors.newSingleThreadExecutor().submit(new Callable<K0>() {
   1109       public K0 call() {
   1110         Thread.currentThread().setName("K0.class");
   1111         return injector.getInstance(K0.class);
   1112       }
   1113     });
   1114 
   1115     // using separate threads to avoid potential deadlock on the main thread
   1116     // waiting twice as much to be sure that both would time out in their respective barriers
   1117     Throwable firstException = null;
   1118     Throwable secondException = null;
   1119     Throwable thirdException = null;
   1120     try {
   1121       firstThreadResult.get(DEADLOCK_TIMEOUT_SECONDS * 3, TimeUnit.SECONDS);
   1122       fail();
   1123     } catch (ExecutionException e) {
   1124       firstException = e.getCause();
   1125     }
   1126     try {
   1127       secondThreadResult.get(DEADLOCK_TIMEOUT_SECONDS * 3, TimeUnit.SECONDS);
   1128       fail();
   1129     } catch (ExecutionException e) {
   1130       secondException = e.getCause();
   1131     }
   1132     try {
   1133       thirdThreadResult.get(DEADLOCK_TIMEOUT_SECONDS * 3, TimeUnit.SECONDS);
   1134       fail();
   1135     } catch (ExecutionException e) {
   1136       thirdException = e.getCause();
   1137     }
   1138 
   1139     // verification of error messages generated
   1140     assertEquals(firstException.getClass(), ProvisionException.class);
   1141     assertEquals(secondException.getClass(), ProvisionException.class);
   1142     assertEquals(thirdException.getClass(), ProvisionException.class);
   1143     List<String> errorMessages = Lists.newArrayList(
   1144         String.format("%s\n%s\n%s",
   1145             firstException.getMessage(), secondException.getMessage(), thirdException.getMessage())
   1146             .split("\\n\\n"));
   1147     Collections.sort(errorMessages, new Comparator<String>() {
   1148       @Override
   1149       public int compare(String s1, String s2) {
   1150         return s2.length() - s1.length();
   1151       }
   1152     });
   1153     // this is brittle, but turns out that second to longest message spans all threads
   1154     String errorMessage = errorMessages.get(1);
   1155     assertContains(errorMessage,
   1156         "Encountered circular dependency spanning several threads. Tried proxying "
   1157             + this.getClass().getName());
   1158     assertFalse("Both I0 and J0 can not be a part of a dependency cycle",
   1159         errorMessage.contains(I0.class.getName()) && errorMessage.contains(J0.class.getName()));
   1160     assertFalse("Both J0 and K0 can not be a part of a dependency cycle",
   1161         errorMessage.contains(J0.class.getName()) && errorMessage.contains(K0.class.getName()));
   1162     assertFalse("Both K0 and I0 can not be a part of a dependency cycle",
   1163         errorMessage.contains(K0.class.getName()) && errorMessage.contains(I0.class.getName()));
   1164 
   1165     List<String> firstErrorLineForThread = new ArrayList<String>();
   1166     boolean addNextLine = false;
   1167     for (String errorLine : errorMessage.split("\\n")) {
   1168       if (errorLine.contains("in thread")) {
   1169         addNextLine = true;
   1170         firstErrorLineForThread.add(errorLine);
   1171       } else if (addNextLine) {
   1172         addNextLine = false;
   1173         firstErrorLineForThread.add(errorLine);
   1174       }
   1175     }
   1176     assertEquals("we expect to see [T1, $A, T2, $B, T3, $C, T1, $A]",
   1177         8, firstErrorLineForThread.size());
   1178     assertEquals("first four elements should be different",
   1179         6, new HashSet<String>(firstErrorLineForThread.subList(0, 6)).size());
   1180     assertEquals(firstErrorLineForThread.get(6), firstErrorLineForThread.get(0));
   1181     assertEquals(firstErrorLineForThread.get(7), firstErrorLineForThread.get(1));
   1182     assertFalse("K0 thread could not be blocked by J0",
   1183         firstErrorLineForThread.get(0).contains("J0")
   1184             && firstErrorLineForThread.get(2).contains("K0"));
   1185     assertFalse("J0 thread could not be blocked by I0",
   1186         firstErrorLineForThread.get(0).contains("I0")
   1187             && firstErrorLineForThread.get(2).contains("J0"));
   1188     assertFalse("I0 thread could not be blocked by K0",
   1189         firstErrorLineForThread.get(0).contains("K0")
   1190             && firstErrorLineForThread.get(2).contains("I0"));
   1191   }
   1192 }
   1193