1 /* 2 * Copyright (C) 2011 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.common.collect.ImmutableList.of; 20 import static com.google.common.truth.Truth.assertThat; 21 import static com.google.inject.Asserts.assertContains; 22 import static com.google.inject.name.Names.named; 23 24 import com.google.common.collect.ImmutableList; 25 import com.google.common.collect.ImmutableSet; 26 import com.google.common.collect.Iterables; 27 import com.google.common.collect.Lists; 28 import com.google.inject.matcher.AbstractMatcher; 29 import com.google.inject.matcher.Matcher; 30 import com.google.inject.matcher.Matchers; 31 import com.google.inject.name.Named; 32 import com.google.inject.spi.InstanceBinding; 33 import com.google.inject.spi.ProvisionListener; 34 import com.google.inject.util.Providers; 35 import java.util.List; 36 import java.util.Set; 37 import java.util.concurrent.atomic.AtomicBoolean; 38 import java.util.concurrent.atomic.AtomicInteger; 39 import java.util.concurrent.atomic.AtomicReference; 40 import junit.framework.TestCase; 41 42 /** 43 * Tests for {@link Binder#bindListener(Matcher, ProvisionListener...)} 44 * 45 * @author sameb@google.com (Sam Berlin) 46 */ 47 // TODO(sameb): Add some tests for private modules & child injectors. 48 public class ProvisionListenerTest extends TestCase { 49 testExceptionInListenerBeforeProvisioning()50 public void testExceptionInListenerBeforeProvisioning() { 51 Injector injector = 52 Guice.createInjector( 53 new AbstractModule() { 54 @Override 55 protected void configure() { 56 bindListener(Matchers.any(), new FailBeforeProvision()); 57 } 58 }); 59 try { 60 injector.getInstance(Foo.class); 61 fail(); 62 } catch (ProvisionException pe) { 63 assertEquals(1, pe.getErrorMessages().size()); 64 assertContains( 65 pe.getMessage(), 66 "1) Error notifying ProvisionListener " 67 + FailBeforeProvision.class.getName() 68 + " of " 69 + Foo.class.getName(), 70 "Reason: java.lang.RuntimeException: boo", 71 "while locating " + Foo.class.getName()); 72 assertEquals("boo", pe.getCause().getMessage()); 73 } 74 } 75 testExceptionInListenerAfterProvisioning()76 public void testExceptionInListenerAfterProvisioning() { 77 Injector injector = 78 Guice.createInjector( 79 new AbstractModule() { 80 @Override 81 protected void configure() { 82 bindListener(Matchers.any(), new FailAfterProvision()); 83 } 84 }); 85 try { 86 injector.getInstance(Foo.class); 87 fail(); 88 } catch (ProvisionException pe) { 89 assertEquals(1, pe.getErrorMessages().size()); 90 assertContains( 91 pe.getMessage(), 92 "1) Error notifying ProvisionListener " 93 + FailAfterProvision.class.getName() 94 + " of " 95 + Foo.class.getName(), 96 "Reason: java.lang.RuntimeException: boo", 97 "while locating " + Foo.class.getName()); 98 assertEquals("boo", pe.getCause().getMessage()); 99 } 100 } 101 testExceptionInProvisionExplicitlyCalled()102 public void testExceptionInProvisionExplicitlyCalled() { 103 Injector injector = 104 Guice.createInjector( 105 new AbstractModule() { 106 @Override 107 protected void configure() { 108 bindListener(Matchers.any(), new JustProvision()); 109 } 110 }); 111 try { 112 injector.getInstance(FooBomb.class); 113 fail(); 114 } catch (ProvisionException pe) { 115 assertEquals(1, pe.getErrorMessages().size()); 116 assertContains( 117 pe.getMessage(), 118 "1) Error injecting constructor, java.lang.RuntimeException: Retry, Abort, Fail", 119 " at " + FooBomb.class.getName(), 120 " while locating " + FooBomb.class.getName()); 121 assertEquals("Retry, Abort, Fail", pe.getCause().getMessage()); 122 } 123 } 124 testExceptionInProvisionAutomaticallyCalled()125 public void testExceptionInProvisionAutomaticallyCalled() { 126 Injector injector = 127 Guice.createInjector( 128 new AbstractModule() { 129 @Override 130 protected void configure() { 131 bindListener(Matchers.any(), new NoProvision()); 132 } 133 }); 134 try { 135 injector.getInstance(FooBomb.class); 136 fail(); 137 } catch (ProvisionException pe) { 138 assertEquals(1, pe.getErrorMessages().size()); 139 assertContains( 140 pe.getMessage(), 141 "1) Error injecting constructor, java.lang.RuntimeException: Retry, Abort, Fail", 142 " at " + FooBomb.class.getName(), 143 " while locating " + FooBomb.class.getName()); 144 assertEquals("Retry, Abort, Fail", pe.getCause().getMessage()); 145 } 146 } 147 testExceptionInFieldProvision()148 public void testExceptionInFieldProvision() throws Exception { 149 final CountAndCaptureExceptionListener listener = new CountAndCaptureExceptionListener(); 150 Injector injector = 151 Guice.createInjector( 152 new AbstractModule() { 153 @Override 154 protected void configure() { 155 bindListener( 156 new AbstractMatcher<Binding<?>>() { 157 @Override 158 public boolean matches(Binding<?> binding) { 159 return binding.getKey().getRawType().equals(DependsOnFooBombInField.class); 160 } 161 }, 162 listener); 163 } 164 }); 165 assertEquals(0, listener.beforeProvision); 166 String expectedMsg = null; 167 try { 168 injector.getInstance(DependsOnFooBombInField.class); 169 fail(); 170 } catch (ProvisionException expected) { 171 assertEquals(1, expected.getErrorMessages().size()); 172 expectedMsg = Iterables.getOnlyElement(expected.getErrorMessages()).getMessage(); 173 assertContains( 174 expected.getMessage(), 175 "1) Error injecting constructor, java.lang.RuntimeException: Retry, Abort, Fail", 176 " at " + FooBomb.class.getName(), 177 " while locating " + FooBomb.class.getName(), 178 " while locating " + DependsOnFooBombInField.class.getName()); 179 assertContains( 180 listener.capture.get().getMessage(), 181 "1) Error injecting constructor, java.lang.RuntimeException: Retry, Abort, Fail", 182 " at " + FooBomb.class.getName(), 183 " while locating " + FooBomb.class.getName()); 184 // The message that is captures by the provision listener does not show what is depending on 185 // the thing being listened to. 186 assertThat(listener.capture.get().getMessage()) 187 .doesNotContain(" while locating " + DependsOnFooBombInField.class.getName()); 188 } 189 assertEquals(1, listener.beforeProvision); 190 assertEquals( 191 expectedMsg, 192 Iterables.getOnlyElement(((ProvisionException) listener.capture.get()).getErrorMessages()) 193 .getMessage()); 194 assertEquals(0, listener.afterProvision); 195 } 196 testExceptionInCxtorProvision()197 public void testExceptionInCxtorProvision() throws Exception { 198 final CountAndCaptureExceptionListener listener = new CountAndCaptureExceptionListener(); 199 Injector injector = 200 Guice.createInjector( 201 new AbstractModule() { 202 @Override 203 protected void configure() { 204 bindListener( 205 new AbstractMatcher<Binding<?>>() { 206 @Override 207 public boolean matches(Binding<?> binding) { 208 return binding.getKey().getRawType().equals(DependsOnFooBombInCxtor.class); 209 } 210 }, 211 listener); 212 } 213 }); 214 assertEquals(0, listener.beforeProvision); 215 String expectedMsg = null; 216 try { 217 injector.getInstance(DependsOnFooBombInCxtor.class); 218 fail(); 219 } catch (ProvisionException expected) { 220 assertEquals(1, expected.getErrorMessages().size()); 221 expectedMsg = Iterables.getOnlyElement(expected.getErrorMessages()).getMessage(); 222 assertContains( 223 expected.getMessage(), 224 "1) Error injecting constructor, java.lang.RuntimeException: Retry, Abort, Fail", 225 " at " + FooBomb.class.getName(), 226 " while locating " + FooBomb.class.getName(), 227 " while locating " + DependsOnFooBombInCxtor.class.getName()); 228 assertContains( 229 listener.capture.get().getMessage(), 230 "1) Error injecting constructor, java.lang.RuntimeException: Retry, Abort, Fail", 231 " at " + FooBomb.class.getName(), 232 " while locating " + FooBomb.class.getName()); 233 // The message that is captures by the provision listener does not show what is depending on 234 // the thing being listened to. 235 assertThat(listener.capture.get().getMessage()) 236 .doesNotContain(" while locating " + DependsOnFooBombInField.class.getName()); 237 } 238 assertEquals(1, listener.beforeProvision); 239 assertEquals( 240 expectedMsg, 241 Iterables.getOnlyElement(((ProvisionException) listener.capture.get()).getErrorMessages()) 242 .getMessage()); 243 assertEquals(0, listener.afterProvision); 244 } 245 testListenerCallsProvisionTwice()246 public void testListenerCallsProvisionTwice() { 247 Injector injector = 248 Guice.createInjector( 249 new AbstractModule() { 250 @Override 251 protected void configure() { 252 bindListener(Matchers.any(), new ProvisionTwice()); 253 } 254 }); 255 try { 256 injector.getInstance(Foo.class); 257 fail(); 258 } catch (ProvisionException pe) { 259 assertEquals(1, pe.getErrorMessages().size()); 260 assertContains( 261 pe.getMessage(), 262 "1) Error notifying ProvisionListener " 263 + ProvisionTwice.class.getName() 264 + " of " 265 + Foo.class.getName(), 266 "Reason: java.lang.IllegalStateException: Already provisioned in this listener.", 267 "while locating " + Foo.class.getName()); 268 assertEquals("Already provisioned in this listener.", pe.getCause().getMessage()); 269 } 270 } 271 testCachedInScopePreventsProvisionNotify()272 public void testCachedInScopePreventsProvisionNotify() { 273 final Counter count1 = new Counter(); 274 Injector injector = 275 Guice.createInjector( 276 new AbstractModule() { 277 @Override 278 protected void configure() { 279 bindListener(Matchers.any(), count1); 280 bind(Foo.class).in(Scopes.SINGLETON); 281 } 282 }); 283 Foo foo = injector.getInstance(Foo.class); 284 assertNotNull(foo); 285 assertEquals(1, count1.count); 286 287 // not notified the second time because nothing is provisioned 288 // (it's cached in the scope) 289 count1.count = 0; 290 assertSame(foo, injector.getInstance(Foo.class)); 291 assertEquals(0, count1.count); 292 } 293 testCombineAllBindListenerCalls()294 public void testCombineAllBindListenerCalls() { 295 final Counter count1 = new Counter(); 296 final Counter count2 = new Counter(); 297 Injector injector = 298 Guice.createInjector( 299 new AbstractModule() { 300 @Override 301 protected void configure() { 302 bindListener(Matchers.any(), count1); 303 bindListener(Matchers.any(), count2); 304 } 305 }); 306 assertNotNull(injector.getInstance(Foo.class)); 307 assertEquals(1, count1.count); 308 assertEquals(1, count2.count); 309 } 310 testNotifyEarlyListenersIfFailBeforeProvision()311 public void testNotifyEarlyListenersIfFailBeforeProvision() { 312 final Counter count1 = new Counter(); 313 final Counter count2 = new Counter(); 314 Injector injector = 315 Guice.createInjector( 316 new AbstractModule() { 317 @Override 318 protected void configure() { 319 bindListener(Matchers.any(), count1, new FailBeforeProvision(), count2); 320 } 321 }); 322 try { 323 injector.getInstance(Foo.class); 324 fail(); 325 } catch (ProvisionException pe) { 326 assertEquals(1, pe.getErrorMessages().size()); 327 assertContains( 328 pe.getMessage(), 329 "1) Error notifying ProvisionListener " 330 + FailBeforeProvision.class.getName() 331 + " of " 332 + Foo.class.getName(), 333 "Reason: java.lang.RuntimeException: boo", 334 "while locating " + Foo.class.getName()); 335 assertEquals("boo", pe.getCause().getMessage()); 336 337 assertEquals(1, count1.count); 338 assertEquals(0, count2.count); 339 } 340 } 341 testNotifyLaterListenersIfFailAfterProvision()342 public void testNotifyLaterListenersIfFailAfterProvision() { 343 final Counter count1 = new Counter(); 344 final Counter count2 = new Counter(); 345 Injector injector = 346 Guice.createInjector( 347 new AbstractModule() { 348 @Override 349 protected void configure() { 350 bindListener(Matchers.any(), count1, new FailAfterProvision(), count2); 351 } 352 }); 353 try { 354 injector.getInstance(Foo.class); 355 fail(); 356 } catch (ProvisionException pe) { 357 assertEquals(1, pe.getErrorMessages().size()); 358 assertContains( 359 pe.getMessage(), 360 "1) Error notifying ProvisionListener " 361 + FailAfterProvision.class.getName() 362 + " of " 363 + Foo.class.getName(), 364 "Reason: java.lang.RuntimeException: boo", 365 "while locating " + Foo.class.getName()); 366 assertEquals("boo", pe.getCause().getMessage()); 367 368 assertEquals(1, count1.count); 369 assertEquals(1, count2.count); 370 } 371 } 372 testNotifiedKeysOfAllBindTypes()373 public void testNotifiedKeysOfAllBindTypes() { 374 final Capturer capturer = new Capturer(); 375 Injector injector = 376 Guice.createInjector( 377 new AbstractModule() { 378 @Override 379 protected void configure() { 380 bindListener(Matchers.any(), capturer); 381 bind(Foo.class).annotatedWith(named("pk")).toProvider(FooP.class); 382 try { 383 bind(Foo.class) 384 .annotatedWith(named("cxtr")) 385 .toConstructor(Foo.class.getDeclaredConstructor()); 386 } catch (Exception ex) { 387 throw new RuntimeException(ex); 388 } 389 bind(LinkedFoo.class).to(Foo.class); 390 bind(Interface.class).toInstance(new Implementation()); 391 bindConstant().annotatedWith(named("constant")).to("MyConstant"); 392 } 393 394 @Provides 395 @Named("pi") 396 Foo provideFooBar() { 397 return new Foo(); 398 } 399 }); 400 401 // toInstance & constant bindings are notified in random order, at the very beginning. 402 assertEquals( 403 ImmutableSet.of(Key.get(Interface.class), Key.get(String.class, named("constant"))), 404 capturer.getAsSetAndClear()); 405 406 // simple binding 407 assertNotNull(injector.getInstance(Foo.class)); 408 assertEquals(of(Key.get(Foo.class)), capturer.getAndClear()); 409 410 // provider key binding -- notifies about provider & the object, always 411 assertNotNull(injector.getInstance(Key.get(Foo.class, named("pk")))); 412 assertEquals(of(Key.get(FooP.class), Key.get(Foo.class, named("pk"))), capturer.getAndClear()); 413 assertNotNull(injector.getInstance(Key.get(Foo.class, named("pk")))); 414 assertEquals(of(Key.get(FooP.class), Key.get(Foo.class, named("pk"))), capturer.getAndClear()); 415 416 // JIT provider key binding -- notifies about provider & the object, always 417 assertNotNull(injector.getInstance(JitFoo2.class)); 418 assertEquals(of(Key.get(JitFoo2P.class), Key.get(JitFoo2.class)), capturer.getAndClear()); 419 assertNotNull(injector.getInstance(JitFoo2.class)); 420 assertEquals(of(Key.get(JitFoo2P.class), Key.get(JitFoo2.class)), capturer.getAndClear()); 421 422 // provider instance binding -- just the object (not the provider) 423 assertNotNull(injector.getInstance(Key.get(Foo.class, named("pi")))); 424 assertEquals(of(Key.get(Foo.class, named("pi"))), capturer.getAndClear()); 425 426 // toConstructor binding 427 assertNotNull(injector.getInstance(Key.get(Foo.class, named("cxtr")))); 428 assertEquals(of(Key.get(Foo.class, named("cxtr"))), capturer.getAndClear()); 429 430 // linked binding -- notifies about the target (that's what's provisioned), not the link 431 assertNotNull(injector.getInstance(LinkedFoo.class)); 432 assertEquals(of(Key.get(Foo.class)), capturer.getAndClear()); 433 434 // JIT linked binding -- notifies about the target (that's what's provisioned), not the link 435 assertNotNull(injector.getInstance(JitFoo.class)); 436 assertEquals(of(Key.get(Foo.class)), capturer.getAndClear()); 437 } 438 testSingletonMatcher()439 public void testSingletonMatcher() { 440 final Counter counter = new Counter(); 441 Injector injector = 442 Guice.createInjector( 443 new AbstractModule() { 444 @Override 445 protected void configure() { 446 bindListener( 447 new AbstractMatcher<Binding<?>>() { 448 @Override 449 public boolean matches(Binding<?> t) { 450 return Scopes.isSingleton(t); 451 } 452 }, 453 counter); 454 } 455 }); 456 assertEquals(0, counter.count); 457 // no increment for getting Many. 458 injector.getInstance(Many.class); 459 assertEquals(0, counter.count); 460 // but an increment for getting Sole, since it's a singleton. 461 injector.getInstance(Sole.class); 462 assertEquals(1, counter.count); 463 } 464 testCallingBindingDotGetProviderDotGet()465 public void testCallingBindingDotGetProviderDotGet() { 466 Injector injector = 467 Guice.createInjector( 468 new AbstractModule() { 469 @Override 470 protected void configure() { 471 bindListener( 472 Matchers.any(), 473 new ProvisionListener() { 474 @Override 475 public <T> void onProvision(ProvisionInvocation<T> provision) { 476 provision.getBinding().getProvider().get(); // AGH! 477 } 478 }); 479 } 480 }); 481 482 try { 483 injector.getInstance(Sole.class); 484 fail(); 485 } catch (ProvisionException expected) { 486 // We don't really care what kind of error you get, we only care you get an error. 487 } 488 489 try { 490 injector.getInstance(Many.class); 491 fail(); 492 } catch (ProvisionException expected) { 493 // We don't really care what kind of error you get, we only care you get an error. 494 } 495 } 496 497 interface Interface {} 498 499 static class Implementation implements Interface {} 500 501 @Singleton 502 static class Sole {} 503 504 static class Many {} 505 506 @ImplementedBy(Foo.class) 507 static interface JitFoo {} 508 509 @ProvidedBy(JitFoo2P.class) 510 static class JitFoo2 {} 511 512 static interface LinkedFoo {} 513 514 static class Foo implements JitFoo, LinkedFoo {} 515 516 static class FooP implements Provider<Foo> { 517 @Override get()518 public Foo get() { 519 return new Foo(); 520 } 521 } 522 523 static class JitFoo2P implements Provider<JitFoo2> { 524 @Override get()525 public JitFoo2 get() { 526 return new JitFoo2(); 527 } 528 } 529 530 static class FooBomb { FooBomb()531 FooBomb() { 532 throw new RuntimeException("Retry, Abort, Fail"); 533 } 534 } 535 536 static class DependsOnFooBombInField { 537 @Inject FooBomb fooBomb; 538 } 539 540 static class DependsOnFooBombInCxtor { 541 @Inject DependsOnFooBombInCxtor(FooBomb fooBomb)542 DependsOnFooBombInCxtor(FooBomb fooBomb) {} 543 } 544 545 private static class Counter implements ProvisionListener { 546 int count = 0; 547 548 @Override onProvision(ProvisionInvocation<T> provision)549 public <T> void onProvision(ProvisionInvocation<T> provision) { 550 count++; 551 } 552 } 553 554 private static class CountAndCaptureExceptionListener implements ProvisionListener { 555 int beforeProvision = 0; 556 int afterProvision = 0; 557 AtomicReference<RuntimeException> capture = new AtomicReference<>(); 558 559 @Override onProvision(ProvisionInvocation<T> provision)560 public <T> void onProvision(ProvisionInvocation<T> provision) { 561 beforeProvision++; 562 try { 563 provision.provision(); 564 } catch (RuntimeException re) { 565 capture.set(re); 566 throw re; 567 } 568 afterProvision++; 569 } 570 } 571 572 private static class Capturer implements ProvisionListener { 573 List<Key> keys = Lists.newArrayList(); 574 575 @Override onProvision(ProvisionInvocation<T> provision)576 public <T> void onProvision(ProvisionInvocation<T> provision) { 577 keys.add(provision.getBinding().getKey()); 578 T provisioned = provision.provision(); 579 // InstanceBindings are the only kind of binding where the key can 580 // be an instanceof the provisioned, because it isn't linked to any 581 // direct implementation. I guess maybe it'd also be possible 582 // with a toConstructor binding... but we don't use that in our tests. 583 if (provision.getBinding() instanceof InstanceBinding) { 584 Class<? super T> expected = provision.getBinding().getKey().getRawType(); 585 assertTrue( 586 "expected instanceof: " + expected + ", but was: " + provisioned, 587 expected.isInstance(provisioned)); 588 } else { 589 assertEquals(provision.getBinding().getKey().getRawType(), provisioned.getClass()); 590 } 591 } 592 getAsSetAndClear()593 Set<Key> getAsSetAndClear() { 594 Set<Key> copy = ImmutableSet.copyOf(keys); 595 keys.clear(); 596 return copy; 597 } 598 getAndClear()599 List<Key> getAndClear() { 600 List<Key> copy = ImmutableList.copyOf(keys); 601 keys.clear(); 602 return copy; 603 } 604 } 605 606 private static class FailBeforeProvision implements ProvisionListener { 607 @Override onProvision(ProvisionInvocation<T> provision)608 public <T> void onProvision(ProvisionInvocation<T> provision) { 609 throw new RuntimeException("boo"); 610 } 611 } 612 613 private static class FailAfterProvision implements ProvisionListener { 614 @Override onProvision(ProvisionInvocation<T> provision)615 public <T> void onProvision(ProvisionInvocation<T> provision) { 616 provision.provision(); 617 throw new RuntimeException("boo"); 618 } 619 } 620 621 private static class JustProvision implements ProvisionListener { 622 @Override onProvision(ProvisionInvocation<T> provision)623 public <T> void onProvision(ProvisionInvocation<T> provision) { 624 provision.provision(); 625 } 626 } 627 628 private static class NoProvision implements ProvisionListener { 629 @Override onProvision(ProvisionInvocation<T> provision)630 public <T> void onProvision(ProvisionInvocation<T> provision) {} 631 } 632 633 private static class ProvisionTwice implements ProvisionListener { 634 @Override onProvision(ProvisionInvocation<T> provision)635 public <T> void onProvision(ProvisionInvocation<T> provision) { 636 provision.provision(); 637 provision.provision(); 638 } 639 } 640 641 private static class ChainAsserter implements ProvisionListener { 642 private final List<Class<?>> provisionList; 643 644 private final List<Class<?>> expected; 645 ChainAsserter(List<Class<?>> provisionList, Iterable<Class<?>> expected)646 public ChainAsserter(List<Class<?>> provisionList, Iterable<Class<?>> expected) { 647 this.provisionList = provisionList; 648 this.expected = ImmutableList.copyOf(expected); 649 } 650 651 @Override onProvision(ProvisionInvocation<T> provision)652 public <T> void onProvision(ProvisionInvocation<T> provision) { 653 List<Class<?>> actual = Lists.newArrayList(); 654 for (com.google.inject.spi.DependencyAndSource dep : provision.getDependencyChain()) { 655 actual.add(dep.getDependency().getKey().getRawType()); 656 } 657 assertEquals(expected, actual); 658 659 provisionList.add(provision.getBinding().getKey().getRawType()); 660 } 661 } 662 keyMatcher(final Class<?> clazz)663 private static Matcher<Binding<?>> keyMatcher(final Class<?> clazz) { 664 return new AbstractMatcher<Binding<?>>() { 665 @Override 666 public boolean matches(Binding<?> t) { 667 return t.getKey().equals(Key.get(clazz)); 668 } 669 }; 670 } 671 672 @SuppressWarnings("unchecked") 673 public void testDependencyChain() { 674 final List<Class<?>> pList = Lists.newArrayList(); 675 final List<Class<?>> totalList = Lists.newArrayList(); 676 Injector injector = 677 Guice.createInjector( 678 new AbstractModule() { 679 @Override 680 protected void configure() { 681 bind(Instance.class).toInstance(new Instance()); 682 bind(B.class).to(BImpl.class); 683 bind(D.class).toProvider(DP.class); 684 685 bindListener( 686 Matchers.any(), 687 new ProvisionListener() { 688 @Override 689 public <T> void onProvision(ProvisionInvocation<T> provision) { 690 totalList.add(provision.getBinding().getKey().getRawType()); 691 } 692 }); 693 694 // Build up a list of asserters for our dependency chains. 695 ImmutableList.Builder<Class<?>> chain = ImmutableList.builder(); 696 chain.add(Instance.class); 697 bindListener(keyMatcher(Instance.class), new ChainAsserter(pList, chain.build())); 698 699 chain.add(A.class); 700 bindListener(keyMatcher(A.class), new ChainAsserter(pList, chain.build())); 701 702 chain.add(B.class).add(BImpl.class); 703 bindListener(keyMatcher(BImpl.class), new ChainAsserter(pList, chain.build())); 704 705 chain.add(C.class); 706 bindListener(keyMatcher(C.class), new ChainAsserter(pList, chain.build())); 707 708 // the chain has D before DP even though DP is provisioned & notified first 709 // because we do DP because of D, and need DP to provision D. 710 chain.add(D.class).add(DP.class); 711 bindListener(keyMatcher(D.class), new ChainAsserter(pList, chain.build())); 712 bindListener(keyMatcher(DP.class), new ChainAsserter(pList, chain.build())); 713 714 chain.add(E.class); 715 bindListener(keyMatcher(E.class), new ChainAsserter(pList, chain.build())); 716 717 chain.add(F.class); 718 bindListener(keyMatcher(F.class), new ChainAsserter(pList, chain.build())); 719 } 720 721 @Provides 722 C c(D d) { 723 return new C() {}; 724 } 725 }); 726 injector.getInstance(Instance.class); 727 // make sure we're checking all of the chain asserters.. 728 assertEquals( 729 of(Instance.class, A.class, BImpl.class, C.class, DP.class, D.class, E.class, F.class), 730 pList); 731 // and make sure that nothing else was notified that we didn't expect. 732 assertEquals(totalList, pList); 733 } 734 735 public void testModuleRequestInjection() { 736 final AtomicBoolean notified = new AtomicBoolean(); 737 Guice.createInjector( 738 new AbstractModule() { 739 @Override 740 protected void configure() { 741 requestInjection( 742 new Object() { 743 @Inject Foo foo; 744 }); 745 bindListener( 746 Matchers.any(), 747 new SpecialChecker(Foo.class, getClass().getName() + ".configure(", notified)); 748 } 749 }); 750 assertTrue(notified.get()); 751 } 752 753 public void testToProviderInstance() { 754 final AtomicBoolean notified = new AtomicBoolean(); 755 Guice.createInjector( 756 new AbstractModule() { 757 @Override 758 protected void configure() { 759 bind(Object.class) 760 .toProvider( 761 new Provider<Object>() { 762 @Inject Foo foo; 763 764 @Override 765 public Object get() { 766 return null; 767 } 768 }); 769 bindListener( 770 Matchers.any(), 771 new SpecialChecker(Foo.class, getClass().getName() + ".configure(", notified)); 772 } 773 }); 774 assertTrue(notified.get()); 775 } 776 777 public void testInjectorInjectMembers() { 778 final Object object = 779 new Object() { 780 @Inject Foo foo; 781 }; 782 final AtomicBoolean notified = new AtomicBoolean(); 783 Guice.createInjector( 784 new AbstractModule() { 785 @Override 786 protected void configure() { 787 bindListener( 788 Matchers.any(), 789 new SpecialChecker(Foo.class, object.getClass().getName(), notified)); 790 } 791 }) 792 .injectMembers(object); 793 assertTrue(notified.get()); 794 } 795 796 private static class SpecialChecker implements ProvisionListener { 797 private final Class<?> notifyType; 798 799 private final String firstSource; 800 801 private final AtomicBoolean notified; 802 803 public SpecialChecker(Class<?> notifyType, String firstSource, AtomicBoolean notified) { 804 this.notifyType = notifyType; 805 this.firstSource = firstSource; 806 this.notified = notified; 807 } 808 809 @Override 810 public <T> void onProvision(ProvisionInvocation<T> provision) { 811 notified.set(true); 812 assertEquals(notifyType, provision.getBinding().getKey().getRawType()); 813 assertEquals(2, provision.getDependencyChain().size()); 814 815 assertNull(provision.getDependencyChain().get(0).getDependency()); 816 assertContains(provision.getDependencyChain().get(0).getBindingSource(), firstSource); 817 818 assertEquals( 819 notifyType, provision.getDependencyChain().get(1).getDependency().getKey().getRawType()); 820 assertContains( 821 provision.getDependencyChain().get(1).getBindingSource(), 822 notifyType.getName() + ".class("); 823 824 } 825 } 826 827 private static class Instance { 828 @Inject A a; 829 } 830 831 private static class A { 832 @Inject 833 A(B b) {} 834 } 835 836 private interface B {} 837 838 private static class BImpl implements B { 839 @Inject 840 void inject(C c) {} 841 } 842 843 private interface C {} 844 845 private interface D {} 846 847 private static class DP implements Provider<D> { 848 @Inject Provider<E> ep; 849 850 @Override 851 public D get() { 852 ep.get(); 853 return new D() {}; 854 } 855 } 856 857 private static class E { 858 @SuppressWarnings("unused") 859 @Inject 860 F f; 861 } 862 863 private static class F {} 864 865 public void testBindToInjectorWithListeningGivesSaneException() { 866 try { 867 Guice.createInjector( 868 new AbstractModule() { 869 @Override 870 protected void configure() { 871 bindListener(Matchers.any(), new Counter()); 872 bind(Injector.class).toProvider(Providers.<Injector>of(null)); 873 } 874 }); 875 fail(); 876 } catch (CreationException ce) { 877 assertContains( 878 ce.getMessage(), "Binding to core guice framework type is not allowed: Injector."); 879 } 880 } 881 882 public void testProvisionIsNotifiedAfterContextsClear() { 883 Injector injector = 884 Guice.createInjector( 885 new AbstractModule() { 886 @Override 887 protected void configure() { 888 bindListener( 889 Matchers.any(), 890 new ProvisionListener() { 891 @Override 892 public <T> void onProvision(ProvisionInvocation<T> provision) { 893 Object provisioned = provision.provision(); 894 if (provisioned instanceof X) { 895 ((X) provisioned).init(); 896 } else if (provisioned instanceof Y) { 897 X.createY = false; 898 ((Y) provisioned).init(); 899 } 900 } 901 }); 902 } 903 }); 904 905 X.createY = true; 906 X x = injector.getInstance(X.class); 907 assertNotSame(x, x.y.x); 908 assertFalse("x.id: " + x.id + ", x.y.x.id: " + x.y.x.id, x.id == x.y.x.id); 909 } 910 911 private static class X { 912 static final AtomicInteger COUNTER = new AtomicInteger(); 913 static boolean createY; 914 915 final int id = COUNTER.getAndIncrement(); 916 final Provider<Y> yProvider; 917 Y y; 918 919 @Inject 920 X(Provider<Y> yProvider) { 921 this.yProvider = yProvider; 922 } 923 924 void init() { 925 if (createY) { 926 this.y = yProvider.get(); 927 } 928 } 929 } 930 931 private static class Y { 932 final Provider<X> xProvider; 933 X x; 934 935 @Inject 936 Y(Provider<X> xProvider) { 937 this.xProvider = xProvider; 938 } 939 940 void init() { 941 this.x = xProvider.get(); 942 } 943 } 944 945 public void testDeDuplicateProvisionListeners() { 946 final Counter counter = new Counter(); 947 Injector injector = 948 Guice.createInjector( 949 new AbstractModule() { 950 @Override 951 protected void configure() { 952 bindListener(Matchers.any(), counter); 953 bindListener(Matchers.any(), counter); 954 } 955 }); 956 injector.getInstance(Many.class); 957 assertEquals("ProvisionListener not de-duplicated", 1, counter.count); 958 } 959 } 960