PageRenderTime 74ms CodeModel.GetById 35ms RepoModel.GetById 0ms app.codeStats 1ms

/core/test/com/google/inject/MembersInjectorTest.java

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