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