001 /*****************************************************************************
002 * Copyright (c) PicoContainer Organization. All rights reserved. *
003 * ------------------------------------------------------------------------- *
004 * The software in this package is published under the terms of the BSD *
005 * style license a copy of which has been included with this distribution in *
006 * the LICENSE.txt file. *
007 * *
008 *****************************************************************************/
009
010 package org.picocontainer.injectors;
011
012 import static org.junit.Assert.assertNotNull;
013 import static org.junit.Assert.assertTrue;import static org.junit.Assert.assertSame;
014 import static org.junit.Assert.assertEquals;
015 import static org.junit.Assert.fail;
016 import org.junit.Test;
017 import org.junit.runner.RunWith;
018 import org.picocontainer.*;
019 import org.picocontainer.Injector;
020 import org.picocontainer.monitors.NullComponentMonitor;
021 import org.picocontainer.behaviors.Caching;
022 import org.picocontainer.containers.EmptyPicoContainer;
023 import org.picocontainer.containers.TransientPicoContainer;
024 import org.picocontainer.tck.AbstractComponentFactoryTest;
025 import static org.picocontainer.tck.MockFactory.mockeryWithCountingNamingScheme;
026 import org.jmock.integration.junit4.JMock;
027 import org.jmock.Mockery;
028 import org.jmock.Expectations;
029 import org.jmock.api.Action;
030 import org.jmock.api.Invocation;
031 import org.hamcrest.Description;
032
033 import java.lang.reflect.Method;
034 import java.lang.reflect.Constructor;
035 import java.lang.reflect.Member;
036 import java.lang.annotation.Retention;
037 import java.lang.annotation.RetentionPolicy;
038 import java.lang.annotation.Target;
039 import java.lang.annotation.ElementType;
040
041 @RunWith(JMock.class)
042 public class ReinjectionTestCase extends AbstractComponentFactoryTest {
043
044 private Mockery mockery = mockeryWithCountingNamingScheme();
045
046 @Retention(RetentionPolicy.RUNTIME)
047 @Target(value={ElementType.METHOD, ElementType.FIELD})
048 public @interface Hurrah {
049 }
050
051 public static interface INeedsShoe {
052 int doIt(String s);
053
054 Object getBar();
055
056 Object getString();
057 }
058
059 public static class NeedsShoe implements INeedsShoe {
060 private Shoe bar;
061 private String string;
062
063 public NeedsShoe(Shoe bar) {
064 this.bar = bar;
065 }
066
067 @Hurrah
068 public int doIt(String s) {
069 this.string = s;
070 return Integer.parseInt(s) / 2;
071 }
072
073 public int doInt(int s) {
074 this.string = "i="+ s;
075 return s/2;
076 }
077
078 public Object getBar() {
079 return bar;
080 }
081
082 public Object getString() {
083 return string;
084 }
085 public static enum M {
086 doIt("doIt", String.class);
087 private Method method;
088
089 M(String s, Class... paramTypes) {
090 try {
091 method = NeedsShoe.class.getMethod(s, paramTypes);
092 } catch (NoSuchMethodException e) {
093 throw new UnsupportedOperationException(e);
094 }
095 }
096 public Method toMethod() {
097 return method;
098 }
099 }
100 }
101
102 public static class Shoe {
103 }
104
105 private static Method DOIT_METHOD;
106
107 static {
108 try {
109 DOIT_METHOD = NeedsShoe.class.getMethod("doIt", String.class);
110 } catch (NoSuchMethodException e) {
111 e.printStackTrace();
112 }
113 }
114
115 @Test public void testCachedComponentCanBeReflectionMethodReinjectedByATransientChildContainer() {
116 cachedComponentCanBeReinjectedByATransientChildContainer(new MethodInjection(DOIT_METHOD));
117 }
118
119 @Test public void testCachedComponentCanBeMethodNameReinjectedByATransientChildContainer() {
120 cachedComponentCanBeReinjectedByATransientChildContainer(new MethodInjection("doIt"));
121 }
122
123 @Test public void testCachedComponentCanBeAnnotatedMethodReinjectedByATransientChildContainer() {
124 cachedComponentCanBeReinjectedByATransientChildContainer(new AnnotatedMethodInjection(Hurrah.class, false));
125 }
126
127 private void cachedComponentCanBeReinjectedByATransientChildContainer(AbstractInjectionFactory methodInjection) {
128 DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
129 parent.addComponent(INeedsShoe.class, NeedsShoe.class);
130 parent.addComponent(Shoe.class);
131 parent.addComponent("12");
132
133 INeedsShoe needsShoe = parent.getComponent(INeedsShoe.class);
134 assertNotNull(needsShoe.getBar());
135 assertTrue(needsShoe.getString() == null);
136
137 TransientPicoContainer tpc = new TransientPicoContainer(new Reinjection(methodInjection, parent), parent);
138 tpc.addComponent(INeedsShoe.class, NeedsShoe.class);
139
140 INeedsShoe needsShoe2 = tpc.getComponent(INeedsShoe.class);
141 assertSame(needsShoe, needsShoe2);
142 assertNotNull(needsShoe2.getBar());
143 assertNotNull(needsShoe2.getString());
144
145 INeedsShoe needsShoe3 = parent.getComponent(INeedsShoe.class);
146 assertSame(needsShoe, needsShoe3);
147 assertNotNull(needsShoe3.getBar());
148 assertNotNull(needsShoe3.getString());
149 }
150
151 @Test
152 public void confirmThatReinjectionCanLeverageParameterNamesForDisambiguation() {
153 MethodInjection methodInjection = new MethodInjection(DOIT_METHOD);
154 DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
155
156 // parameter name leverage can't work on interfaces if using bytecode retrieval technique
157
158 parent.addComponent(NeedsShoe.class);
159 parent.addComponent(Shoe.class);
160 parent.addComponent("a", "1333");
161 parent.addComponent("s", "12");
162 parent.addComponent("tjklhjkjhkjh", "44");
163
164 NeedsShoe needsShoe = parent.getComponent(NeedsShoe.class);
165 assertNotNull(needsShoe.bar);
166 assertTrue(needsShoe.string == null);
167
168 Reinjection reinjection = new Reinjection(methodInjection, parent);
169 TransientPicoContainer tpc = new TransientPicoContainer(reinjection, parent);
170 tpc.as(Characteristics.USE_NAMES).addComponent(NeedsShoe.class);
171
172 NeedsShoe needsShoe2 = tpc.getComponent(NeedsShoe.class);
173 assertSame(needsShoe, needsShoe2);
174 assertNotNull(needsShoe2.bar);
175 assertNotNull(needsShoe2.string);
176 assertEquals("12", needsShoe2.string);
177
178 }
179
180 @Test
181 public void confirmThatReinjectionCanLeverageParameterNamesForDisambiguationWithTypeConversion() throws NoSuchMethodException {
182 MethodInjection methodInjection = new MethodInjection(NeedsShoe.class.getMethod("doInt", int.class));
183 DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
184
185 // parameter name leverage can't work on interfaces if using bytecode retrieval technique
186
187 parent.addComponent(NeedsShoe.class);
188 parent.addComponent(Shoe.class);
189 parent.addComponent("a", "1333");
190 parent.addComponent("s", "12");
191 parent.addComponent("tjklhjkjhkjh", "44");
192
193 NeedsShoe needsShoe = parent.getComponent(NeedsShoe.class);
194 assertNotNull(needsShoe.bar);
195 assertTrue(needsShoe.string == null);
196
197 Reinjection reinjection = new Reinjection(methodInjection, parent);
198 TransientPicoContainer tpc = new TransientPicoContainer(reinjection, parent);
199 tpc.as(Characteristics.USE_NAMES).addComponent(NeedsShoe.class);
200
201 NeedsShoe needsShoe2 = tpc.getComponent(NeedsShoe.class);
202 assertSame(needsShoe, needsShoe2);
203 assertNotNull(needsShoe2.bar);
204 assertNotNull(needsShoe2.string);
205 assertEquals("i=12", needsShoe2.string);
206
207 }
208
209 @Test public void testCachedComponentCanBeReinjectedByATransientReflectionMethodReinjector() {
210 cachedComponentCanBeReinjectedByATransientReinjector(new MethodInjection(DOIT_METHOD));
211 }
212
213 @Test public void testCachedComponentCanBeReinjectedByATransientMethodNameReinjector() {
214 cachedComponentCanBeReinjectedByATransientReinjector(new MethodInjection("doIt"));
215 }
216
217 @Test public void testCachedComponentCanBeReinjectedByATransientAnnotatedMethodReinjector() {
218 cachedComponentCanBeReinjectedByATransientReinjector(new AnnotatedMethodInjection(Hurrah.class, false));
219 }
220
221 public static class ReturnParameterAction implements Action {
222 private final int parameter;
223
224 public ReturnParameterAction(int parameter) {
225 this.parameter = parameter;
226 }
227
228 public void describeTo(Description description) {
229 // describe it
230 }
231
232 public Object invoke(Invocation invocation) {
233 return invocation.getParameter(parameter);
234 }
235 }
236
237 private void cachedComponentCanBeReinjectedByATransientReinjector(AbstractInjectionFactory methodInjection) {
238 final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
239 parent.setName("parent");
240 parent.addComponent(INeedsShoe.class, NeedsShoe.class);
241 parent.addComponent(Shoe.class);
242 parent.addComponent("12");
243
244 final INeedsShoe foo = parent.getComponent(INeedsShoe.class);
245 assertNotNull(foo.getBar());
246 assertTrue(foo.getString() == null);
247
248 final ComponentMonitor cm = mockery.mock(ComponentMonitor.class);
249 Reinjector reinjector = new Reinjector(parent, cm);
250 mockery.checking(new Expectations() {{
251 atLeast(1).of(cm).newInjector(with(any(org.picocontainer.Injector.class)));
252 will(new ReturnParameterAction(0));
253 one(cm).invoking(with(any(PicoContainer.class)), with(any(ComponentAdapter.class)),
254 with(any(Method.class)), with(any(Object.class)), with(any(Object[].class)));
255 will(returnValue(ComponentMonitor.KEEP));
256 one(cm).invoked(with(any(PicoContainer.class)), with(any(ComponentAdapter.class)),
257 with(any(Method.class)), with(any(Object.class)), with(any(Long.class)), with(any(Object[].class)), with(any(Integer.class)));
258 }});
259
260 Object o = reinjector.reinject(NeedsShoe.class, methodInjection);
261 int result = (Integer) o;
262 assertEquals(6, result);
263
264 INeedsShoe foo3 = parent.getComponent(INeedsShoe.class);
265 assertSame(foo, foo3);
266 assertNotNull(foo3.getBar());
267 assertNotNull(foo3.getString());
268 assertEquals("12", foo3.getString());
269 }
270
271 @Test public void testOverloadedReinjectMethodsAreIdentical() {
272 final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
273 parent.addComponent(INeedsShoe.class, NeedsShoe.class);
274 parent.addComponent(Shoe.class);
275 parent.addComponent("12");
276
277 final ComponentMonitor cm = new NullComponentMonitor();
278 Reinjector reinjector = new Reinjector(parent, cm);
279
280 //int result = (Integer) reinjector.reinject(NeedsShoe.class, DOIT_METHOD);
281 assertEquals(6, (int) (Integer) reinjector.reinject(NeedsShoe.class, DOIT_METHOD));
282 assertEquals(6, (int) (Integer) reinjector.reinject(NeedsShoe.class, new MethodInjection(DOIT_METHOD)));
283
284 }
285
286 @Test public void testOverloadedReinjectMethodsAreIdentical2() {
287 final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
288 parent.addComponent(INeedsShoe.class, NeedsShoe.class);
289 parent.addComponent(Shoe.class);
290 parent.addComponent("12");
291
292 final ComponentMonitor cm = new NullComponentMonitor();
293 Reinjector reinjector = new Reinjector(parent, cm);
294
295 assertEquals(6, (int) (Integer) reinjector.reinject(NeedsShoe.class, NeedsShoe.M.doIt));
296 assertEquals(6, (int) (Integer) reinjector.reinject(NeedsShoe.class, new MethodInjection(DOIT_METHOD)));
297
298 }
299
300 @Test public void testReinjectorCanBeOverridenByComponentMonitor() {
301 final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
302 parent.addComponent(INeedsShoe.class, NeedsShoe.class);
303 parent.addComponent(Shoe.class);
304 parent.addComponent("12");
305
306 final ComponentMonitor cm = new NullComponentMonitor() {
307 public Object invoking(PicoContainer container, ComponentAdapter<?> componentAdapter, Member member, Object instance, Object[] args) {
308 return 4444;
309 }
310 };
311 Reinjector reinjector = new Reinjector(parent, cm);
312
313 assertEquals(4444, (int) (Integer) reinjector.reinject(NeedsShoe.class, DOIT_METHOD));
314
315 }
316
317 @Test public void testReinjectorCanBeHonoredByComponentMonitor() {
318 final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
319 parent.addComponent(INeedsShoe.class, NeedsShoe.class);
320 parent.addComponent(Shoe.class);
321 parent.addComponent("12");
322
323 final ComponentMonitor cm = new NullComponentMonitor() {
324 public Object invoking(PicoContainer container, ComponentAdapter<?> componentAdapter, Member member, Object instance, Object[] args) {
325 return ComponentMonitor.KEEP;
326 }
327 };
328 Reinjector reinjector = new Reinjector(parent, cm);
329
330 assertEquals(6, (int) (Integer) reinjector.reinject(NeedsShoe.class, DOIT_METHOD));
331
332 }
333
334 @Test public void testReinjectorCanBeNullifiedByComponentMonitor() {
335 final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
336 parent.addComponent(INeedsShoe.class, NeedsShoe.class);
337 parent.addComponent(Shoe.class);
338 parent.addComponent("12");
339
340 final ComponentMonitor cm = new NullComponentMonitor() {
341 public Object invoking(PicoContainer container, ComponentAdapter<?> componentAdapter, Member member, Object instance, Object[] args) {
342 return null;
343 }
344 };
345 Reinjector reinjector = new Reinjector(parent, cm);
346
347 Object retval = reinjector.reinject(NeedsShoe.class, DOIT_METHOD);
348 assertTrue(retval == null);
349
350 }
351
352 protected ComponentFactory createComponentFactory() {
353 return new Reinjection(new MethodInjection(DOIT_METHOD), new EmptyPicoContainer());
354 }
355
356 @Test
357 public void testRegisterComponent() throws PicoCompositionException {
358 try {
359 super.testRegisterComponent();
360 fail();
361 } catch (PicoCompositionException e) {
362 assertTrue(e.getMessage().contains("] not on impl "));
363 }
364 }
365
366 @Test
367 public void testUnregisterComponent() throws PicoCompositionException {
368 try {
369 super.testUnregisterComponent();
370 fail();
371 } catch (PicoCompositionException e) {
372 assertTrue(e.getMessage().contains("] not on impl "));
373 }
374 }
375
376 @Test
377 public void testEquals() throws PicoCompositionException {
378 try {
379 super.testEquals();
380 fail();
381 } catch (PicoCompositionException e) {
382 assertTrue(e.getMessage().contains("] not on impl "));
383 }
384 }
385 }