• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.google.inject;
18 
19 import static com.google.inject.Asserts.assertContains;
20 
21 import com.google.inject.internal.Annotations;
22 import com.google.inject.name.Names;
23 import com.google.inject.util.Providers;
24 import java.util.concurrent.Callable;
25 import java.util.concurrent.Executors;
26 import java.util.concurrent.TimeUnit;
27 import java.util.concurrent.TimeoutException;
28 import java.util.concurrent.atomic.AtomicReference;
29 import javax.inject.Inject;
30 import junit.framework.AssertionFailedError;
31 import junit.framework.TestCase;
32 
33 /** @author jessewilson@google.com (Jesse Wilson) */
34 public class MembersInjectorTest extends TestCase {
35 
36   private static final long DEADLOCK_TIMEOUT_SECONDS = 1;
37 
38   private static final A<C> uninjectableA =
39       new A<C>() {
40         @Inject
41         @Override
42         void doNothing() {
43           throw new AssertionFailedError();
44         }
45       };
46 
47   private static final B uninjectableB =
48       new B() {
49         @Inject
50         @Override
51         void doNothing() {
52           throw new AssertionFailedError();
53         }
54       };
55 
56   private static final C myFavouriteC = new C();
57 
testMembersInjectorFromBinder()58   public void testMembersInjectorFromBinder() {
59     final AtomicReference<MembersInjector<A<C>>> aMembersInjectorReference =
60         new AtomicReference<MembersInjector<A<C>>>();
61     final AtomicReference<MembersInjector<B>> bMembersInjectorReference =
62         new AtomicReference<MembersInjector<B>>();
63 
64     Guice.createInjector(
65         new AbstractModule() {
66           @Override
67           protected void configure() {
68             MembersInjector<A<C>> aMembersInjector = getMembersInjector(new TypeLiteral<A<C>>() {});
69             try {
70               aMembersInjector.injectMembers(uninjectableA);
71               fail();
72             } catch (IllegalStateException expected) {
73               assertContains(
74                   expected.getMessage(),
75                   "This MembersInjector cannot be used until the Injector has been created.");
76             }
77 
78             MembersInjector<B> bMembersInjector = getMembersInjector(B.class);
79             try {
80               bMembersInjector.injectMembers(uninjectableB);
81               fail();
82             } catch (IllegalStateException expected) {
83               assertContains(
84                   expected.getMessage(),
85                   "This MembersInjector cannot be used until the Injector has been created.");
86             }
87 
88             aMembersInjectorReference.set(aMembersInjector);
89             bMembersInjectorReference.set(bMembersInjector);
90 
91             assertEquals(
92                 "MembersInjector<java.lang.String>", getMembersInjector(String.class).toString());
93 
94             bind(C.class).toInstance(myFavouriteC);
95           }
96         });
97 
98     A<C> injectableA = new A<>();
99     aMembersInjectorReference.get().injectMembers(injectableA);
100     assertSame(myFavouriteC, injectableA.t);
101     assertSame(myFavouriteC, injectableA.b.c);
102 
103     B injectableB = new B();
104     bMembersInjectorReference.get().injectMembers(injectableB);
105     assertSame(myFavouriteC, injectableB.c);
106 
107     B anotherInjectableB = new B();
108     bMembersInjectorReference.get().injectMembers(anotherInjectableB);
109     assertSame(myFavouriteC, anotherInjectableB.c);
110   }
111 
testMembersInjectorFromInjector()112   public void testMembersInjectorFromInjector() {
113     Injector injector =
114         Guice.createInjector(
115             new AbstractModule() {
116               @Override
117               protected void configure() {
118                 bind(C.class).toInstance(myFavouriteC);
119               }
120             });
121 
122     MembersInjector<A<C>> aMembersInjector =
123         injector.getMembersInjector(new TypeLiteral<A<C>>() {});
124     MembersInjector<B> bMembersInjector = injector.getMembersInjector(B.class);
125 
126     A<C> injectableA = new A<>();
127     aMembersInjector.injectMembers(injectableA);
128     assertSame(myFavouriteC, injectableA.t);
129     assertSame(myFavouriteC, injectableA.b.c);
130 
131     B injectableB = new B();
132     bMembersInjector.injectMembers(injectableB);
133     assertSame(myFavouriteC, injectableB.c);
134 
135     B anotherInjectableB = new B();
136     bMembersInjector.injectMembers(anotherInjectableB);
137     assertSame(myFavouriteC, anotherInjectableB.c);
138 
139     assertEquals(
140         "MembersInjector<java.lang.String>", injector.getMembersInjector(String.class).toString());
141   }
142 
testMembersInjectorWithNonInjectedTypes()143   public void testMembersInjectorWithNonInjectedTypes() {
144     Injector injector = Guice.createInjector();
145 
146     MembersInjector<NoInjectedMembers> membersInjector =
147         injector.getMembersInjector(NoInjectedMembers.class);
148 
149     membersInjector.injectMembers(new NoInjectedMembers());
150     membersInjector.injectMembers(new NoInjectedMembers());
151   }
152 
testInjectionFailure()153   public void testInjectionFailure() {
154     Injector injector = Guice.createInjector();
155 
156     MembersInjector<InjectionFailure> membersInjector =
157         injector.getMembersInjector(InjectionFailure.class);
158 
159     try {
160       membersInjector.injectMembers(new InjectionFailure());
161       fail();
162     } catch (ProvisionException expected) {
163       assertContains(
164           expected.getMessage(),
165           "1) Error injecting method, java.lang.ClassCastException: whoops, failure #1");
166     }
167   }
168 
testInjectionAppliesToSpecifiedType()169   public void testInjectionAppliesToSpecifiedType() {
170     Injector injector = Guice.createInjector();
171 
172     MembersInjector<Object> membersInjector = injector.getMembersInjector(Object.class);
173     membersInjector.injectMembers(new InjectionFailure());
174   }
175 
testInjectingMembersInjector()176   public void testInjectingMembersInjector() {
177     InjectsMembersInjector injectsMembersInjector =
178         Guice.createInjector(
179                 new AbstractModule() {
180                   @Override
181                   protected void configure() {
182                     bind(C.class).toInstance(myFavouriteC);
183                   }
184                 })
185             .getInstance(InjectsMembersInjector.class);
186 
187     A<C> a = new A<>();
188     injectsMembersInjector.aMembersInjector.injectMembers(a);
189     assertSame(myFavouriteC, a.t);
190     assertSame(myFavouriteC, a.b.c);
191   }
192 
testCannotBindMembersInjector()193   public void testCannotBindMembersInjector() {
194     try {
195       Guice.createInjector(
196           new AbstractModule() {
197             @Override
198             protected void configure() {
199               bind(MembersInjector.class).toProvider(Providers.<MembersInjector>of(null));
200             }
201           });
202       fail();
203     } catch (CreationException expected) {
204       assertContains(
205           expected.getMessage(),
206           "1) Binding to core guice framework type is not allowed: MembersInjector.");
207     }
208 
209     try {
210       Guice.createInjector(
211           new AbstractModule() {
212             @Override
213             protected void configure() {
214               bind(new TypeLiteral<MembersInjector<A<C>>>() {})
215                   .toProvider(Providers.<MembersInjector<A<C>>>of(null));
216             }
217           });
218       fail();
219     } catch (CreationException expected) {
220       assertContains(
221           expected.getMessage(),
222           "1) Binding to core guice framework type is not allowed: MembersInjector.");
223     }
224   }
225 
testInjectingMembersInjectorWithErrorsInDependencies()226   public void testInjectingMembersInjectorWithErrorsInDependencies() {
227     try {
228       Guice.createInjector().getInstance(InjectsBrokenMembersInjector.class);
229       fail();
230     } catch (ConfigurationException expected) {
231       assertContains(
232           expected.getMessage(),
233           "1) No implementation for " + Unimplemented.class.getName() + " was bound.",
234           "while locating " + Unimplemented.class.getName(),
235           "for field at " + A.class.getName() + ".t(MembersInjectorTest.java:",
236           "while locating com.google.inject.MembersInjector<",
237           "for field at " + InjectsBrokenMembersInjector.class.getName() + ".aMembersInjector(",
238           "while locating " + InjectsBrokenMembersInjector.class.getName());
239     }
240   }
241 
testLookupMembersInjectorBinding()242   public void testLookupMembersInjectorBinding() {
243     Injector injector =
244         Guice.createInjector(
245             new AbstractModule() {
246               @Override
247               protected void configure() {
248                 bind(C.class).toInstance(myFavouriteC);
249               }
250             });
251     MembersInjector<A<C>> membersInjector =
252         injector.getInstance(new Key<MembersInjector<A<C>>>() {});
253 
254     A<C> a = new A<>();
255     membersInjector.injectMembers(a);
256     assertSame(myFavouriteC, a.t);
257     assertSame(myFavouriteC, a.b.c);
258 
259     assertEquals(
260         "MembersInjector<java.lang.String>",
261         injector.getInstance(new Key<MembersInjector<String>>() {}).toString());
262   }
263 
testGettingRawMembersInjector()264   public void testGettingRawMembersInjector() {
265     Injector injector = Guice.createInjector();
266     try {
267       injector.getInstance(MembersInjector.class);
268       fail();
269     } catch (ConfigurationException expected) {
270       assertContains(
271           expected.getMessage(), "Cannot inject a MembersInjector that has no type parameter");
272     }
273   }
274 
testGettingAnnotatedMembersInjector()275   public void testGettingAnnotatedMembersInjector() {
276     Injector injector = Guice.createInjector();
277     try {
278       injector.getInstance(new Key<MembersInjector<String>>(Names.named("foo")) {});
279       fail();
280     } catch (ConfigurationException expected) {
281       assertContains(
282           expected.getMessage(),
283           "1) No implementation for com.google.inject.MembersInjector<java.lang.String> "
284               + "annotated with @com.google.inject.name.Named(value="
285               + Annotations.memberValueString("foo")
286               + ") was bound.");
287     }
288   }
289 
290   /** Callback for member injection. Uses a static type to be referable by getInstance(). */
291   abstract static class AbstractParallelMemberInjectionCallback {
292 
293     volatile boolean called = false;
294 
295     private final Thread mainThread;
296     private final Class<? extends AbstractParallelMemberInjectionCallback> otherCallbackClass;
297 
AbstractParallelMemberInjectionCallback( Class<? extends AbstractParallelMemberInjectionCallback> otherCallbackClass)298     AbstractParallelMemberInjectionCallback(
299         Class<? extends AbstractParallelMemberInjectionCallback> otherCallbackClass) {
300       this.mainThread = Thread.currentThread();
301       this.otherCallbackClass = otherCallbackClass;
302     }
303 
304     @Inject
callback(final Injector injector)305     void callback(final Injector injector) throws Exception {
306       called = true;
307       if (mainThread != Thread.currentThread()) {
308         // only execute logic on the main thread
309         return;
310       }
311       // verify that other callback can be finished on a separate thread
312       AbstractParallelMemberInjectionCallback otherCallback =
313           Executors.newSingleThreadExecutor()
314               .submit(
315                   new Callable<AbstractParallelMemberInjectionCallback>() {
316                     @Override
317                     public AbstractParallelMemberInjectionCallback call() throws Exception {
318                       return injector.getInstance(otherCallbackClass);
319                     }
320                   })
321               .get(DEADLOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS);
322       assertTrue(otherCallback.called);
323 
324       try {
325         // other thread would wait for callback to finish on this thread first
326         Executors.newSingleThreadExecutor()
327             .submit(
328                 new Callable<Object>() {
329                   @Override
330                   public Object call() throws Exception {
331                     return injector.getInstance(
332                         AbstractParallelMemberInjectionCallback.this.getClass());
333                   }
334                 })
335             .get(DEADLOCK_TIMEOUT_SECONDS, TimeUnit.SECONDS);
336         fail();
337       } catch (TimeoutException expected) {
338         // recursive call from another thread should time out
339         // as it would be waiting for this thread to finish
340       }
341     }
342   }
343 
344   static class ParallelMemberInjectionCallback1 extends AbstractParallelMemberInjectionCallback {
345 
ParallelMemberInjectionCallback1()346     ParallelMemberInjectionCallback1() {
347       super(ParallelMemberInjectionCallback2.class);
348     }
349   }
350 
351   static class ParallelMemberInjectionCallback2 extends AbstractParallelMemberInjectionCallback {
352 
ParallelMemberInjectionCallback2()353     ParallelMemberInjectionCallback2() {
354       super(ParallelMemberInjectionCallback1.class);
355     }
356   }
357 
358   /**
359    * Tests that member injections could happen in parallel.
360    *
361    * <p>Additional check that when member injection happen other threads would wait for it to finish
362    * to provide proper resolution order semantics.
363    */
364 
testMemberInjectorParallelization()365   public void testMemberInjectorParallelization() throws Exception {
366     final ParallelMemberInjectionCallback1 c1 = new ParallelMemberInjectionCallback1();
367     final ParallelMemberInjectionCallback2 c2 = new ParallelMemberInjectionCallback2();
368     Guice.createInjector(
369         new AbstractModule() {
370           @Override
371           protected void configure() {
372             bind(ParallelMemberInjectionCallback1.class).toInstance(c1);
373             bind(ParallelMemberInjectionCallback2.class).toInstance(c2);
374           }
375         });
376     assertTrue(c1.called);
377     assertTrue(c2.called);
378   }
379 
380   /** Member injection callback that injects itself. */
381   static class RecursiveMemberInjection {
382     boolean called = false;
383 
384     @Inject
callback(RecursiveMemberInjection recursiveMemberInjection)385     void callback(RecursiveMemberInjection recursiveMemberInjection) {
386       if (called) {
387         fail("Should not be called twice");
388       }
389       called = true;
390     }
391   }
392 
393   /** Verifies that member injection injecting itself would get a non initialized instance. */
testRecursiveMemberInjector()394   public void testRecursiveMemberInjector() throws Exception {
395     final RecursiveMemberInjection rmi = new RecursiveMemberInjection();
396     Guice.createInjector(
397         new AbstractModule() {
398           @Override
399           protected void configure() {
400             bind(RecursiveMemberInjection.class).toInstance(rmi);
401           }
402         });
403     assertTrue("Member injection should happen", rmi.called);
404   }
405 
406   static class A<T> {
407     @Inject B b;
408     @Inject T t;
409 
410     @Inject
doNothing()411     void doNothing() {}
412   }
413 
414   static class B {
415     @Inject C c;
416 
417     @Inject
doNothing()418     void doNothing() {}
419   }
420 
421   static class C {}
422 
423   static class NoInjectedMembers {}
424 
425   static class InjectionFailure {
426     int failures = 0;
427 
428     @Inject
fail()429     void fail() {
430       throw new ClassCastException("whoops, failure #" + (++failures));
431     }
432   }
433 
434   static class InjectsMembersInjector {
435     @Inject MembersInjector<A<C>> aMembersInjector;
436     @Inject A<B> ab;
437   }
438 
439   static class InjectsBrokenMembersInjector {
440     @Inject MembersInjector<A<Unimplemented>> aMembersInjector;
441   }
442 
443   static interface Unimplemented {}
444 }
445