1 /** 2 * Copyright (C) 2009 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.googlecode.guice; 18 19 import static com.google.inject.Asserts.assertContains; 20 import static java.lang.annotation.RetentionPolicy.RUNTIME; 21 22 import com.google.inject.AbstractModule; 23 import com.google.inject.Binding; 24 import com.google.inject.CreationException; 25 import com.google.inject.Guice; 26 import com.google.inject.Injector; 27 import com.google.inject.Key; 28 import com.google.inject.Scope; 29 import com.google.inject.Scopes; 30 import com.google.inject.Stage; 31 import com.google.inject.TypeLiteral; 32 import com.google.inject.name.Names; 33 import com.google.inject.spi.Dependency; 34 import com.google.inject.spi.HasDependencies; 35 import com.google.inject.util.Providers; 36 37 import junit.framework.TestCase; 38 39 import java.lang.annotation.Annotation; 40 import java.lang.annotation.Retention; 41 import java.util.Set; 42 43 import javax.inject.Inject; 44 import javax.inject.Named; 45 import javax.inject.Provider; 46 import javax.inject.Qualifier; 47 import javax.inject.Singleton; 48 49 public class Jsr330Test extends TestCase { 50 51 private final B b = new B(); 52 private final C c = new C(); 53 private final D d = new D(); 54 private final E e = new E(); 55 56 @Override protected void setUp() throws Exception { 57 J.nextInstanceId = 0; 58 K.nextInstanceId = 0; 59 } 60 61 public void testInject() { 62 Injector injector = Guice.createInjector(new AbstractModule() { 63 protected void configure() { 64 bind(B.class).toInstance(b); 65 bind(C.class).toInstance(c); 66 bind(D.class).toInstance(d); 67 bind(E.class).toInstance(e); 68 bind(A.class); 69 } 70 }); 71 72 A a = injector.getInstance(A.class); 73 assertSame(b, a.b); 74 assertSame(c, a.c); 75 assertSame(d, a.d); 76 assertSame(e, a.e); 77 } 78 79 public void testQualifiedInject() { 80 Injector injector = Guice.createInjector(new AbstractModule() { 81 protected void configure() { 82 bind(B.class).annotatedWith(Names.named("jodie")).toInstance(b); 83 bind(C.class).annotatedWith(Red.class).toInstance(c); 84 bind(D.class).annotatedWith(RED).toInstance(d); 85 bind(E.class).annotatedWith(Names.named("jesse")).toInstance(e); 86 bind(F.class); 87 } 88 }); 89 90 F f = injector.getInstance(F.class); 91 assertSame(b, f.b); 92 assertSame(c, f.c); 93 assertSame(d, f.d); 94 assertSame(e, f.e); 95 } 96 97 public void testProviderInject() { 98 Injector injector = Guice.createInjector(new AbstractModule() { 99 protected void configure() { 100 bind(B.class).annotatedWith(Names.named("jodie")).toInstance(b); 101 bind(C.class).toInstance(c); 102 bind(D.class).annotatedWith(RED).toInstance(d); 103 bind(E.class).toInstance(e); 104 bind(G.class); 105 } 106 }); 107 108 G g = injector.getInstance(G.class); 109 assertSame(b, g.bProvider.get()); 110 assertSame(c, g.cProvider.get()); 111 assertSame(d, g.dProvider.get()); 112 assertSame(e, g.eProvider.get()); 113 } 114 115 public void testScopeAnnotation() { 116 final TestScope scope = new TestScope(); 117 118 Injector injector = Guice.createInjector(new AbstractModule() { 119 protected void configure() { 120 bind(B.class).in(scope); 121 bind(C.class).in(TestScoped.class); 122 bindScope(TestScoped.class, scope); 123 } 124 }); 125 126 B b = injector.getInstance(B.class); 127 assertSame(b, injector.getInstance(B.class)); 128 assertSame(b, injector.getInstance(B.class)); 129 130 C c = injector.getInstance(C.class); 131 assertSame(c, injector.getInstance(C.class)); 132 assertSame(c, injector.getInstance(C.class)); 133 134 H h = injector.getInstance(H.class); 135 assertSame(h, injector.getInstance(H.class)); 136 assertSame(h, injector.getInstance(H.class)); 137 138 scope.reset(); 139 140 assertNotSame(b, injector.getInstance(B.class)); 141 assertNotSame(c, injector.getInstance(C.class)); 142 assertNotSame(h, injector.getInstance(H.class)); 143 } 144 145 public void testSingleton() { 146 Injector injector = Guice.createInjector(new AbstractModule() { 147 protected void configure() { 148 bind(B.class).in(Singleton.class); 149 } 150 }); 151 152 B b = injector.getInstance(B.class); 153 assertSame(b, injector.getInstance(B.class)); 154 assertSame(b, injector.getInstance(B.class)); 155 156 J j = injector.getInstance(J.class); 157 assertSame(j, injector.getInstance(J.class)); 158 assertSame(j, injector.getInstance(J.class)); 159 } 160 161 public void testEagerSingleton() { 162 Guice.createInjector(Stage.PRODUCTION, new AbstractModule() { 163 protected void configure() { 164 bind(J.class); 165 bind(K.class).in(Singleton.class); 166 } 167 }); 168 169 assertEquals(1, J.nextInstanceId); 170 assertEquals(1, K.nextInstanceId); 171 } 172 173 public void testScopesIsSingleton() { 174 Injector injector = Guice.createInjector(new AbstractModule() { 175 protected void configure() { 176 bind(J.class); 177 bind(K.class).in(Singleton.class); 178 } 179 }); 180 181 assertTrue(Scopes.isSingleton(injector.getBinding(J.class))); 182 assertTrue(Scopes.isSingleton(injector.getBinding(K.class))); 183 } 184 185 public void testInjectingFinalFieldsIsForbidden() { 186 try { 187 Guice.createInjector(new AbstractModule() { 188 protected void configure() { 189 bind(L.class); 190 } 191 }); 192 fail(); 193 } catch (CreationException expected) { 194 assertContains(expected.getMessage(), 195 "1) Injected field " + L.class.getName() + ".b cannot be final."); 196 } 197 } 198 199 public void testInjectingAbstractMethodsIsForbidden() { 200 try { 201 Guice.createInjector(new AbstractModule() { 202 protected void configure() { 203 bind(M.class); 204 } 205 }); 206 fail(); 207 } catch (CreationException expected) { 208 assertContains(expected.getMessage(), 209 "1) Injected method " + AbstractM.class.getName() + ".setB() cannot be abstract."); 210 } 211 } 212 213 public void testInjectingMethodsWithTypeParametersIsForbidden() { 214 try { 215 Guice.createInjector(new AbstractModule() { 216 protected void configure() { 217 bind(N.class); 218 } 219 }); 220 fail(); 221 } catch (CreationException expected) { 222 assertContains(expected.getMessage(), "1) Injected method " + N.class.getName() 223 + ".setB() cannot declare type parameters of its own."); 224 } 225 } 226 227 public void testInjectingMethodsWithNonVoidReturnTypes() { 228 Guice.createInjector(new AbstractModule() { 229 protected void configure() { 230 bind(P.class); 231 } 232 }); 233 } 234 235 /** 236 * This test verifies that we can compile bindings to provider instances 237 * whose compile-time type implements javax.inject.Provider but not 238 * com.google.inject.Provider. For binary compatibility, we don't (and won't) 239 * support binding to instances of javax.inject.Provider. 240 */ 241 public void testBindProviderClass() { 242 Injector injector = Guice.createInjector(new AbstractModule() { 243 protected void configure() { 244 bind(B.class).toProvider(BProvider.class); 245 bind(B.class).annotatedWith(Names.named("1")).toProvider(BProvider.class); 246 bind(B.class).annotatedWith(Names.named("2")).toProvider(Key.get(BProvider.class)); 247 bind(B.class).annotatedWith(Names.named("3")).toProvider(TypeLiteral.get(BProvider.class)); 248 } 249 }); 250 251 injector.getInstance(Key.get(B.class)); 252 injector.getInstance(Key.get(B.class, Names.named("1"))); 253 injector.getInstance(Key.get(B.class, Names.named("2"))); 254 injector.getInstance(Key.get(B.class, Names.named("3"))); 255 } 256 257 public void testGuicify330Provider() { 258 Provider<String> jsr330Provider = new Provider<String>() { 259 public String get() { 260 return "A"; 261 } 262 263 @Override public String toString() { 264 return "jsr330Provider"; 265 } 266 }; 267 268 com.google.inject.Provider<String> guicified = Providers.guicify(jsr330Provider); 269 assertEquals("guicified(jsr330Provider)", guicified.toString()); 270 assertEquals("A", guicified.get()); 271 272 // when you guicify the Guice-friendly, it's a no-op 273 assertSame(guicified, Providers.guicify(guicified)); 274 275 assertFalse(guicified instanceof HasDependencies); 276 } 277 278 public void testGuicifyWithDependencies() { 279 Provider<String> jsr330Provider = new Provider<String>() { 280 @Inject double d; 281 int i; 282 @Inject void injectMe(int i) { 283 this.i = i; 284 } 285 286 public String get() { 287 return d + "-" + i; 288 } 289 }; 290 291 final com.google.inject.Provider<String> guicified = 292 Providers.guicify(jsr330Provider); 293 assertTrue(guicified instanceof HasDependencies); 294 Set<Dependency<?>> actual = ((HasDependencies)guicified).getDependencies(); 295 validateDependencies(actual, jsr330Provider.getClass()); 296 297 Injector injector = Guice.createInjector(new AbstractModule() { 298 @Override 299 protected void configure() { 300 bind(String.class).toProvider(guicified); 301 bind(int.class).toInstance(1); 302 bind(double.class).toInstance(2.0d); 303 } 304 }); 305 306 Binding<String> binding = injector.getBinding(String.class); 307 assertEquals("2.0-1", binding.getProvider().get()); 308 validateDependencies(actual, jsr330Provider.getClass()); 309 } 310 311 private void validateDependencies(Set<Dependency<?>> actual, Class<?> owner) { 312 assertEquals(actual.toString(), 2, actual.size()); 313 Dependency<?> dDep = null; 314 Dependency<?> iDep = null; 315 for(Dependency<?> dep : actual) { 316 if(dep.getKey().equals(Key.get(Double.class))) { 317 dDep = dep; 318 } else if(dep.getKey().equals(Key.get(Integer.class))) { 319 iDep = dep; 320 } 321 } 322 assertNotNull(dDep); 323 assertNotNull(iDep); 324 assertEquals(TypeLiteral.get(owner), dDep.getInjectionPoint().getDeclaringType()); 325 assertEquals("d", dDep.getInjectionPoint().getMember().getName()); 326 assertEquals(-1, dDep.getParameterIndex()); 327 328 assertEquals(TypeLiteral.get(owner), iDep.getInjectionPoint().getDeclaringType()); 329 assertEquals("injectMe", iDep.getInjectionPoint().getMember().getName()); 330 assertEquals(0, iDep.getParameterIndex()); 331 } 332 333 static class A { 334 final B b; 335 @Inject C c; 336 D d; 337 E e; 338 339 @Inject A(B b) { 340 this.b = b; 341 } 342 343 @Inject void injectD(D d, E e) { 344 this.d = d; 345 this.e = e; 346 } 347 } 348 349 static class B {} 350 static class C {} 351 static class D {} 352 static class E {} 353 354 static class F { 355 final B b; 356 @Inject @Red C c; 357 D d; 358 E e; 359 360 @Inject F(@Named("jodie") B b) { 361 this.b = b; 362 } 363 364 @Inject void injectD(@Red D d, @Named("jesse") E e) { 365 this.d = d; 366 this.e = e; 367 } 368 } 369 370 @Qualifier @Retention(RUNTIME) 371 @interface Red {} 372 373 public static final Red RED = new Red() { 374 public Class<? extends Annotation> annotationType() { 375 return Red.class; 376 } 377 378 @Override public boolean equals(Object obj) { 379 return obj instanceof Red; 380 } 381 382 @Override public int hashCode() { 383 return 0; 384 } 385 }; 386 387 static class G { 388 final Provider<B> bProvider; 389 @Inject Provider<C> cProvider; 390 Provider<D> dProvider; 391 Provider<E> eProvider; 392 393 @Inject G(@Named("jodie") Provider<B> bProvider) { 394 this.bProvider = bProvider; 395 } 396 397 @Inject void injectD(@Red Provider<D> dProvider, Provider<E> eProvider) { 398 this.dProvider = dProvider; 399 this.eProvider = eProvider; 400 } 401 } 402 403 @javax.inject.Scope @Retention(RUNTIME) 404 @interface TestScoped {} 405 406 static class TestScope implements Scope { 407 private int now = 0; 408 409 public <T> com.google.inject.Provider<T> scope(Key<T> key, 410 final com.google.inject.Provider<T> unscoped) { 411 return new com.google.inject.Provider<T>() { 412 private T value; 413 private int snapshotTime = -1; 414 415 public T get() { 416 if (snapshotTime != now) { 417 value = unscoped.get(); 418 snapshotTime = now; 419 } 420 return value; 421 } 422 }; 423 } 424 425 public void reset() { 426 now++; 427 } 428 } 429 430 @TestScoped 431 static class H {} 432 433 @Singleton 434 static class J { 435 static int nextInstanceId = 0; 436 int instanceId = nextInstanceId++; 437 } 438 439 static class K { 440 static int nextInstanceId = 0; 441 int instanceId = nextInstanceId++; 442 } 443 444 static class L { 445 @SuppressWarnings("InjectJavaxInjectOnFinalField") 446 @Inject 447 final B b = null; 448 } 449 450 static abstract class AbstractM { 451 @SuppressWarnings("InjectJavaxInjectOnAbstractMethod") 452 @Inject 453 abstract void setB(B b); 454 } 455 456 static class M extends AbstractM { 457 void setB(B b) {} 458 } 459 460 static class N { 461 @Inject <T> void setB(B b) {} 462 } 463 464 static class P { 465 @Inject B setB(B b) { 466 return b; 467 } 468 } 469 470 static class BProvider implements Provider<B> { 471 public B get() { 472 return new B(); 473 } 474 } 475 } 476