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@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 
setUp()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 
testSingletons()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 
testJustInTimeAnnotatedSingleton()123   public void testJustInTimeAnnotatedSingleton() {
124     Injector injector = Guice.createInjector();
125 
126     assertSame(
127         injector.getInstance(AnnotatedSingleton.class),
128         injector.getInstance(AnnotatedSingleton.class));
129   }
130 
testSingletonIsPerInjector()131   public void testSingletonIsPerInjector() {
132     assertNotSame(
133         Guice.createInjector().getInstance(AnnotatedSingleton.class),
134         Guice.createInjector().getInstance(AnnotatedSingleton.class));
135   }
136 
testOverriddingAnnotation()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 
testScopingAnnotationsOnAbstractTypeViaBind()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 
testScopingAnnotationsOnAbstractTypeIsValidForComponent()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 
testScopingAnnotationsOnAbstractTypeViaImplementedBy()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 
testScopingAnnotationsOnAbstractTypeViaProvidedBy()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> {
get()216     public E get() {
217       return null;
218     }
219   }
220 
testScopeUsedButNotBound()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 
testSingletonsInProductionStage()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 
testSingletonsInDevelopmentStage()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 
testSingletonScopeIsNotSerializable()266   public void testSingletonScopeIsNotSerializable() throws IOException {
267     Asserts.assertNotSerializable(Scopes.SINGLETON);
268   }
269 
testNoScopeIsNotSerializable()270   public void testNoScopeIsNotSerializable() throws IOException {
271     Asserts.assertNotSerializable(Scopes.NO_SCOPE);
272   }
273 
testUnscopedProviderWorksOutsideOfRequestedScope()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 {
configure()293     @Override protected void configure() {
294       install(new InnerRuntimeModule());
295     }
296   }
297   static class InnerRuntimeModule extends AbstractModule {
configure()298     @Override protected void configure() {
299       bindScope(NotRuntimeRetainedScoped.class, Scopes.NO_SCOPE);
300     }
301   }
testScopeAnnotationWithoutRuntimeRetention()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 {
configure()316     @Override protected void configure() {
317       install(new InnerDeprecatedModule());
318     }
319   }
320   static class InnerDeprecatedModule extends AbstractModule {
configure()321     @Override protected void configure() {
322       bindScope(Deprecated.class, Scopes.NO_SCOPE);
323     }
324   }
testBindScopeToAnnotationWithoutScopeAnnotation()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 {
configure()338     @Override protected void configure() {
339       install(new CustomNoScopeModule());
340       install(new CustomSingletonModule());
341     }
342   }
343   static class CustomNoScopeModule extends AbstractModule {
configure()344     @Override protected void configure() {
345       bindScope(CustomScoped.class, Scopes.NO_SCOPE);
346     }
347   }
348   static class CustomSingletonModule extends AbstractModule {
configure()349     @Override protected void configure() {
350       bindScope(CustomScoped.class, Scopes.SINGLETON);
351     }
352   }
353 
testBindScopeTooManyTimes()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 
testBindDuplicateScope()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 
testDuplicateScopeAnnotations()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 
testNullScopedAsASingleton()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();
scope(Key<T> key, Provider<T> unscoped)418     public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
419       providers.put(key, unscoped);
420       return unscoped;
421     }
422   }
423 
testSingletonAnnotationOnParameterizedType()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> {
get()512     public ProvidedBySingleton get() {
513       return new ProvidedBySingleton();
514     }
515   }
516 
testScopeThatGetsAnUnrelatedObject()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 
scope(Key<T> key, final Provider<T> unscoped)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