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    }