/core/test/com/google/inject/ScopesTest.java
Java | 1192 lines | 923 code | 161 blank | 108 comment | 23 complexity | dc5125a2017b361216d07303d52de428 MD5 | raw file
- /**
- * Copyright (C) 2006 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.google.inject;
- import static com.google.inject.Asserts.asModuleChain;
- import static com.google.inject.Asserts.assertContains;
- import static com.google.inject.Asserts.getDeclaringSourcePart;
- import static com.google.inject.name.Names.named;
- import static java.lang.annotation.RetentionPolicy.RUNTIME;
- import com.google.common.collect.ImmutableMap;
- import com.google.common.collect.Lists;
- import com.google.common.collect.Maps;
- import com.google.inject.name.Named;
- import com.google.inject.spi.Element;
- import com.google.inject.spi.Elements;
- import com.google.inject.spi.PrivateElements;
- import com.google.inject.util.Providers;
- import junit.framework.TestCase;
- import java.io.IOException;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.Target;
- import java.util.ArrayList;
- import java.util.Arrays;
- import java.util.Collections;
- import java.util.Comparator;
- import java.util.HashSet;
- import java.util.Iterator;
- import java.util.List;
- import java.util.Map;
- import java.util.concurrent.Callable;
- import java.util.concurrent.CyclicBarrier;
- import java.util.concurrent.ExecutionException;
- import java.util.concurrent.Executors;
- import java.util.concurrent.Future;
- import java.util.concurrent.TimeUnit;
- /**
- * @author crazybob@google.com (Bob Lee)
- */
- public class ScopesTest extends TestCase {
- static final long DEADLOCK_TIMEOUT_SECONDS = 1;
- private final AbstractModule singletonsModule = new AbstractModule() {
- @Override protected void configure() {
- bind(BoundAsSingleton.class).in(Scopes.SINGLETON);
- bind(AnnotatedSingleton.class);
- bind(EagerSingleton.class).asEagerSingleton();
- bind(LinkedSingleton.class).to(RealLinkedSingleton.class);
- bind(DependsOnJustInTimeSingleton.class);
- bind(NotASingleton.class);
- bind(ImplementedBySingleton.class).in(Scopes.SINGLETON);
- bind(ProvidedBySingleton.class).in(Scopes.SINGLETON);
- }
- };
- @Override protected void setUp() throws Exception {
- AnnotatedSingleton.nextInstanceId = 0;
- BoundAsSingleton.nextInstanceId = 0;
- EagerSingleton.nextInstanceId = 0;
- RealLinkedSingleton.nextInstanceId = 0;
- JustInTimeSingleton.nextInstanceId = 0;
- NotASingleton.nextInstanceId = 0;
- Implementation.nextInstanceId = 0;
- ProvidedBySingleton.nextInstanceId = 0;
- ThrowingSingleton.nextInstanceId = 0;
- }
- public void testSingletons() {
- Injector injector = Guice.createInjector(singletonsModule);
- assertSame(
- injector.getInstance(BoundAsSingleton.class),
- injector.getInstance(BoundAsSingleton.class));
- assertSame(
- injector.getInstance(AnnotatedSingleton.class),
- injector.getInstance(AnnotatedSingleton.class));
- assertSame(
- injector.getInstance(EagerSingleton.class),
- injector.getInstance(EagerSingleton.class));
- assertSame(
- injector.getInstance(LinkedSingleton.class),
- injector.getInstance(LinkedSingleton.class));
- assertSame(
- injector.getInstance(JustInTimeSingleton.class),
- injector.getInstance(JustInTimeSingleton.class));
- assertNotSame(
- injector.getInstance(NotASingleton.class),
- injector.getInstance(NotASingleton.class));
- assertSame(
- injector.getInstance(ImplementedBySingleton.class),
- injector.getInstance(ImplementedBySingleton.class));
- assertSame(
- injector.getInstance(ProvidedBySingleton.class),
- injector.getInstance(ProvidedBySingleton.class));
- }
- public void testJustInTimeAnnotatedSingleton() {
- Injector injector = Guice.createInjector();
- assertSame(
- injector.getInstance(AnnotatedSingleton.class),
- injector.getInstance(AnnotatedSingleton.class));
- }
- public void testSingletonIsPerInjector() {
- assertNotSame(
- Guice.createInjector().getInstance(AnnotatedSingleton.class),
- Guice.createInjector().getInstance(AnnotatedSingleton.class));
- }
- public void testOverriddingAnnotation() {
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bind(AnnotatedSingleton.class).in(Scopes.NO_SCOPE);
- }
- });
- assertNotSame(
- injector.getInstance(AnnotatedSingleton.class),
- injector.getInstance(AnnotatedSingleton.class));
- }
- public void testScopingAnnotationsOnAbstractTypeViaBind() {
- try {
- Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bind(A.class).to(AImpl.class);
- }
- });
- fail();
- } catch (CreationException expected) {
- assertContains(expected.getMessage(),
- A.class.getName() + " is annotated with " + Singleton.class.getName(),
- "but scope annotations are not supported for abstract types.",
- "at " + A.class.getName() + ".class(ScopesTest.java:");
- }
- }
- @Singleton
- interface A {}
- static class AImpl implements A {}
- @Retention(RUNTIME)
- @interface Component {}
- @Component
- @Singleton
- interface ComponentAnnotationTest {}
- static class ComponentAnnotationTestImpl implements ComponentAnnotationTest {}
- public void testScopingAnnotationsOnAbstractTypeIsValidForComponent() {
- Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bind(ComponentAnnotationTest.class).to(ComponentAnnotationTestImpl.class);
- }
- });
- }
- public void testScopingAnnotationsOnAbstractTypeViaImplementedBy() {
- try {
- Guice.createInjector().getInstance(D.class);
- fail();
- } catch (ConfigurationException expected) {
- assertContains(expected.getMessage(),
- D.class.getName() + " is annotated with " + Singleton.class.getName(),
- "but scope annotations are not supported for abstract types.",
- "at " + D.class.getName() + ".class(ScopesTest.java:");
- }
- }
- @Singleton @ImplementedBy(DImpl.class)
- interface D {}
- static class DImpl implements D {}
- public void testScopingAnnotationsOnAbstractTypeViaProvidedBy() {
- try {
- Guice.createInjector().getInstance(E.class);
- fail();
- } catch (ConfigurationException expected) {
- assertContains(expected.getMessage(),
- E.class.getName() + " is annotated with " + Singleton.class.getName(),
- "but scope annotations are not supported for abstract types.",
- "at " + E.class.getName() + ".class(ScopesTest.java:");
- }
- }
- @Singleton @ProvidedBy(EProvider.class)
- interface E {}
- static class EProvider implements Provider<E> {
- public E get() {
- return null;
- }
- }
- public void testScopeUsedButNotBound() {
- try {
- Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bind(B.class).in(CustomScoped.class);
- bind(C.class);
- }
- });
- fail();
- } catch (CreationException expected) {
- assertContains(expected.getMessage(),
- "1) No scope is bound to " + CustomScoped.class.getName(),
- "at " + getClass().getName(), getDeclaringSourcePart(getClass()),
- "2) No scope is bound to " + CustomScoped.class.getName(),
- "at " + C.class.getName() + ".class");
- }
- }
- static class B {}
- @CustomScoped
- static class C {}
- public void testSingletonsInProductionStage() {
- Guice.createInjector(Stage.PRODUCTION, singletonsModule);
- assertEquals(1, AnnotatedSingleton.nextInstanceId);
- assertEquals(1, BoundAsSingleton.nextInstanceId);
- assertEquals(1, EagerSingleton.nextInstanceId);
- assertEquals(1, RealLinkedSingleton.nextInstanceId);
- assertEquals(1, JustInTimeSingleton.nextInstanceId);
- assertEquals(0, NotASingleton.nextInstanceId);
- }
- public void testSingletonsInDevelopmentStage() {
- Guice.createInjector(Stage.DEVELOPMENT, singletonsModule);
- assertEquals(0, AnnotatedSingleton.nextInstanceId);
- assertEquals(0, BoundAsSingleton.nextInstanceId);
- assertEquals(1, EagerSingleton.nextInstanceId);
- assertEquals(0, RealLinkedSingleton.nextInstanceId);
- assertEquals(0, JustInTimeSingleton.nextInstanceId);
- assertEquals(0, NotASingleton.nextInstanceId);
- }
- public void testSingletonScopeIsNotSerializable() throws IOException {
- Asserts.assertNotSerializable(Scopes.SINGLETON);
- }
- public void testNoScopeIsNotSerializable() throws IOException {
- Asserts.assertNotSerializable(Scopes.NO_SCOPE);
- }
- public void testUnscopedProviderWorksOutsideOfRequestedScope() {
- final RememberProviderScope scope = new RememberProviderScope();
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bindScope(CustomScoped.class, scope);
- bind(List.class).to(ArrayList.class).in(CustomScoped.class);
- }
- });
- injector.getInstance(List.class);
- Provider<?> listProvider = scope.providers.get(Key.get(List.class));
- // this line fails with a NullPointerException because the Providers
- // passed to Scope.scope() don't work outside of the scope() method.
- assertTrue(listProvider.get() instanceof ArrayList);
- }
- static class OuterRuntimeModule extends AbstractModule {
- @Override protected void configure() {
- install(new InnerRuntimeModule());
- }
- }
- static class InnerRuntimeModule extends AbstractModule {
- @Override protected void configure() {
- bindScope(NotRuntimeRetainedScoped.class, Scopes.NO_SCOPE);
- }
- }
- public void testScopeAnnotationWithoutRuntimeRetention() {
- try {
- Guice.createInjector(new OuterRuntimeModule());
- fail();
- } catch (CreationException expected) {
- assertContains(expected.getMessage(),
- "1) Please annotate " + NotRuntimeRetainedScoped.class.getName()
- + " with @Retention(RUNTIME).",
- "at " + InnerRuntimeModule.class.getName() + getDeclaringSourcePart(getClass()),
- asModuleChain(OuterRuntimeModule.class, InnerRuntimeModule.class));
- }
- }
- static class OuterDeprecatedModule extends AbstractModule {
- @Override protected void configure() {
- install(new InnerDeprecatedModule());
- }
- }
- static class InnerDeprecatedModule extends AbstractModule {
- @Override protected void configure() {
- bindScope(Deprecated.class, Scopes.NO_SCOPE);
- }
- }
- public void testBindScopeToAnnotationWithoutScopeAnnotation() {
- try {
- Guice.createInjector(new OuterDeprecatedModule());
- fail();
- } catch (CreationException expected) {
- assertContains(expected.getMessage(),
- "1) Please annotate " + Deprecated.class.getName() + " with @ScopeAnnotation.",
- "at " + InnerDeprecatedModule.class.getName() + getDeclaringSourcePart(getClass()),
- asModuleChain(OuterDeprecatedModule.class, InnerDeprecatedModule.class));
- }
- }
- static class OuterScopeModule extends AbstractModule {
- @Override protected void configure() {
- install(new CustomNoScopeModule());
- install(new CustomSingletonModule());
- }
- }
- static class CustomNoScopeModule extends AbstractModule {
- @Override protected void configure() {
- bindScope(CustomScoped.class, Scopes.NO_SCOPE);
- }
- }
- static class CustomSingletonModule extends AbstractModule {
- @Override protected void configure() {
- bindScope(CustomScoped.class, Scopes.SINGLETON);
- }
- }
- public void testBindScopeTooManyTimes() {
- try {
- Guice.createInjector(new OuterScopeModule());
- fail();
- } catch (CreationException expected) {
- assertContains(expected.getMessage(),
- "1) Scope Scopes.NO_SCOPE is already bound to " + CustomScoped.class.getName()
- + " at " + CustomNoScopeModule.class.getName() + getDeclaringSourcePart(getClass()),
- asModuleChain(OuterScopeModule.class, CustomNoScopeModule.class),
- "Cannot bind Scopes.SINGLETON.",
- "at " + ScopesTest.class.getName(), getDeclaringSourcePart(getClass()),
- asModuleChain(OuterScopeModule.class, CustomSingletonModule.class));
- }
- }
- public void testBindDuplicateScope() {
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bindScope(CustomScoped.class, Scopes.SINGLETON);
- bindScope(CustomScoped.class, Scopes.SINGLETON);
- }
- });
- assertSame(
- injector.getInstance(AnnotatedCustomScoped.class),
- injector.getInstance(AnnotatedCustomScoped.class));
- }
- public void testDuplicateScopeAnnotations() {
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bindScope(CustomScoped.class, Scopes.NO_SCOPE);
- }
- });
- try {
- injector.getInstance(SingletonAndCustomScoped.class);
- fail();
- } catch (ConfigurationException expected) {
- assertContains(expected.getMessage(),
- "1) More than one scope annotation was found: ",
- "while locating " + SingletonAndCustomScoped.class.getName());
- }
- }
- public void testNullScopedAsASingleton() {
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override
- protected void configure() {}
- final Iterator<String> values = Arrays.asList(null, "A").iterator();
- @Provides @Singleton String provideString() {
- return values.next();
- }
- });
- assertNull(injector.getInstance(String.class));
- assertNull(injector.getInstance(String.class));
- assertNull(injector.getInstance(String.class));
- }
- class RememberProviderScope implements Scope {
- final Map<Key<?>, Provider<?>> providers = Maps.newHashMap();
- public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
- providers.put(key, unscoped);
- return unscoped;
- }
- }
- public void testSingletonAnnotationOnParameterizedType() {
- Injector injector = Guice.createInjector();
- assertSame(injector.getInstance(new Key<Injected<String>>() {}),
- injector.getInstance(new Key<Injected<String>>() {}));
- assertSame(injector.getInstance(new Key<In<Integer>>() {}),
- injector.getInstance(new Key<In<Short>>() {}));
- }
- @ImplementedBy(Injected.class) public interface In<T> {}
- @Singleton public static class Injected<T> implements In<T> {}
- @Target({ ElementType.TYPE, ElementType.METHOD })
- @Retention(RUNTIME)
- @ScopeAnnotation
- public @interface CustomScoped {}
- static final Scope CUSTOM_SCOPE = new Scope() {
- public <T> Provider<T> scope(Key<T> key, Provider<T> unscoped) {
- return Scopes.SINGLETON.scope(key, unscoped);
- }
- };
- @Target({ ElementType.TYPE, ElementType.METHOD })
- @ScopeAnnotation
- public @interface NotRuntimeRetainedScoped {}
- @CustomScoped
- static class AnnotatedCustomScoped {}
- @Singleton
- static class AnnotatedSingleton {
- static int nextInstanceId;
- final int instanceId = nextInstanceId++;
- }
- static class BoundAsSingleton {
- static int nextInstanceId;
- final int instanceId = nextInstanceId++;
- }
- static class EagerSingleton {
- static int nextInstanceId;
- final int instanceId = nextInstanceId++;
- }
- interface LinkedSingleton {}
- @Singleton
- static class RealLinkedSingleton implements LinkedSingleton {
- static int nextInstanceId;
- final int instanceId = nextInstanceId++;
- }
- static class DependsOnJustInTimeSingleton {
- @Inject JustInTimeSingleton justInTimeSingleton;
- }
- @Singleton
- static class JustInTimeSingleton {
- static int nextInstanceId;
- final int instanceId = nextInstanceId++;
- }
- static class NotASingleton {
- static int nextInstanceId;
- final int instanceId = nextInstanceId++;
- }
- @SuppressWarnings("MoreThanOneScopeAnnotationOnClass") // suppress compiler error for testing
- @Singleton
- @CustomScoped
- static class SingletonAndCustomScoped {}
- @ImplementedBy(Implementation.class)
- static interface ImplementedBySingleton {}
- @ProvidedBy(ImplementationProvider.class)
- static class ProvidedBySingleton {
- static int nextInstanceId;
- final int instanceId = nextInstanceId++;
- }
- static class Implementation implements ImplementedBySingleton {
- static int nextInstanceId;
- final int instanceId = nextInstanceId++;
- }
- static class ImplementationProvider implements Provider<ProvidedBySingleton> {
- public ProvidedBySingleton get() {
- return new ProvidedBySingleton();
- }
- }
- public void testScopeThatGetsAnUnrelatedObject() {
- Injector injector = Guice.createInjector(new AbstractModule() {
- @Override protected void configure() {
- bind(B.class);
- bind(C.class);
- ProviderGetScope providerGetScope = new ProviderGetScope();
- requestInjection(providerGetScope);
- bindScope(CustomScoped.class, providerGetScope);
- }
- });
- injector.getInstance(C.class);
- }
- class ProviderGetScope implements Scope {
- @Inject Provider<B> bProvider;
- public <T> Provider<T> scope(Key<T> key, final Provider<T> unscoped) {
- return new Provider<T>() {
- public T get() {
- bProvider.get();
- return unscoped.get();
- }
- };
- }
- }
- public void testIsSingletonPositive() {
- final Key<String> a = Key.get(String.class, named("A"));
- final Key<String> b = Key.get(String.class, named("B"));
- final Key<String> c = Key.get(String.class, named("C"));
- final Key<String> d = Key.get(String.class, named("D"));
- final Key<String> e = Key.get(String.class, named("E"));
- final Key<String> f = Key.get(String.class, named("F"));
- final Key<String> g = Key.get(String.class, named("G"));
- final Key<Object> h = Key.get(Object.class, named("H"));
- final Key<String> i = Key.get(String.class, named("I"));
- Module singletonBindings = new AbstractModule() {
- @Override protected void configure() {
- bind(a).to(b);
- bind(b).to(c);
- bind(c).toProvider(Providers.of("c")).in(Scopes.SINGLETON);
- bind(d).toInstance("d");
- bind(e).toProvider(Providers.of("e")).asEagerSingleton();
- bind(f).toProvider(Providers.of("f")).in(Singleton.class);
- bind(h).to(AnnotatedSingleton.class);
- install(new PrivateModule() {
- @Override protected void configure() {
- bind(i).toProvider(Providers.of("i")).in(Singleton.class);
- expose(i);
- }
- });
- }
- @Provides @Named("G") @Singleton String provideG() {
- return "g";
- }
- };
- @SuppressWarnings("unchecked") // we know the module contains only bindings
- List<Element> moduleBindings = Elements.getElements(singletonBindings);
- ImmutableMap<Key<?>, Binding<?>> map = indexBindings(moduleBindings);
- assertFalse(Scopes.isSingleton(map.get(a))); // linked bindings are not followed by modules
- assertFalse(Scopes.isSingleton(map.get(b)));
- assertTrue(Scopes.isSingleton(map.get(c)));
- assertTrue(Scopes.isSingleton(map.get(d)));
- assertTrue(Scopes.isSingleton(map.get(e)));
- assertTrue(Scopes.isSingleton(map.get(f)));
- assertTrue(Scopes.isSingleton(map.get(g)));
- assertFalse(Scopes.isSingleton(map.get(h))); // annotated classes are not followed by modules
- assertTrue(Scopes.isSingleton(map.get(i)));
- Injector injector = Guice.createInjector(singletonBindings);
- assertTrue(Scopes.isSingleton(injector.getBinding(a)));
- assertTrue(Scopes.isSingleton(injector.getBinding(b)));
- assertTrue(Scopes.isSingleton(injector.getBinding(c)));
- assertTrue(Scopes.isSingleton(injector.getBinding(d)));
- assertTrue(Scopes.isSingleton(injector.getBinding(e)));
- assertTrue(Scopes.isSingleton(injector.getBinding(f)));
- assertTrue(Scopes.isSingleton(injector.getBinding(g)));
- assertTrue(Scopes.isSingleton(injector.getBinding(h)));
- assertTrue(Scopes.isSingleton(injector.getBinding(i)));
- }
- public void testIsSingletonNegative() {
- final Key<String> a = Key.get(String.class, named("A"));
- final Key<String> b = Key.get(String.class, named("B"));
- final Key<String> c = Key.get(String.class, named("C"));
- final Key<String> d = Key.get(String.class, named("D"));
- final Key<String> e = Key.get(String.class, named("E"));
- final Key<String> f = Key.get(String.class, named("F"));
- Module singletonBindings = new AbstractModule() {
- @Override protected void configure() {
- bind(a).to(b);
- bind(b).to(c);
- bind(c).toProvider(Providers.of("c")).in(Scopes.NO_SCOPE);
- bind(d).toProvider(Providers.of("d")).in(CustomScoped.class);
- bindScope(CustomScoped.class, Scopes.NO_SCOPE);
- install(new PrivateModule() {
- @Override protected void configure() {
- bind(f).toProvider(Providers.of("f")).in(CustomScoped.class);
- expose(f);
- }
- });
- }
- @Provides @Named("E") @CustomScoped String provideE() {
- return "e";
- }
- };
- @SuppressWarnings("unchecked") // we know the module contains only bindings
- List<Element> moduleBindings = Elements.getElements(singletonBindings);
- ImmutableMap<Key<?>, Binding<?>> map = indexBindings(moduleBindings);
- assertFalse(Scopes.isSingleton(map.get(a)));
- assertFalse(Scopes.isSingleton(map.get(b)));
- assertFalse(Scopes.isSingleton(map.get(c)));
- assertFalse(Scopes.isSingleton(map.get(d)));
- assertFalse(Scopes.isSingleton(map.get(e)));
- assertFalse(Scopes.isSingleton(map.get(f)));
- Injector injector = Guice.createInjector(singletonBindings);
- assertFalse(Scopes.isSingleton(injector.getBinding(a)));
- assertFalse(Scopes.isSingleton(injector.getBinding(b)));
- assertFalse(Scopes.isSingleton(injector.getBinding(c)));
- assertFalse(Scopes.isSingleton(injector.getBinding(d)));
- assertFalse(Scopes.isSingleton(injector.getBinding(e)));
- assertFalse(Scopes.isSingleton(injector.getBinding(f)));
- }
- public void testIsScopedPositive() {
- final Key<String> a = Key.get(String.class, named("A"));
- final Key<String> b = Key.get(String.class, named("B"));
- final Key<String> c = Key.get(String.class, named("C"));
- final Key<String> d = Key.get(String.class, named("D"));
- final Key<String> e = Key.get(String.class, named("E"));
- final Key<Object> f = Key.get(Object.class, named("F"));
- final Key<String> g = Key.get(String.class, named("G"));
- Module customBindings = new AbstractModule() {
- @Override protected void configure() {
- bindScope(CustomScoped.class, CUSTOM_SCOPE);
- bind(a).to(b);
- bind(b).to(c);
- bind(c).toProvider(Providers.of("c")).in(CUSTOM_SCOPE);
- bind(d).toProvider(Providers.of("d")).in(CustomScoped.class);
- bind(f).to(AnnotatedCustomScoped.class);
- install(new PrivateModule() {
- @Override protected void configure() {
- bind(g).toProvider(Providers.of("g")).in(CustomScoped.class);
- expose(g);
- }
- });
- }
- @Provides @Named("E") @CustomScoped String provideE() {
- return "e";
- }
- };
- @SuppressWarnings("unchecked") // we know the module contains only bindings
- List<Element> moduleBindings = Elements.getElements(customBindings);
- ImmutableMap<Key<?>, Binding<?>> map = indexBindings(moduleBindings);
- assertFalse(isCustomScoped(map.get(a))); // linked bindings are not followed by modules
- assertFalse(isCustomScoped(map.get(b)));
- assertTrue(isCustomScoped(map.get(c)));
- assertTrue(isCustomScoped(map.get(d)));
- assertTrue(isCustomScoped(map.get(e)));
- assertFalse(isCustomScoped(map.get(f))); // annotated classes are not followed by modules
- assertTrue(isCustomScoped(map.get(g)));
- Injector injector = Guice.createInjector(customBindings);
- assertTrue(isCustomScoped(injector.getBinding(a)));
- assertTrue(isCustomScoped(injector.getBinding(b)));
- assertTrue(isCustomScoped(injector.getBinding(c)));
- assertTrue(isCustomScoped(injector.getBinding(d)));
- assertTrue(isCustomScoped(injector.getBinding(e)));
- assertTrue(isCustomScoped(injector.getBinding(f)));
- assertTrue(isCustomScoped(injector.getBinding(g)));
- }
- public void testIsScopedNegative() {
- final Key<String> a = Key.get(String.class, named("A"));
- final Key<String> b = Key.get(String.class, named("B"));
- final Key<String> c = Key.get(String.class, named("C"));
- final Key<String> d = Key.get(String.class, named("D"));
- final Key<String> e = Key.get(String.class, named("E"));
- final Key<String> f = Key.get(String.class, named("F"));
- final Key<String> g = Key.get(String.class, named("G"));
- final Key<String> h = Key.get(String.class, named("H"));
- Module customBindings = new AbstractModule() {
- @Override protected void configure() {
- bind(a).to(b);
- bind(b).to(c);
- bind(c).toProvider(Providers.of("c")).in(Scopes.NO_SCOPE);
- bind(d).toProvider(Providers.of("d")).in(Singleton.class);
- install(new PrivateModule() {
- @Override
- protected void configure() {
- bind(f).toProvider(Providers.of("f")).in(Singleton.class);
- expose(f);
- }
- });
- bind(g).toInstance("g");
- bind(h).toProvider(Providers.of("h")).asEagerSingleton();
- }
- @Provides @Named("E") @Singleton String provideE() {
- return "e";
- }
- };
- @SuppressWarnings("unchecked") // we know the module contains only bindings
- List<Element> moduleBindings = Elements.getElements(customBindings);
- ImmutableMap<Key<?>, Binding<?>> map = indexBindings(moduleBindings);
- assertFalse(isCustomScoped(map.get(a)));
- assertFalse(isCustomScoped(map.get(b)));
- assertFalse(isCustomScoped(map.get(c)));
- assertFalse(isCustomScoped(map.get(d)));
- assertFalse(isCustomScoped(map.get(e)));
- assertFalse(isCustomScoped(map.get(f)));
- assertFalse(isCustomScoped(map.get(g)));
- assertFalse(isCustomScoped(map.get(h)));
- Injector injector = Guice.createInjector(customBindings);
- assertFalse(isCustomScoped(injector.getBinding(a)));
- assertFalse(isCustomScoped(injector.getBinding(b)));
- assertFalse(isCustomScoped(injector.getBinding(c)));
- assertFalse(isCustomScoped(injector.getBinding(d)));
- assertFalse(isCustomScoped(injector.getBinding(e)));
- assertFalse(isCustomScoped(injector.getBinding(f)));
- assertFalse(isCustomScoped(injector.getBinding(g)));
- assertFalse(isCustomScoped(injector.getBinding(h)));
- }
- private boolean isCustomScoped(Binding<?> binding) {
- return Scopes.isScoped(binding, CUSTOM_SCOPE, CustomScoped.class);
- }
- ImmutableMap<Key<?>, Binding<?>> indexBindings(Iterable<Element> elements) {
- ImmutableMap.Builder<Key<?>, Binding<?>> builder = ImmutableMap.builder();
- for (Element element : elements) {
- if (element instanceof Binding) {
- Binding<?> binding = (Binding<?>) element;
- builder.put(binding.getKey(), binding);
- } else if (element instanceof PrivateElements) {
- PrivateElements privateElements = (PrivateElements)element;
- Map<Key<?>, Binding<?>> privateBindings = indexBindings(privateElements.getElements());
- for(Key<?> exposed : privateElements.getExposedKeys()) {
- builder.put(exposed, privateBindings.get(exposed));
- }
- }
- }
- return builder.build();
- }
- @Singleton
- static class ThrowingSingleton {
- static int nextInstanceId;
- final int instanceId = nextInstanceId++;
- ThrowingSingleton() {
- if (instanceId == 0) {
- throw new RuntimeException();
- }
- }
- }
- public void testSingletonConstructorThrows() {
- Injector injector = Guice.createInjector();
- try {
- injector.getInstance(ThrowingSingleton.class);
- fail();
- } catch (ProvisionException expected) {
- }
- // this behaviour is unspecified. If we change Guice to re-throw the exception, this test
- // should be changed
- injector.getInstance(ThrowingSingleton.class);
- assertEquals(2, ThrowingSingleton.nextInstanceId);
- }
- /**
- * Should only be created by {@link SBarrierProvider}.
- *
- * <p>{@code S} stands for synchronization.
- *
- * @see SBarrierProvider
- */
- static class S {
- private S(int preventInjectionWithoutProvider) {
- }
- }
- /**
- * Provides all the instances of S simultaneously using {@link CyclicBarrier} with {@code
- * nThreads}. Intended to be used for threads synchronization during injection.
- */
- static class SBarrierProvider implements Provider<S> {
- final CyclicBarrier barrier;
- volatile boolean barrierPassed = false;
- SBarrierProvider(int nThreads) {
- barrier = new CyclicBarrier(nThreads, new Runnable() {
- public void run() {
- // would finish before returning from await() for any thread
- barrierPassed = true;
- }
- });
- }
- public S get() {
- try {
- if (!barrierPassed) {
- // only if we're triggering barrier for the first time
- barrier.await(DEADLOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS);
- }
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- return new S(0);
- }
- }
- /**
- * Tests that different injectors should not affect each other.
- *
- * <p>This creates a second thread to work in parallel, to create two instances of
- * {@link S} as the same time. If the lock if not granular enough (i.e. JVM-wide)
- * then they would block each other creating a deadlock and await timeout.
- */
- public void testInjectorsDontDeadlockOnSingletons() throws Exception {
- final Provider<S> provider = new SBarrierProvider(2);
- final Injector injector = Guice.createInjector(new AbstractModule() {
- @Override
- protected void configure() {
- Thread.currentThread().setName("S.class[1]");
- bind(S.class).toProvider(provider).in(Scopes.SINGLETON);
- }
- });
- final Injector secondInjector = Guice.createInjector(new AbstractModule() {
- @Override
- protected void configure() {
- Thread.currentThread().setName("S.class[2]");
- bind(S.class).toProvider(provider).in(Scopes.SINGLETON);
- }
- });
- Future<S> secondThreadResult = Executors.newSingleThreadExecutor().submit(new Callable<S>() {
- public S call() {
- return secondInjector.getInstance(S.class);
- }
- });
- S firstS = injector.getInstance(S.class);
- S secondS = secondThreadResult.get();
- assertNotSame(firstS, secondS);
- }
- @ImplementedBy(GImpl.class)
- interface G {
- }
- @Singleton
- static class GImpl implements G {
- final H h;
- /**
- * Relies on Guice implementation to inject S first and H later, which provides a barrier .
- */
- @Inject
- GImpl(S synchronizationBarrier, H h) {
- this.h = h;
- }
- }
- @ImplementedBy(HImpl.class)
- interface H {
- }
- @Singleton
- static class HImpl implements H {
- final G g;
- /**
- * Relies on Guice implementation to inject S first and G later, which provides a barrier .
- */
- @Inject
- HImpl(S synchronizationBarrier, G g) throws Exception {
- this.g = g;
- }
- }
- /**
- * Tests that injector can create two singletons with circular dependency in parallel.
- *
- * <p>This creates two threads to work in parallel, to create instances of
- * {@link G} and {@link H}. Creation is synchronized by injection of {@link S},
- * first thread would block until second would be inside a singleton creation as well.
- *
- * <p>Both instances are created by sibling injectors, that share singleton scope.
- * Verifies that exactly one circular proxy object is created.
- */
- public void testSiblingInjectorGettingCircularSingletonsOneCircularProxy() throws Exception {
- final Provider<S> provider = new SBarrierProvider(2);
- final Injector injector = Guice.createInjector(new AbstractModule() {
- @Override
- protected void configure() {
- bind(S.class).toProvider(provider);
- }
- });
- Future<G> firstThreadResult = Executors.newSingleThreadExecutor().submit(new Callable<G>() {
- public G call() {
- Thread.currentThread().setName("G.class");
- return injector.createChildInjector().getInstance(G.class);
- }
- });
- Future<H> secondThreadResult = Executors.newSingleThreadExecutor().submit(new Callable<H>() {
- public H call() {
- Thread.currentThread().setName("H.class");
- return injector.createChildInjector().getInstance(H.class);
- }
- });
- // using separate threads to avoid potential deadlock on the main thread
- // waiting twice as much to be sure that both would time out in their respective barriers
- GImpl g = (GImpl) firstThreadResult.get(DEADLOCK_TIMEOUT_SECONDS * 3, TimeUnit.SECONDS);
- HImpl h = (HImpl) secondThreadResult.get(DEADLOCK_TIMEOUT_SECONDS * 3, TimeUnit.SECONDS);
- // Check that G and H created are not proxied
- assertTrue(!Scopes.isCircularProxy(g) && !Scopes.isCircularProxy(h));
- // Check that we have no more than one circular proxy created
- assertFalse(Scopes.isCircularProxy(g.h) && Scopes.isCircularProxy(h.g));
- // Check that non-proxy variable points to another singleton
- assertTrue(g.h == h || h.g == g);
- // Check correct proxy initialization as default equals implementation would
- assertEquals(g.h, h);
- assertEquals(h.g, g);
- }
- @Singleton
- static class I0 {
- /**
- * Relies on Guice implementation to inject S first, which provides a barrier .
- */
- @Inject
- I0(I1 i) {
- }
- }
- @Singleton
- static class I1 {
- /**
- * Relies on Guice implementation to inject S first, which provides a barrier .
- */
- @Inject
- I1(S synchronizationBarrier, I2 i) {
- }
- }
- @Singleton
- static class I2 {
- /**
- * Relies on Guice implementation to inject S first, which provides a barrier .
- */
- @Inject
- I2(J1 j) {
- }
- }
- @Singleton
- static class J0 {
- /**
- * Relies on Guice implementation to inject S first, which provides a barrier .
- */
- @Inject
- J0(J1 j) {
- }
- }
- @Singleton
- static class J1 {
- /**
- * Relies on Guice implementation to inject S first, which provides a barrier .
- */
- @Inject
- J1(S synchronizationBarrier, J2 j) {
- }
- }
- @Singleton
- static class J2 {
- /**
- * Relies on Guice implementation to inject S first, which provides a barrier .
- */
- @Inject
- J2(K1 k) {
- }
- }
- @Singleton
- static class K0 {
- /**
- * Relies on Guice implementation to inject S first, which provides a barrier .
- */
- @Inject
- K0(K1 k) {
- }
- }
- @Singleton
- static class K1 {
- /**
- * Relies on Guice implementation to inject S first, which provides a barrier .
- */
- @Inject
- K1(S synchronizationBarrier, K2 k) {
- }
- }
- @Singleton
- static class K2 {
- /**
- * Relies on Guice implementation to inject S first, which provides a barrier .
- */
- @Inject
- K2(I1 i) {
- }
- }
- /**
- * Check that circular dependencies on non-interfaces are correctly resolved in multi-threaded
- * case. And that an error message constructed is a good one.
- *
- * <p>I0 -> I1 -> I2 -> J1 and J0 -> J1 -> J2 -> K1 and K0 -> K1 -> K2,
- * where I1, J1 and K1 are created in parallel.
- *
- * <p>Creation is synchronized by injection of {@link S}, first thread would block until second
- * would be inside a singleton creation as well.
- *
- * <p>Verifies that provision results in an error, that spans two threads and
- * has a dependency cycle.
- */
- public void testUnresolvableSingletonCircularDependencyErrorMessage() throws Exception {
- final Provider<S> provider = new SBarrierProvider(3);
- final Injector injector = Guice.createInjector(new AbstractModule() {
- @Override
- protected void configure() {
- bind(S.class).toProvider(provider);
- }
- });
- Future<I0> firstThreadResult = Executors.newSingleThreadExecutor().submit(new Callable<I0>() {
- public I0 call() {
- Thread.currentThread().setName("I0.class");
- return injector.getInstance(I0.class);
- }
- });
- Future<J0> secondThreadResult = Executors.newSingleThreadExecutor().submit(new Callable<J0>() {
- public J0 call() {
- Thread.currentThread().setName("J0.class");
- return injector.getInstance(J0.class);
- }
- });
- Future<K0> thirdThreadResult = Executors.newSingleThreadExecutor().submit(new Callable<K0>() {
- public K0 call() {
- Thread.currentThread().setName("K0.class");
- return injector.getInstance(K0.class);
- }
- });
- // using separate threads to avoid potential deadlock on the main thread
- // waiting twice as much to be sure that both would time out in their respective barriers
- Throwable firstException = null;
- Throwable secondException = null;
- Throwable thirdException = null;
- try {
- firstThreadResult.get(DEADLOCK_TIMEOUT_SECONDS * 3, TimeUnit.SECONDS);
- fail();
- } catch (ExecutionException e) {
- firstException = e.getCause();
- }
- try {
- secondThreadResult.get(DEADLOCK_TIMEOUT_SECONDS * 3, TimeUnit.SECONDS);
- fail();
- } catch (ExecutionException e) {
- secondException = e.getCause();
- }
- try {
- thirdThreadResult.get(DEADLOCK_TIMEOUT_SECONDS * 3, TimeUnit.SECONDS);
- fail();
- } catch (ExecutionException e) {
- thirdException = e.getCause();
- }
- // verification of error messages generated
- assertEquals(firstException.getClass(), ProvisionException.class);
- assertEquals(secondException.getClass(), ProvisionException.class);
- assertEquals(thirdException.getClass(), ProvisionException.class);
- List<String> errorMessages = Lists.newArrayList(
- String.format("%s\n%s\n%s",
- firstException.getMessage(), secondException.getMessage(), thirdException.getMessage())
- .split("\\n\\n"));
- Collections.sort(errorMessages, new Comparator<String>() {
- @Override
- public int compare(String s1, String s2) {
- return s2.length() - s1.length();
- }
- });
- // this is brittle, but turns out that second to longest message spans all threads
- String errorMessage = errorMessages.get(1);
- assertContains(errorMessage,
- "Encountered circular dependency spanning several threads. Tried proxying "
- + this.getClass().getName());
- assertFalse("Both I0 and J0 can not be a part of a dependency cycle",
- errorMessage.contains(I0.class.getName()) && errorMessage.contains(J0.class.getName()));
- assertFalse("Both J0 and K0 can not be a part of a dependency cycle",
- errorMessage.contains(J0.class.getName()) && errorMessage.contains(K0.class.getName()));
- assertFalse("Both K0 and I0 can not be a part of a dependency cycle",
- errorMessage.contains(K0.class.getName()) && errorMessage.contains(I0.class.getName()));
- List<String> firstErrorLineForThread = new ArrayList<String>();
- boolean addNextLine = false;
- for (String errorLine : errorMessage.split("\\n")) {
- if (errorLine.contains("in thread")) {
- addNextLine = true;
- firstErrorLineForThread.add(errorLine);
- } else if (addNextLine) {
- addNextLine = false;
- firstErrorLineForThread.add(errorLine);
- }
- }
- assertEquals("we expect to see [T1, $A, T2, $B, T3, $C, T1, $A]",
- 8, firstErrorLineForThread.size());
- assertEquals("first four elements should be different",
- 6, new HashSet<String>(firstErrorLineForThread.subList(0, 6)).size());
- assertEquals(firstErrorLineForThread.get(6), firstErrorLineForThread.get(0));
- assertEquals(firstErrorLineForThread.get(7), firstErrorLineForThread.get(1));
- assertFalse("K0 thread could not be blocked by J0",
- firstErrorLineForThread.get(0).contains("J0")
- && firstErrorLineForThread.get(2).contains("K0"));
- assertFalse("J0 thread could not be blocked by I0",
- firstErrorLineForThread.get(0).contains("I0")
- && firstErrorLineForThread.get(2).contains("J0"));
- assertFalse("I0 thread could not be blocked by K0",
- firstErrorLineForThread.get(0).contains("K0")
- && firstErrorLineForThread.get(2).contains("I0"));
- }
- }