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