1 /* 2 * Copyright (C) 2007 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.spi; 18 19 import static com.google.inject.Asserts.assertContains; 20 import static java.lang.annotation.RetentionPolicy.RUNTIME; 21 22 import com.google.common.collect.ImmutableList; 23 import com.google.common.collect.ImmutableSet; 24 import com.google.common.collect.Iterables; 25 import com.google.common.collect.Lists; 26 import com.google.inject.AbstractModule; 27 import com.google.inject.Binder; 28 import com.google.inject.Binding; 29 import com.google.inject.BindingAnnotation; 30 import com.google.inject.CreationException; 31 import com.google.inject.Guice; 32 import com.google.inject.Inject; 33 import com.google.inject.Injector; 34 import com.google.inject.Key; 35 import com.google.inject.Module; 36 import com.google.inject.Provider; 37 import com.google.inject.Provides; 38 import com.google.inject.ProvisionException; 39 import com.google.inject.Singleton; 40 import com.google.inject.Stage; 41 import com.google.inject.TypeLiteral; 42 import com.google.inject.internal.Errors; 43 import com.google.inject.internal.InternalFlags; 44 import com.google.inject.internal.ProviderMethod; 45 import com.google.inject.internal.ProviderMethodsModule; 46 import com.google.inject.name.Named; 47 import com.google.inject.name.Names; 48 import com.google.inject.util.Providers; 49 import com.google.inject.util.Types; 50 import java.lang.annotation.ElementType; 51 import java.lang.annotation.Retention; 52 import java.lang.annotation.RetentionPolicy; 53 import java.lang.annotation.Target; 54 import java.lang.reflect.Method; 55 import java.util.ArrayList; 56 import java.util.Collection; 57 import java.util.List; 58 import java.util.Set; 59 import java.util.concurrent.atomic.AtomicReference; 60 import java.util.logging.Handler; 61 import java.util.logging.LogRecord; 62 import java.util.logging.Logger; 63 import junit.framework.TestCase; 64 65 /** @author crazybob@google.com (Bob Lee) */ 66 @SuppressWarnings("ProvidesMethodOutsideOfModule") 67 public class ProviderMethodsTest extends TestCase implements Module { 68 69 @SuppressWarnings("unchecked") testProviderMethods()70 public void testProviderMethods() { 71 Injector injector = Guice.createInjector(this); 72 73 Bob bob = injector.getInstance(Bob.class); 74 assertEquals("A Bob", bob.getName()); 75 76 Bob clone = injector.getInstance(Bob.class); 77 assertEquals("A Bob", clone.getName()); 78 79 assertNotSame(bob, clone); 80 assertSame(bob.getDaughter(), clone.getDaughter()); 81 82 Key soleBobKey = Key.get(Bob.class, Sole.class); 83 assertSame(injector.getInstance(soleBobKey), injector.getInstance(soleBobKey)); 84 } 85 86 @Override configure(Binder binder)87 public void configure(Binder binder) {} 88 89 interface Bob { getName()90 String getName(); 91 getDaughter()92 Dagny getDaughter(); 93 } 94 95 interface Dagny { getAge()96 int getAge(); 97 } 98 99 @Provides provideBob(final Dagny dagny)100 Bob provideBob(final Dagny dagny) { 101 return new Bob() { 102 @Override 103 public String getName() { 104 return "A Bob"; 105 } 106 107 @Override 108 public Dagny getDaughter() { 109 return dagny; 110 } 111 }; 112 } 113 114 @Provides 115 @Singleton 116 @Sole 117 Bob provideSoleBob(final Dagny dagny) { 118 return new Bob() { 119 @Override 120 public String getName() { 121 return "Only Bob"; 122 } 123 124 @Override 125 public Dagny getDaughter() { 126 return dagny; 127 } 128 }; 129 } 130 131 @Provides 132 @Singleton 133 Dagny provideDagny() { 134 return new Dagny() { 135 @Override 136 public int getAge() { 137 return 1; 138 } 139 }; 140 } 141 142 @Retention(RUNTIME) 143 @Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD}) 144 @BindingAnnotation 145 @interface Sole {} 146 147 public void testCircularDependency() { 148 Injector injector = 149 Guice.createInjector( 150 new AbstractModule() { 151 152 @Provides 153 Foo newFoo(final Bar bar) { 154 return new Foo() { 155 156 @Override 157 public Bar getBar() { 158 return bar; 159 } 160 161 @Override 162 public int getI() { 163 return 5; 164 } 165 }; 166 } 167 168 @Provides 169 Bar newBar(final Foo foo) { 170 return new Bar() { 171 172 @Override 173 public Foo getFoo() { 174 return foo; 175 } 176 177 @Override 178 public int getI() { 179 return 10; 180 } 181 }; 182 } 183 }); 184 185 Foo foo = injector.getInstance(Foo.class); 186 assertEquals(5, foo.getI()); 187 assertEquals(10, foo.getBar().getI()); 188 assertEquals(5, foo.getBar().getFoo().getI()); 189 } 190 191 public interface Foo { 192 Bar getBar(); 193 194 int getI(); 195 } 196 197 public interface Bar { 198 Foo getFoo(); 199 200 int getI(); 201 } 202 203 public void testMultipleBindingAnnotations() { 204 try { 205 Guice.createInjector( 206 new AbstractModule() { 207 208 @Provides 209 @Named("A") 210 @Blue 211 public String provideString() { 212 return "a"; 213 } 214 }); 215 fail(); 216 } catch (CreationException expected) { 217 assertContains( 218 expected.getMessage(), 219 "more than one annotation annotated with @BindingAnnotation:", 220 "Named", 221 "Blue", 222 "at " + getClass().getName(), 223 ".provideString(ProviderMethodsTest.java:"); 224 } 225 } 226 227 @Retention(RUNTIME) 228 @BindingAnnotation 229 @interface Blue {} 230 231 public void testGenericProviderMethods() { 232 Injector injector = 233 Guice.createInjector(new ProvideTs<String>("A", "B") {}, new ProvideTs<Integer>(1, 2) {}); 234 235 assertEquals("A", injector.getInstance(Key.get(String.class, Names.named("First")))); 236 assertEquals("B", injector.getInstance(Key.get(String.class, Names.named("Second")))); 237 assertEquals( 238 ImmutableSet.of("A", "B"), injector.getInstance(Key.get(Types.setOf(String.class)))); 239 240 assertEquals(1, injector.getInstance(Key.get(Integer.class, Names.named("First"))).intValue()); 241 assertEquals(2, injector.getInstance(Key.get(Integer.class, Names.named("Second"))).intValue()); 242 assertEquals(ImmutableSet.of(1, 2), injector.getInstance(Key.get(Types.setOf(Integer.class)))); 243 } 244 245 abstract class ProvideTs<T> extends AbstractModule { 246 final T first; 247 final T second; 248 249 protected ProvideTs(T first, T second) { 250 this.first = first; 251 this.second = second; 252 } 253 254 @Named("First") 255 @Provides 256 T provideFirst() { 257 return first; 258 } 259 260 @Named("Second") 261 @Provides 262 T provideSecond() { 263 return second; 264 } 265 266 @Provides 267 Set<T> provideBoth(@Named("First") T first, @Named("Second") T second) { 268 return ImmutableSet.of(first, second); 269 } 270 } 271 272 public void testAutomaticProviderMethods() { 273 Injector injector = 274 Guice.createInjector( 275 (Module) 276 new AbstractModule() { 277 278 private int next = 1; 279 280 @Provides 281 @Named("count") 282 public Integer provideCount() { 283 return next++; 284 } 285 }); 286 287 assertEquals(1, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue()); 288 assertEquals(2, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue()); 289 assertEquals(3, injector.getInstance(Key.get(Integer.class, Names.named("count"))).intValue()); 290 } 291 292 /** 293 * If the user installs provider methods for the module manually, that shouldn't cause a double 294 * binding of the provider methods' types. 295 */ 296 public void testAutomaticProviderMethodsDoNotCauseDoubleBinding() { 297 Module installsSelf = 298 new AbstractModule() { 299 @Override 300 protected void configure() { 301 install(this); 302 bind(Integer.class).toInstance(5); 303 } 304 305 @Provides 306 public String provideString(Integer count) { 307 return "A" + count; 308 } 309 }; 310 311 Injector injector = Guice.createInjector(installsSelf); 312 assertEquals("A5", injector.getInstance(String.class)); 313 } 314 315 public void testWildcardProviderMethods() { 316 final List<String> strings = ImmutableList.of("A", "B", "C"); 317 final List<Number> numbers = ImmutableList.<Number>of(1, 2, 3); 318 319 Injector injector = 320 Guice.createInjector( 321 new AbstractModule() { 322 @Override 323 protected void configure() { 324 @SuppressWarnings("unchecked") 325 Key<List<? super Integer>> listOfSupertypesOfInteger = 326 (Key<List<? super Integer>>) 327 Key.get(Types.listOf(Types.supertypeOf(Integer.class))); 328 bind(listOfSupertypesOfInteger).toInstance(numbers); 329 } 330 331 @Provides 332 public List<? extends CharSequence> provideCharSequences() { 333 return strings; 334 } 335 336 @Provides 337 public Class<?> provideType() { 338 return Float.class; 339 } 340 }); 341 342 assertSame(strings, injector.getInstance(HasWildcardInjection.class).charSequences); 343 assertSame(numbers, injector.getInstance(HasWildcardInjection.class).numbers); 344 assertSame(Float.class, injector.getInstance(HasWildcardInjection.class).type); 345 } 346 347 static class HasWildcardInjection { 348 @Inject List<? extends CharSequence> charSequences; 349 @Inject List<? super Integer> numbers; 350 @Inject Class<?> type; 351 } 352 353 public void testProviderMethodDependenciesAreExposed() throws Exception { 354 Module module = 355 new AbstractModule() { 356 @Override 357 protected void configure() { 358 bind(Integer.class).toInstance(50); 359 bindConstant().annotatedWith(Names.named("units")).to("Kg"); 360 } 361 362 @Provides 363 @Named("weight") 364 String provideWeight(Integer count, @Named("units") String units) { 365 return count + units; 366 } 367 }; 368 Injector injector = Guice.createInjector(module); 369 370 ProviderInstanceBinding<?> binding = 371 (ProviderInstanceBinding<?>) 372 injector.getBinding(Key.get(String.class, Names.named("weight"))); 373 Method method = 374 module.getClass().getDeclaredMethod("provideWeight", Integer.class, String.class); 375 InjectionPoint point = new InjectionPoint(TypeLiteral.get(module.getClass()), method, false); 376 assertEquals( 377 ImmutableSet.<Dependency<?>>of( 378 new Dependency<Integer>(point, Key.get(Integer.class), false, 0), 379 new Dependency<String>(point, Key.get(String.class, Names.named("units")), false, 1)), 380 binding.getDependencies()); 381 } 382 383 public void testNonModuleProviderMethods() { 384 final Object methodsObject = 385 new Object() { 386 @Provides 387 @Named("foo") 388 String provideFoo() { 389 return "foo-value"; 390 } 391 }; 392 393 Module module = 394 new AbstractModule() { 395 @Override 396 protected void configure() { 397 install(ProviderMethodsModule.forObject(methodsObject)); 398 } 399 }; 400 401 Injector injector = Guice.createInjector(module); 402 403 Key<String> key = Key.get(String.class, Names.named("foo")); 404 assertEquals("foo-value", injector.getInstance(key)); 405 406 // Test the provider method object itself. This makes sure getInstance works, since GIN uses it 407 List<Element> elements = Elements.getElements(module); 408 assertEquals(1, elements.size()); 409 410 Element element = elements.get(0); 411 assertTrue( 412 element + " instanceof ProviderInstanceBinding", 413 element instanceof ProviderInstanceBinding); 414 415 ProviderInstanceBinding binding = (ProviderInstanceBinding) element; 416 javax.inject.Provider provider = binding.getUserSuppliedProvider(); 417 assertTrue(provider instanceof ProviderMethod); 418 assertEquals(methodsObject, ((ProviderMethod) provider).getInstance()); 419 assertSame(provider, binding.getProviderInstance()); 420 } 421 422 public void testVoidProviderMethods() { 423 try { 424 Guice.createInjector( 425 new AbstractModule() { 426 427 @Provides 428 void provideFoo() {} 429 }); 430 fail(); 431 } catch (CreationException expected) { 432 assertContains( 433 expected.getMessage(), 434 "1) Provider methods must return a value. Do not return void.", 435 getClass().getName(), 436 ".provideFoo(ProviderMethodsTest.java:"); 437 } 438 } 439 440 public void testInjectsJustOneLogger() { 441 AtomicReference<Logger> loggerRef = new AtomicReference<>(); 442 Injector injector = Guice.createInjector(new FooModule(loggerRef)); 443 444 assertNull(loggerRef.get()); 445 injector.getInstance(Integer.class); 446 Logger lastLogger = loggerRef.getAndSet(null); 447 assertNotNull(lastLogger); 448 injector.getInstance(Integer.class); 449 assertSame(lastLogger, loggerRef.get()); 450 451 assertEquals(FooModule.class.getName(), lastLogger.getName()); 452 } 453 454 private static class FooModule extends AbstractModule { 455 private final AtomicReference<Logger> loggerRef; 456 457 public FooModule(AtomicReference<Logger> loggerRef) { 458 this.loggerRef = loggerRef; 459 } 460 461 @SuppressWarnings("unused") 462 @Provides 463 Integer foo(Logger logger) { 464 loggerRef.set(logger); 465 return 42; 466 } 467 } 468 469 public void testSpi() throws Exception { 470 Module m1 = 471 new AbstractModule() { 472 473 @Provides 474 @Named("foo") 475 String provideFoo(Integer dep) { 476 return "foo"; 477 } 478 }; 479 Module m2 = 480 new AbstractModule() { 481 482 @Provides 483 Integer provideInt(@Named("foo") String dep) { 484 return 42; 485 } 486 }; 487 Injector injector = Guice.createInjector(m1, m2); 488 489 Binding<String> stringBinding = injector.getBinding(Key.get(String.class, Names.named("foo"))); 490 ProvidesMethodBinding<String> stringMethod = 491 stringBinding.acceptTargetVisitor(new BindingCapturer<String>()); 492 assertEquals(m1, stringMethod.getEnclosingInstance()); 493 assertEquals( 494 m1.getClass().getDeclaredMethod("provideFoo", Integer.class), stringMethod.getMethod()); 495 assertEquals( 496 ((HasDependencies) stringBinding).getDependencies(), stringMethod.getDependencies()); 497 assertEquals(Key.get(String.class, Names.named("foo")), stringMethod.getKey()); 498 499 Binding<Integer> intBinding = injector.getBinding(Integer.class); 500 ProvidesMethodBinding<Integer> intMethod = 501 intBinding.acceptTargetVisitor(new BindingCapturer<Integer>()); 502 assertEquals(m2, intMethod.getEnclosingInstance()); 503 assertEquals( 504 m2.getClass().getDeclaredMethod("provideInt", String.class), intMethod.getMethod()); 505 assertEquals(((HasDependencies) intBinding).getDependencies(), intMethod.getDependencies()); 506 assertEquals(Key.get(Integer.class), intMethod.getKey()); 507 } 508 509 private static class BindingCapturer<T> 510 extends DefaultBindingTargetVisitor<T, ProvidesMethodBinding<T>> 511 implements ProvidesMethodTargetVisitor<T, ProvidesMethodBinding<T>> { 512 513 @Override 514 @SuppressWarnings("unchecked") 515 public ProvidesMethodBinding<T> visit( 516 ProvidesMethodBinding<? extends T> providesMethodBinding) { 517 return (ProvidesMethodBinding<T>) providesMethodBinding; 518 } 519 520 @Override 521 protected ProvidesMethodBinding<T> visitOther(Binding<? extends T> binding) { 522 throw new IllegalStateException("unexpected visit of: " + binding); 523 } 524 } 525 526 public void testProvidesMethodVisibility() { 527 Injector injector = Guice.createInjector(new VisibilityModule()); 528 529 assertEquals(42, injector.getInstance(Integer.class).intValue()); 530 assertEquals(42L, injector.getInstance(Long.class).longValue()); 531 assertEquals(42D, injector.getInstance(Double.class).doubleValue(), 0.0); 532 assertEquals(42F, injector.getInstance(Float.class).floatValue(), 0.0f); 533 } 534 535 private static class VisibilityModule extends AbstractModule { 536 537 @SuppressWarnings("unused") 538 @Provides 539 Integer foo() { 540 return 42; 541 } 542 543 @SuppressWarnings("unused") 544 @Provides 545 private Long bar() { 546 return 42L; 547 } 548 549 @SuppressWarnings("unused") 550 @Provides 551 protected Double baz() { 552 return 42D; 553 } 554 555 @SuppressWarnings("unused") 556 @Provides 557 public Float quux() { 558 return 42F; 559 } 560 } 561 562 public void testProvidesMethodInheritenceHierarchy() { 563 try { 564 Guice.createInjector(new Sub1Module(), new Sub2Module()); 565 fail("Expected injector creation failure"); 566 } catch (CreationException expected) { 567 // both of our super class bindings cause errors 568 assertContains( 569 expected.getMessage(), 570 "A binding to java.lang.Long was already configured", 571 "A binding to java.lang.Integer was already configured"); 572 } 573 } 574 575 public void testProvidesMethodsDefinedInSuperClass() { 576 Injector injector = Guice.createInjector(new Sub1Module()); 577 assertEquals(42, injector.getInstance(Integer.class).intValue()); 578 assertEquals(42L, injector.getInstance(Long.class).longValue()); 579 assertEquals(42D, injector.getInstance(Double.class).doubleValue(), 0.0); 580 } 581 582 private static class BaseModule extends AbstractModule { 583 584 @Provides 585 Integer foo() { 586 return 42; 587 } 588 589 @Provides 590 Long bar() { 591 return 42L; 592 } 593 } 594 595 private static class Sub1Module extends BaseModule { 596 @Provides 597 Double baz() { 598 return 42D; 599 } 600 } 601 602 private static class Sub2Module extends BaseModule { 603 @Provides 604 Float quux() { 605 return 42F; 606 } 607 } 608 609 /*if[AOP]*/ 610 public void testShareFastClass() { 611 CallerInspecterModule module = new CallerInspecterModule(); 612 Guice.createInjector(Stage.PRODUCTION, module); 613 assertEquals(module.fooCallerClass, module.barCallerClass); 614 assertTrue(module.fooCallerClass.contains("$$FastClassByGuice$$")); 615 } 616 617 private static class CallerInspecterModule extends AbstractModule { 618 // start them off as unequal 619 String barCallerClass = "not_set_bar"; 620 String fooCallerClass = "not_set_foo"; 621 622 @Provides 623 @Singleton 624 Integer foo() { 625 this.fooCallerClass = new Exception().getStackTrace()[1].getClassName(); 626 return 42; 627 } 628 629 @Provides 630 @Singleton 631 Long bar() { 632 this.barCallerClass = new Exception().getStackTrace()[1].getClassName(); 633 return 42L; 634 } 635 } 636 637 public void testShareFastClassWithSuperClass() { 638 CallerInspecterSubClassModule module = new CallerInspecterSubClassModule(); 639 Guice.createInjector(Stage.PRODUCTION, module); 640 assertEquals( 641 "Expected provider methods in the same class to share fastclass classes", 642 module.fooCallerClass, 643 module.barCallerClass); 644 assertFalse( 645 "Did not expect provider methods in the subclasses to share fastclass classes " 646 + "with their parent classes", 647 module.bazCallerClass.equals(module.barCallerClass)); 648 } 649 650 private static class CallerInspecterSubClassModule extends CallerInspecterModule { 651 String bazCallerClass; 652 653 @Override 654 protected void configure() {} 655 656 @Provides 657 @Singleton 658 Double baz() { 659 this.bazCallerClass = new Exception().getStackTrace()[1].getClassName(); 660 return 42D; 661 } 662 } 663 /*end[AOP]*/ 664 665 static class SuperClassModule extends AbstractModule { 666 667 @Provides 668 Number providerMethod() { 669 return 1D; 670 } 671 672 @Provides 673 @Named("rawlist") 674 List rawProvider(@Named("list") List<String> f) { 675 return f; 676 } 677 678 @Provides 679 @Named("unrawlist") 680 List<String> rawParameterProvider(@Named("rawlist") List f) { 681 return f; 682 } 683 684 @Provides 685 @Named("list") 686 List<String> annotatedGenericProviderMethod() { 687 return new ArrayList<String>(); 688 } 689 690 @Provides 691 @Named("collection") 692 Collection<String> annotatedGenericParameterProviderMethod(@Named("list") List<String> foo) { 693 return foo; 694 } 695 696 @Provides 697 private String privateProviderMethod() { 698 return "hello"; 699 } 700 } 701 702 public void testOverrideProviderMethod_overrideHasProvides() { 703 class SubClassModule extends SuperClassModule { 704 @Override 705 @Provides 706 Number providerMethod() { 707 return 2D; 708 } 709 } 710 try { 711 Guice.createInjector(new SubClassModule()); 712 fail(); 713 } catch (CreationException e) { 714 assertContains( 715 e.getMessage(), 716 "Overriding @Provides methods is not allowed.", 717 "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()", 718 "overridden by: " + SubClassModule.class.getName() + ".providerMethod()"); 719 } 720 } 721 722 public void testOverrideProviderMethod_overrideHasProvides_withNewAnnotation() { 723 class SubClassModule extends SuperClassModule { 724 @Override 725 @Provides 726 @Named("foo") 727 Number providerMethod() { 728 return 2D; 729 } 730 } 731 try { 732 Guice.createInjector(new SubClassModule()); 733 fail(); 734 } catch (CreationException e) { 735 assertContains( 736 e.getMessage(), 737 "Overriding @Provides methods is not allowed.", 738 "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()", 739 "overridden by: " + SubClassModule.class.getName() + ".providerMethod()"); 740 } 741 } 742 743 public void testOverrideProviderMethod_overrideDoesntHaveProvides() { 744 class SubClassModule extends SuperClassModule { 745 @Override 746 Number providerMethod() { 747 return 2D; 748 } 749 } 750 try { 751 Guice.createInjector(new SubClassModule()); 752 fail(); 753 } catch (CreationException e) { 754 assertContains( 755 e.getMessage(), 756 "Overriding @Provides methods is not allowed.", 757 "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()", 758 "overridden by: " + SubClassModule.class.getName() + ".providerMethod()"); 759 } 760 } 761 762 public void testOverrideProviderMethod_overrideDoesntHaveProvides_withNewAnnotation() { 763 class SubClassModule extends SuperClassModule { 764 @Override 765 @Named("foo") 766 Number providerMethod() { 767 return 2D; 768 } 769 } 770 try { 771 Guice.createInjector(new SubClassModule()); 772 fail(); 773 } catch (CreationException e) { 774 assertContains( 775 e.getMessage(), 776 "Overriding @Provides methods is not allowed.", 777 "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()", 778 "overridden by: " + SubClassModule.class.getName() + ".providerMethod()"); 779 } 780 } 781 782 public void testOverrideProviderMethod_covariantOverrideDoesntHaveProvides() { 783 class SubClassModule extends SuperClassModule { 784 @Override 785 Double providerMethod() { 786 return 2D; 787 } 788 } 789 try { 790 Guice.createInjector(new SubClassModule()); 791 fail(); 792 } catch (CreationException e) { 793 assertContains( 794 e.getMessage(), 795 "Overriding @Provides methods is not allowed.", 796 "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()", 797 "overridden by: " + SubClassModule.class.getName() + ".providerMethod()"); 798 } 799 } 800 801 public void testOverrideProviderMethod_covariantOverrideHasProvides() { 802 class SubClassModule extends SuperClassModule { 803 @Override 804 @Provides 805 Double providerMethod() { 806 return 2D; 807 } 808 } 809 try { 810 Guice.createInjector(new SubClassModule()); 811 fail(); 812 } catch (CreationException e) { 813 assertContains( 814 e.getMessage(), 815 "Overriding @Provides methods is not allowed.", 816 "@Provides method: " + SuperClassModule.class.getName() + ".providerMethod()", 817 "overridden by: " + SubClassModule.class.getName() + ".providerMethod()"); 818 } 819 } 820 821 public void testOverrideProviderMethod_fakeOverridePrivateMethod() { 822 class SubClassModule extends SuperClassModule { 823 // not actually an override, just looks like it 824 String privateProviderMethod() { 825 return "sub"; 826 } 827 } 828 assertEquals("hello", Guice.createInjector(new SubClassModule()).getInstance(String.class)); 829 } 830 831 public void testOverrideProviderMethod_subclassRawTypes_returnType() { 832 class SubClassModule extends SuperClassModule { 833 @Override 834 List annotatedGenericProviderMethod() { 835 return super.annotatedGenericProviderMethod(); 836 } 837 } 838 try { 839 Guice.createInjector(new SubClassModule()); 840 fail(); 841 } catch (CreationException e) { 842 assertContains( 843 e.getMessage(), 844 "Overriding @Provides methods is not allowed.", 845 "@Provides method: " 846 + SuperClassModule.class.getName() 847 + ".annotatedGenericProviderMethod()", 848 "overridden by: " + SubClassModule.class.getName() + ".annotatedGenericProviderMethod()"); 849 } 850 } 851 852 public void testOverrideProviderMethod_subclassRawTypes_parameterType() { 853 class SubClassModule extends SuperClassModule { 854 @Override 855 Collection<String> annotatedGenericParameterProviderMethod(List foo) { 856 return super.annotatedGenericParameterProviderMethod(foo); 857 } 858 } 859 try { 860 Guice.createInjector(new SubClassModule()); 861 fail(); 862 } catch (CreationException e) { 863 assertContains( 864 e.getMessage(), 865 "Overriding @Provides methods is not allowed.", 866 "@Provides method: " 867 + SuperClassModule.class.getName() 868 + ".annotatedGenericParameterProviderMethod()", 869 "overridden by: " 870 + SubClassModule.class.getName() 871 + ".annotatedGenericParameterProviderMethod()"); 872 } 873 } 874 875 public void testOverrideProviderMethod_superclassRawTypes_returnType() { 876 class SubClassModule extends SuperClassModule { 877 // remove the rawtype from the override 878 @Override 879 List<String> rawProvider(List<String> f) { 880 return f; 881 } 882 } 883 try { 884 Guice.createInjector(new SubClassModule()); 885 fail(); 886 } catch (CreationException e) { 887 assertContains( 888 e.getMessage(), 889 "Overriding @Provides methods is not allowed.", 890 "@Provides method: " + SuperClassModule.class.getName() + ".rawProvider()", 891 "overridden by: " + SubClassModule.class.getName() + ".rawProvider()"); 892 } 893 } 894 895 abstract static class GenericSuperModule<T> extends AbstractModule { 896 @Provides 897 String provide(T thing) { 898 return thing.toString(); 899 } 900 } 901 902 // This is a tricky case where signatures don't match, but it is an override (facilitated via a 903 // bridge method) 904 public void testOverrideProviderMethod_erasureBasedOverrides() { 905 class SubClassModule extends GenericSuperModule<Integer> { 906 @Override 907 String provide(Integer thing) { 908 return thing.toString(); 909 } 910 911 @Override 912 protected void configure() { 913 bind(Integer.class).toInstance(3); 914 } 915 } 916 try { 917 Guice.createInjector(new SubClassModule()); 918 fail(); 919 } catch (CreationException e) { 920 assertContains( 921 e.getMessage(), 922 "Overriding @Provides methods is not allowed.", 923 "@Provides method: " + GenericSuperModule.class.getName() + ".provide()", 924 "overridden by: " + SubClassModule.class.getName() + ".provide()"); 925 } 926 } 927 928 static class RestrictedSuper extends AbstractModule { 929 @Provides 930 public String provideFoo() { 931 return "foo"; 932 } 933 934 } 935 936 public static class ExposedSub extends RestrictedSuper {} 937 938 public void testOverrideProviderMethod_increasedVisibility() { 939 // ensure we don't detect the synthetic provideFoo method in ExposedSub as an override (it is, 940 // but since it is synthetic it would be annoying to throw an error on it). 941 assertEquals("foo", Guice.createInjector(new ExposedSub()).getInstance(String.class)); 942 } 943 944 interface ProviderInterface<T> { 945 T getT(); 946 } 947 948 static class ModuleImpl extends AbstractModule implements ProviderInterface<String> { 949 950 @Override 951 @Provides 952 public String getT() { 953 return "string"; 954 } 955 956 @Provides 957 public Object getObject() { 958 return new Object(); 959 } 960 /* javac will synthesize a bridge method for getT with the types erased, equivalent to: 961 * @Provides public Object getT() { ... } 962 */ 963 } 964 965 public void testIgnoreSyntheticBridgeMethods() { 966 Guice.createInjector(new ModuleImpl()); 967 } 968 969 public void testScopedProviderMethodThrowsException() { 970 Injector injector = 971 Guice.createInjector( 972 new AbstractModule() { 973 974 @Provides 975 @Singleton 976 int provideInt() { 977 throw new RuntimeException("boom"); 978 } 979 }); 980 Provider<Integer> intProvider = injector.getProvider(Integer.class); 981 try { 982 intProvider.get(); 983 fail(); 984 } catch (ProvisionException pe) { 985 // by default assertContains asserts that the last item doesn't repeat... which is the main 986 // thing we are testing for 987 assertContains(pe.getMessage(), "java.lang.RuntimeException: boom", "provideInt"); 988 } 989 } 990 991 public void testNullability() throws Exception { 992 Module module = 993 new AbstractModule() { 994 @Override 995 protected void configure() { 996 bind(String.class).toProvider(Providers.<String>of(null)); 997 } 998 999 @SuppressWarnings("unused") 1000 @Provides 1001 Integer fail(String foo) { 1002 return 1; 1003 } 1004 1005 @SuppressWarnings("unused") 1006 @Provides 1007 Long succeed(@Nullable String foo) { 1008 return 2L; 1009 } 1010 }; 1011 Injector injector = Guice.createInjector(module); 1012 InjectionPoint fooPoint = 1013 InjectionPoint.forMethod( 1014 module.getClass().getDeclaredMethod("fail", String.class), 1015 TypeLiteral.get(module.getClass())); 1016 Dependency<?> fooDependency = Iterables.getOnlyElement(fooPoint.getDependencies()); 1017 1018 runNullableTest(injector, fooDependency, module); 1019 1020 injector.getInstance(Long.class); 1021 } 1022 1023 public void testModuleBindings() throws Exception { 1024 Module module = 1025 new AbstractModule() { 1026 1027 @Provides 1028 Integer fail() { 1029 return 1; 1030 } 1031 }; 1032 // sanity check that the injector works 1033 Injector injector = Guice.createInjector(module); 1034 assertEquals(1, injector.getInstance(Integer.class).intValue()); 1035 ProviderInstanceBinding injectorBinding = 1036 (ProviderInstanceBinding) injector.getBinding(Integer.class); 1037 assertEquals(1, injectorBinding.getUserSuppliedProvider().get()); 1038 1039 ProviderInstanceBinding moduleBinding = 1040 (ProviderInstanceBinding) Iterables.getOnlyElement(Elements.getElements(module)); 1041 try { 1042 moduleBinding.getUserSuppliedProvider().get(); 1043 fail(); 1044 } catch (IllegalStateException ise) { 1045 assertEquals( 1046 "This Provider cannot be used until the Injector has been created.", ise.getMessage()); 1047 } 1048 } 1049 1050 private void runNullableTest(Injector injector, Dependency<?> dependency, Module module) { 1051 switch (InternalFlags.getNullableProvidesOption()) { 1052 case ERROR: 1053 validateNullableFails(injector, module); 1054 break; 1055 case IGNORE: 1056 validateNullableIgnored(injector); 1057 break; 1058 case WARN: 1059 validateNullableWarns(injector, dependency); 1060 break; 1061 } 1062 } 1063 1064 private void validateNullableFails(Injector injector, Module module) { 1065 try { 1066 injector.getInstance(Integer.class); 1067 fail(); 1068 } catch (ProvisionException expected) { 1069 assertContains( 1070 expected.getMessage(), 1071 "1) null returned by binding at " + module.getClass().getName() + ".configure(", 1072 "but the 1st parameter of " + module.getClass().getName() + ".fail(", 1073 "is not @Nullable", 1074 "while locating java.lang.String", 1075 "for the 1st parameter of " + module.getClass().getName() + ".fail(", 1076 "while locating java.lang.Integer"); 1077 1078 assertEquals(1, expected.getErrorMessages().size()); 1079 } 1080 } 1081 1082 private void validateNullableIgnored(Injector injector) { 1083 injector.getInstance(Integer.class); // no exception 1084 } 1085 1086 private void validateNullableWarns(Injector injector, Dependency<?> dependency) { 1087 final List<LogRecord> logRecords = Lists.newArrayList(); 1088 final Handler fakeHandler = 1089 new Handler() { 1090 @Override 1091 public void publish(LogRecord logRecord) { 1092 logRecords.add(logRecord); 1093 } 1094 1095 @Override 1096 public void flush() {} 1097 1098 @Override 1099 public void close() throws SecurityException {} 1100 }; 1101 Logger.getLogger(Guice.class.getName()).addHandler(fakeHandler); 1102 try { 1103 injector.getInstance(Integer.class); // no exception, but assert it does log. 1104 LogRecord record = Iterables.getOnlyElement(logRecords); 1105 assertEquals( 1106 "Guice injected null into {0} (a {1}), please mark it @Nullable." 1107 + " Use -Dguice_check_nullable_provides_params=ERROR to turn this into an" 1108 + " error.", 1109 record.getMessage()); 1110 assertEquals(Errors.convert(dependency.getKey()), record.getParameters()[1]); 1111 } finally { 1112 Logger.getLogger(Guice.class.getName()).removeHandler(fakeHandler); 1113 } 1114 } 1115 1116 @Retention(RetentionPolicy.RUNTIME) 1117 @interface Nullable {} 1118 } 1119