    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    }