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 
setUp()56   @Override protected void setUp() throws Exception {
57     J.nextInstanceId = 0;
58     K.nextInstanceId = 0;
59   }
60 
testInject()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 
testQualifiedInject()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 
testProviderInject()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 
testScopeAnnotation()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 
testSingleton()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 
testEagerSingleton()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 
testScopesIsSingleton()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 
testInjectingFinalFieldsIsForbidden()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 
testInjectingAbstractMethodsIsForbidden()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 
testInjectingMethodsWithTypeParametersIsForbidden()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 
testInjectingMethodsWithNonVoidReturnTypes()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    */
testBindProviderClass()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 
testGuicify330Provider()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 
testGuicifyWithDependencies()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 
validateDependencies(Set<Dependency<?>> actual, Class<?> owner)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 
A(B b)339     @Inject A(B b) {
340       this.b = b;
341     }
342 
injectD(D d, E e)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 
F(@amed"jodie") B b)360     @Inject F(@Named("jodie") B b) {
361       this.b = b;
362     }
363 
injectD(@ed D d, @Named("jesse") E e)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 
G(@amed"jodie") Provider<B> bProvider)393     @Inject G(@Named("jodie") Provider<B> bProvider) {
394       this.bProvider = bProvider;
395     }
396 
injectD(@ed Provider<D> dProvider, Provider<E> eProvider)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 
scope(Key<T> key, final com.google.inject.Provider<T> unscoped)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 
reset()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
setB(B b)453     abstract void setB(B b);
454   }
455 
456   static class M extends AbstractM {
setB(B b)457     void setB(B b) {}
458   }
459 
460   static class N {
setB(B b)461     @Inject <T> void setB(B b) {}
462   }
463 
464   static class P {
setB(B b)465     @Inject B setB(B b) {
466       return b;
467     }
468   }
469 
470   static class BProvider implements Provider<B> {
get()471     public B get() {
472       return new B();
473     }
474   }
475 }
476