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