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 Object getBar() {
074 return bar;
075 }
076
077 public Object getString() {
078 return string;
079 }
080 }
081
082 public static class Shoe {
083 }
084
085 private static Method DOIT_METHOD;
086
087 static {
088 try {
089 DOIT_METHOD = NeedsShoe.class.getMethod("doIt", String.class);
090 } catch (NoSuchMethodException e) {
091 e.printStackTrace();
092 }
093 }
094
095 @Test public void testCachedComponentCanBeReflectionMethodReinjectedByATransientChildContainer() {
096 cachedComponentCanBeReinjectedByATransientChildContainer(new MethodInjection(DOIT_METHOD));
097 }
098
099 @Test public void testCachedComponentCanBeMethodNameReinjectedByATransientChildContainer() {
100 cachedComponentCanBeReinjectedByATransientChildContainer(new MethodInjection("doIt"));
101 }
102
103 @Test public void testCachedComponentCanBeAnnotatedMethodReinjectedByATransientChildContainer() {
104 cachedComponentCanBeReinjectedByATransientChildContainer(new AnnotatedMethodInjection(Hurrah.class, false));
105 }
106
107 private void cachedComponentCanBeReinjectedByATransientChildContainer(AbstractInjectionFactory methodInjection) {
108 DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
109 parent.addComponent(INeedsShoe.class, NeedsShoe.class);
110 parent.addComponent(Shoe.class);
111 parent.addComponent("12");
112
113 INeedsShoe needsShoe = parent.getComponent(INeedsShoe.class);
114 assertNotNull(needsShoe.getBar());
115 assertTrue(needsShoe.getString() == null);
116
117 TransientPicoContainer tpc = new TransientPicoContainer(new Reinjection(methodInjection, parent), parent);
118 tpc.addComponent(INeedsShoe.class, NeedsShoe.class);
119
120 INeedsShoe needsShoe2 = tpc.getComponent(INeedsShoe.class);
121 assertSame(needsShoe, needsShoe2);
122 assertNotNull(needsShoe2.getBar());
123 assertNotNull(needsShoe2.getString());
124
125 INeedsShoe needsShoe3 = parent.getComponent(INeedsShoe.class);
126 assertSame(needsShoe, needsShoe3);
127 assertNotNull(needsShoe3.getBar());
128 assertNotNull(needsShoe3.getString());
129 }
130
131 @Test
132 public void confirmThatReinjectionCanLeverageParameterNamesForDisambiguation() {
133 MethodInjection methodInjection = new MethodInjection(DOIT_METHOD);
134 DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
135
136 // parameter name leverage can't work on interfaces if using bytecode retrieval technique
137
138 parent.addComponent(NeedsShoe.class);
139 parent.addComponent(Shoe.class);
140 parent.addComponent("a", "1333");
141 parent.addComponent("s", "12");
142 parent.addComponent("tjklhjkjhkjh", "44");
143
144 NeedsShoe needsShoe = parent.getComponent(NeedsShoe.class);
145 assertNotNull(needsShoe.bar);
146 assertTrue(needsShoe.string == null);
147
148 Reinjection reinjection = new Reinjection(methodInjection, parent);
149 TransientPicoContainer tpc = new TransientPicoContainer(reinjection, parent);
150 tpc.as(Characteristics.USE_NAMES).addComponent(NeedsShoe.class);
151
152 NeedsShoe needsShoe2 = tpc.getComponent(NeedsShoe.class);
153 assertSame(needsShoe, needsShoe2);
154 assertNotNull(needsShoe2.bar);
155 assertNotNull(needsShoe2.string);
156 assertEquals("12", needsShoe2.string);
157
158 }
159
160 @Test public void testCachedComponentCanBeReinjectedByATransientReflectionMethodReinjector() {
161 cachedComponentCanBeReinjectedByATransientReinjector(new MethodInjection(DOIT_METHOD));
162 }
163
164 @Test public void testCachedComponentCanBeReinjectedByATransientMethodNameReinjector() {
165 cachedComponentCanBeReinjectedByATransientReinjector(new MethodInjection("doIt"));
166 }
167
168 @Test public void testCachedComponentCanBeReinjectedByATransientAnnotatedMethodReinjector() {
169 cachedComponentCanBeReinjectedByATransientReinjector(new AnnotatedMethodInjection(Hurrah.class, false));
170 }
171
172 public static class ReturnParameterAction implements Action {
173 private final int parameter;
174
175 public ReturnParameterAction(int parameter) {
176 this.parameter = parameter;
177 }
178
179 public void describeTo(Description description) {
180 // describe it
181 }
182
183 public Object invoke(Invocation invocation) {
184 return invocation.getParameter(parameter);
185 }
186 }
187
188 private void cachedComponentCanBeReinjectedByATransientReinjector(AbstractInjectionFactory methodInjection) {
189 final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
190 parent.setName("parent");
191 parent.addComponent(INeedsShoe.class, NeedsShoe.class);
192 parent.addComponent(Shoe.class);
193 parent.addComponent("12");
194
195 final INeedsShoe foo = parent.getComponent(INeedsShoe.class);
196 assertNotNull(foo.getBar());
197 assertTrue(foo.getString() == null);
198
199 final ComponentMonitor cm = mockery.mock(ComponentMonitor.class);
200 Reinjector reinjector = new Reinjector(parent, cm);
201 mockery.checking(new Expectations() {{
202 atLeast(1).of(cm).newInjector(with(any(org.picocontainer.Injector.class)));
203 will(new ReturnParameterAction(0));
204 one(cm).invoking(with(any(PicoContainer.class)), with(any(ComponentAdapter.class)),
205 with(any(Method.class)), with(any(Object.class)), with(any(Object[].class)));
206 will(returnValue(ComponentMonitor.KEEP));
207 one(cm).invoked(with(any(PicoContainer.class)), with(any(ComponentAdapter.class)),
208 with(any(Method.class)), with(any(Object.class)), with(any(Long.class)), with(any(Object[].class)), with(any(Integer.class)));
209 }});
210
211 Object o = reinjector.reinject(NeedsShoe.class, methodInjection);
212 int result = (Integer) o;
213 assertEquals(6, result);
214
215 INeedsShoe foo3 = parent.getComponent(INeedsShoe.class);
216 assertSame(foo, foo3);
217 assertNotNull(foo3.getBar());
218 assertNotNull(foo3.getString());
219 assertEquals("12", foo3.getString());
220 }
221
222 @Test public void testOverloadedReinjectMethodsAreIdentical() {
223 final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
224 parent.addComponent(INeedsShoe.class, NeedsShoe.class);
225 parent.addComponent(Shoe.class);
226 parent.addComponent("12");
227
228 final ComponentMonitor cm = new NullComponentMonitor();
229 Reinjector reinjector = new Reinjector(parent, cm);
230
231 int result = (Integer) reinjector.reinject(NeedsShoe.class, DOIT_METHOD);
232 assertEquals(6, (int) (Integer) reinjector.reinject(NeedsShoe.class, DOIT_METHOD));
233 assertEquals(6, (int) (Integer) reinjector.reinject(NeedsShoe.class, new MethodInjection(DOIT_METHOD)));
234
235 }
236
237 @Test public void testReinjectorCanBeOverridenByComponentMonitor() {
238 final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
239 parent.addComponent(INeedsShoe.class, NeedsShoe.class);
240 parent.addComponent(Shoe.class);
241 parent.addComponent("12");
242
243 final ComponentMonitor cm = new NullComponentMonitor() {
244 public Object invoking(PicoContainer container, ComponentAdapter<?> componentAdapter, Member member, Object instance, Object[] args) {
245 return 4444;
246 }
247 };
248 Reinjector reinjector = new Reinjector(parent, cm);
249
250 int result = (Integer) reinjector.reinject(NeedsShoe.class, DOIT_METHOD);
251 assertEquals(4444, (int) (Integer) reinjector.reinject(NeedsShoe.class, DOIT_METHOD));
252
253 }
254
255 @Test public void testReinjectorCanBeHonoredByComponentMonitor() {
256 final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
257 parent.addComponent(INeedsShoe.class, NeedsShoe.class);
258 parent.addComponent(Shoe.class);
259 parent.addComponent("12");
260
261 final ComponentMonitor cm = new NullComponentMonitor() {
262 public Object invoking(PicoContainer container, ComponentAdapter<?> componentAdapter, Member member, Object instance, Object[] args) {
263 return ComponentMonitor.KEEP;
264 }
265 };
266 Reinjector reinjector = new Reinjector(parent, cm);
267
268 int result = (Integer) reinjector.reinject(NeedsShoe.class, DOIT_METHOD);
269 assertEquals(6, (int) (Integer) reinjector.reinject(NeedsShoe.class, DOIT_METHOD));
270
271 }
272
273 @Test public void testReinjectorCanBeNullifiedByComponentMonitor() {
274 final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
275 parent.addComponent(INeedsShoe.class, NeedsShoe.class);
276 parent.addComponent(Shoe.class);
277 parent.addComponent("12");
278
279 final ComponentMonitor cm = new NullComponentMonitor() {
280 public Object invoking(PicoContainer container, ComponentAdapter<?> componentAdapter, Member member, Object instance, Object[] args) {
281 return null;
282 }
283 };
284 Reinjector reinjector = new Reinjector(parent, cm);
285
286 Object retval = reinjector.reinject(NeedsShoe.class, DOIT_METHOD);
287 assertTrue(retval == null);
288
289 }
290
291 protected ComponentFactory createComponentFactory() {
292 return new Reinjection(new MethodInjection(DOIT_METHOD), new EmptyPicoContainer());
293 }
294
295 @Test
296 public void testRegisterComponent() throws PicoCompositionException {
297 try {
298 super.testRegisterComponent();
299 fail();
300 } catch (PicoCompositionException e) {
301 assertTrue(e.getMessage().contains("] not on impl "));
302 }
303 }
304
305 @Test
306 public void testUnregisterComponent() throws PicoCompositionException {
307 try {
308 super.testUnregisterComponent();
309 fail();
310 } catch (PicoCompositionException e) {
311 assertTrue(e.getMessage().contains("] not on impl "));
312 }
313 }
314
315 @Test
316 public void testEquals() throws PicoCompositionException {
317 try {
318 super.testEquals();
319 fail();
320 } catch (PicoCompositionException e) {
321 assertTrue(e.getMessage().contains("] not on impl "));
322 }
323 }
324 }