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.ComponentFactory;
019    import org.picocontainer.DefaultPicoContainer;
020    import org.picocontainer.ComponentMonitor;
021    import org.picocontainer.ComponentAdapter;
022    import org.picocontainer.PicoContainer;
023    import org.picocontainer.PicoCompositionException;
024    import org.picocontainer.Characteristics;
025    import org.picocontainer.monitors.NullComponentMonitor;
026    import org.picocontainer.behaviors.Caching;
027    import org.picocontainer.containers.EmptyPicoContainer;
028    import org.picocontainer.containers.TransientPicoContainer;
029    import org.picocontainer.tck.AbstractComponentFactoryTest;
030    import static org.picocontainer.tck.MockFactory.mockeryWithCountingNamingScheme;
031    import org.jmock.integration.junit4.JMock;
032    import org.jmock.Mockery;
033    import org.jmock.Expectations;
034    import org.jmock.api.Action;
035    import org.jmock.api.Invocation;
036    import org.hamcrest.Description;
037    
038    import java.lang.reflect.Method;
039    import java.lang.annotation.Retention;
040    import java.lang.annotation.RetentionPolicy;
041    import java.lang.annotation.Target;
042    import java.lang.annotation.ElementType;
043    
044    @RunWith(JMock.class)
045    public class ReinjectionTestCase extends AbstractComponentFactoryTest {
046    
047        private Mockery mockery = mockeryWithCountingNamingScheme();
048    
049        @Retention(RetentionPolicy.RUNTIME)
050        @Target(value={ElementType.METHOD, ElementType.FIELD})
051        public @interface Hurrah {
052        }
053    
054        public static class NeedsShoe {
055            private Shoe bar;
056            private String string;
057    
058            public NeedsShoe(Shoe bar) {
059                this.bar = bar;
060            }
061    
062            @Hurrah
063            public int doIt(String s) {
064                this.string = s;
065                return Integer.parseInt(s) / 2;
066            }
067        }
068    
069        public static class Shoe {
070        }
071    
072        private static Method DOIT_METHOD = NeedsShoe.class.getMethods()[0];
073    
074        @Test public void testCachedComponentCanBeReflectionMethodReinjectedByATransientChildContainer() {
075            cachedComponentCanBeReinjectedByATransientChildContainer(new MethodInjection(DOIT_METHOD));
076        }
077    
078        @Test public void testCachedComponentCanBeMethodNameReinjectedByATransientChildContainer() {
079            cachedComponentCanBeReinjectedByATransientChildContainer(new MethodInjection("doIt"));
080        }
081        
082        @Test public void testCachedComponentCanBeAnnotatedMethodReinjectedByATransientChildContainer() {
083            cachedComponentCanBeReinjectedByATransientChildContainer(new AnnotatedMethodInjection(Hurrah.class, false));
084        }
085    
086        private void cachedComponentCanBeReinjectedByATransientChildContainer(AbstractInjectionFactory methodInjection) {
087            DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
088            parent.addComponent(NeedsShoe.class);
089            parent.addComponent(Shoe.class);
090            parent.addComponent("12");
091    
092            NeedsShoe needsShoe = parent.getComponent(NeedsShoe.class);
093            assertNotNull(needsShoe.bar);
094            assertTrue(needsShoe.string == null);
095    
096            TransientPicoContainer tpc = new TransientPicoContainer(new Reinjection(methodInjection, parent), parent);
097            tpc.addComponent(NeedsShoe.class);
098    
099            NeedsShoe needsShoe2 = tpc.getComponent(NeedsShoe.class);
100            assertSame(needsShoe, needsShoe2);
101            assertNotNull(needsShoe2.bar);
102            assertNotNull(needsShoe2.string);
103    
104            NeedsShoe needsShoe3 = parent.getComponent(NeedsShoe.class);
105            assertSame(needsShoe, needsShoe3);
106            assertNotNull(needsShoe3.bar);
107            assertNotNull(needsShoe3.string);
108        }
109    
110        @Test
111        public void confirmThatReinjectionCanLeverageParameterNamesForDisambiguation() {
112            MethodInjection methodInjection = new MethodInjection(DOIT_METHOD);
113            DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
114            parent.addComponent(NeedsShoe.class);
115            parent.addComponent(Shoe.class);
116            parent.addComponent("a", "1333");
117            parent.addComponent("s", "12");
118            parent.addComponent("tjklhjkjhkjh", "44");
119    
120            NeedsShoe needsShoe = parent.getComponent(NeedsShoe.class);
121            assertNotNull(needsShoe.bar);
122            assertTrue(needsShoe.string == null);
123    
124            Reinjection reinjection = new Reinjection(methodInjection, parent);
125            TransientPicoContainer tpc = new TransientPicoContainer(reinjection, parent);
126            tpc.as(Characteristics.USE_NAMES).addComponent(NeedsShoe.class);
127    
128            NeedsShoe needsShoe2 = tpc.getComponent(NeedsShoe.class);
129            assertSame(needsShoe, needsShoe2);
130            assertNotNull(needsShoe2.bar);
131            assertNotNull(needsShoe2.string);
132            assertEquals("12", needsShoe2.string);
133    
134        }
135    
136        @Test public void testCachedComponentCanBeReinjectedByATransientReflectionMethodReinjector() {
137            cachedComponentCanBeReinjectedByATransientReinjector(new MethodInjection(DOIT_METHOD));
138        }
139        @Test public void testCachedComponentCanBeReinjectedByATransientMethodNameReinjector() {
140            cachedComponentCanBeReinjectedByATransientReinjector(new MethodInjection("doIt"));
141        }
142        @Test public void testCachedComponentCanBeReinjectedByATransientAnnotatedMethodReinjector() {
143            cachedComponentCanBeReinjectedByATransientReinjector(new AnnotatedMethodInjection(Hurrah.class, false));
144        }
145    
146        public static class ReturnParameterAction implements Action {
147            private final int parameter;
148    
149            public ReturnParameterAction(int parameter) {
150                this.parameter = parameter;
151            }
152    
153            public void describeTo(Description description) {
154                // describe it
155            }
156    
157            public Object invoke(Invocation invocation) {
158                return invocation.getParameter(parameter);
159            }
160        }
161    
162        private void cachedComponentCanBeReinjectedByATransientReinjector(AbstractInjectionFactory methodInjection) {
163            final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
164            parent.setName("parent");
165            parent.addComponent(NeedsShoe.class);
166            parent.addComponent(Shoe.class);
167            parent.addComponent("12");
168    
169            final NeedsShoe foo = parent.getComponent(NeedsShoe.class);
170            assertNotNull(foo.bar);
171            assertTrue(foo.string == null);
172    
173            final ComponentMonitor cm = mockery.mock(ComponentMonitor.class);
174            Reinjector reinjector = new Reinjector(parent, cm);
175            mockery.checking(new Expectations() {{
176                atLeast(1).of(cm).newInjector(with(any(org.picocontainer.Injector.class)));
177                will(new ReturnParameterAction(0));
178                one(cm).invoking(with(any(PicoContainer.class)), with(any(ComponentAdapter.class)),
179                        with(any(Method.class)), with(any(Object.class)));
180            }});
181    
182            Object o = reinjector.reinject(NeedsShoe.class, methodInjection);
183            int result = (Integer) o;
184            assertEquals(6, result);
185    
186            NeedsShoe foo3 = parent.getComponent(NeedsShoe.class);
187            assertSame(foo, foo3);
188            assertNotNull(foo3.bar);
189            assertNotNull(foo3.string);
190            assertEquals("12", foo3.string);
191        }
192    
193        @Test public void testOverloadedReinjectMethodsAreIdentical() {
194            final DefaultPicoContainer parent = new DefaultPicoContainer(new Caching().wrap(new ConstructorInjection()));
195            parent.addComponent(NeedsShoe.class);
196            parent.addComponent(Shoe.class);
197            parent.addComponent("12");
198    
199            final ComponentMonitor cm = new NullComponentMonitor();
200            Reinjector reinjector = new Reinjector(parent, cm);
201    
202            int result = (Integer) reinjector.reinject(NeedsShoe.class, DOIT_METHOD);
203            assertEquals(6, (int) (Integer) reinjector.reinject(NeedsShoe.class, DOIT_METHOD));
204            assertEquals(6, (int) (Integer) reinjector.reinject(NeedsShoe.class, new MethodInjection(DOIT_METHOD)));
205    
206        }
207    
208        protected ComponentFactory createComponentFactory() {
209            return new Reinjection(new MethodInjection(DOIT_METHOD), new EmptyPicoContainer());
210        }
211    
212        @Test
213        public void testRegisterComponent() throws PicoCompositionException {
214            try {
215                super.testRegisterComponent();
216                fail();
217            } catch (PicoCompositionException e) {
218                assertTrue(e.getMessage().contains("] not on impl "));
219            }
220        }
221    
222        @Test
223        public void testUnregisterComponent() throws PicoCompositionException {
224            try {
225                super.testUnregisterComponent();
226                fail();
227            } catch (PicoCompositionException e) {
228                assertTrue(e.getMessage().contains("] not on impl "));
229            }
230        }
231    
232        @Test
233        public void testEquals() throws PicoCompositionException {
234            try {
235                super.testEquals();
236                fail();
237            } catch (PicoCompositionException e) {
238                assertTrue(e.getMessage().contains("] not on impl "));
239            }
240        }
241    }