PageRenderTime 42ms CodeModel.GetById 0ms RepoModel.GetById 0ms app.codeStats 1ms

/core/test/com/google/inject/ScopesTest.java

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