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    }