    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    package org.picocontainer.injectors;
    010
    011    import org.junit.Test;
    012    import static org.junit.Assert.assertNotNull;
    013    import static org.junit.Assert.assertEquals;
    014    import static org.junit.Assert.assertTrue;
    015    import static org.junit.Assert.fail;
    016    import org.picocontainer.DefaultPicoContainer;
    017    import org.picocontainer.PicoCompositionException;
    018    import org.picocontainer.annotations.Nullable;
    019
    020    public class ProviderTestCase {
    021
    022        @Test
    023        public void provideMethodCanParticipateInInjection() {
    024            DefaultPicoContainer dpc = new DefaultPicoContainer();
    025            dpc.addAdapter(new Chocolatier(true));
    026            dpc.addComponent(NeedsChocolate.class);
    027            dpc.addComponent(CocaoBeans.class);
    028            dpc.addComponent(String.class, "Cadbury's"); // the only string in the set of components
    029            NeedsChocolate needsChocolate = dpc.getComponent(NeedsChocolate.class);
    030            assertNotNull(needsChocolate);
    031            assertNotNull(needsChocolate.choc);
    032            assertEquals(true, needsChocolate.choc.milky);
    033            assertNotNull(needsChocolate.choc.cocaoBeans);
    034            assertEquals("Cadbury's", needsChocolate.choc.name);
    035        }
    036
    037        @Test
    038        public void provideMethodCanDisambiguateUsingParameterNames() {
    039            DefaultPicoContainer dpc = new DefaultPicoContainer();
    040            dpc.addAdapter(new Chocolatier(true));
    041            dpc.addComponent(NeedsChocolate.class);
    042            dpc.addComponent(CocaoBeans.class);
    043            dpc.addComponent("color", "Red"); // not used by virtue of key
    044            dpc.addComponent("name", "Cadbury's");
    045            dpc.addComponent("band", "Abba"); // not used by virtue of key
    046            NeedsChocolate needsChocolate = dpc.getComponent(NeedsChocolate.class);
    047            assertNotNull(needsChocolate);
    048            assertNotNull(needsChocolate.choc);
    049            assertEquals(true, needsChocolate.choc.milky);
    050            assertNotNull(needsChocolate.choc.cocaoBeans);
    051            assertEquals("Cadbury's", needsChocolate.choc.name);
    052        }
    053
    054        @Test
    055        public void providerBarfsIfProvideMethodsParamsCanNotBeSatisfied() {
    056            DefaultPicoContainer dpc = new DefaultPicoContainer();
    057            dpc.addAdapter(new Chocolatier(true));
    058            dpc.addComponent(NeedsChocolate.class);
    059            try {
    060                dpc.getComponent(NeedsChocolate.class);
    061            } catch (PicoCompositionException e) {
    062                assertTrue(e.getMessage().contains("Parameter 0 "));
    063                assertTrue(e.getMessage().contains("cannot be null"));
    064            }
    065        }
    066
    067        @Test
    068        public void providerDoesNotBarfIfProvideMethodsParamsCanNotBeSatisfiedButNullbleAnnotationUsed() {
    069            DefaultPicoContainer dpc = new DefaultPicoContainer();
    070            dpc.addAdapter(new NullableChocolatier());
    071            dpc.addComponent(NeedsChocolate.class);
    072            NeedsChocolate nc = dpc.getComponent(NeedsChocolate.class);
    073            assertNotNull(nc);
    074            assertNotNull(nc.choc);
    075            assertTrue(nc.choc.cocaoBeans == null);
    076        }
    077
    078        public static class CocaoBeans {
    079        }
    080
    081        public static class Chocolate {
    082            private boolean milky;
    083            private final CocaoBeans cocaoBeans;
    084            private final String name;
    085
    086            public Chocolate(String name) {
    087                this(true, new CocaoBeans(), name);
    088            }
    089
    090            public Chocolate(boolean milky, CocaoBeans cocaoBeans, String name) {
    091                this.milky = milky;
    092                this.cocaoBeans = cocaoBeans;
    093                this.name = name;
    094            }
    095        }
    096
    097        public static class Chocolatier extends ProviderAdapter {
    098            private final boolean milky;
    099            public Chocolatier(boolean milky) {
    100                this.milky = milky;
    101            }
    102            public Chocolate provide(CocaoBeans cocaoBeans, String name) {
    103                return new Chocolate(milky, cocaoBeans, name);
    104            }
    105            @Override
    106            protected boolean useNames() {
    107                return true;
    108            }
    109        }
    110
    111        public static class NullableChocolatier extends Chocolatier {
    112            public NullableChocolatier() {
    113                super(true);
    114            }
    115
    116            public Chocolate provide(@Nullable CocaoBeans cocaoBeans, @Nullable String name) {
    117                return super.provide(cocaoBeans, name);
    118            }
    119        }
    120
    121        public static class NeedsChocolate {
    122            private Chocolate choc;
    123            public NeedsChocolate(Chocolate choc) {
    124                this.choc = choc;
    125            }
    126        }
    127
    128        @Test
    129        public void providerBarfsIfNoProvideMethod() {
    130            DefaultPicoContainer dpc = new DefaultPicoContainer();
    131            try {
    132                dpc.addAdapter(new ProviderWithoutProvideMethod());
    133                fail("should have barfed");
    134            } catch (PicoCompositionException e) {
    135                assertEquals("There must be a method named 'provide' in the AbstractProvider implementation", e.getMessage());
    136            }
    137        }
    138
    139        @Test
    140        public void providerBarfsIfBadProvideMethod() {
    141            DefaultPicoContainer dpc = new DefaultPicoContainer();
    142            try {
    143                dpc.addAdapter(new ProviderWithBadProvideMethod());
    144                fail("should have barfed");
    145            } catch (PicoCompositionException e) {
    146                assertEquals("There must be a non void returning method named 'provide' in the AbstractProvider implementation", e.getMessage());
    147            }
    148        }
    149
    150        @Test
    151        public void providerBarfsIfTooManyProvideMethod() {
    152            DefaultPicoContainer dpc = new DefaultPicoContainer();
    153            try {
    154                dpc.addAdapter(new ProviderWithTooManyProvideMethods());
    155                fail("should have barfed");
    156            } catch (PicoCompositionException e) {
    157                assertEquals("There must be only one method named 'provide' in the AbstractProvider implementation", e.getMessage());
    158            }
    159        }
    160
    161        public static class ProviderWithoutProvideMethod extends ProviderAdapter {
    162        }
    163        public static class ProviderWithBadProvideMethod extends ProviderAdapter {
    164            public void provide() {
    165
    166            }
    167        }
    168        public static class ProviderWithTooManyProvideMethods extends ProviderAdapter {
    169            public String provide(String str) {
    170                return null;
    171            }
    172            public Integer provide() {
    173                return null;
    174            }
    175        }
    176
    177        @Test
    178        public void provideMethodCanParticipateInInjectionWhenNotExtendingProviderAdapter() {
    179            DefaultPicoContainer dpc = new DefaultPicoContainer();
    180            dpc.addAdapter(new ProviderAdapter(new Chocolatier2(true)));
    181            dpc.addComponent(NeedsChocolate.class);
    182            dpc.addComponent(CocaoBeans.class);
    183            dpc.addComponent(String.class, "Cadbury's"); // the only string in the set of components
    184            NeedsChocolate needsChocolate = dpc.getComponent(NeedsChocolate.class);
    185            assertNotNull(needsChocolate);
    186            assertNotNull(needsChocolate.choc);
    187            assertEquals(true, needsChocolate.choc.milky);
    188            assertNotNull(needsChocolate.choc.cocaoBeans);
    189            assertEquals("Cadbury's", needsChocolate.choc.name);
    190        }
    191
    192        public static class Chocolatier2 implements Provider {
    193            private final boolean milky;
    194            public Chocolatier2(boolean milky) {
    195                this.milky = milky;
    196            }
    197            public Chocolate provide(CocaoBeans cocaoBeans, String name) {
    198                return new Chocolate(milky, cocaoBeans, name);
    199            }
    200        }
    201
    202        @Test
    203        public void providedTypeCanBeDyanamicallyDeterminedFromInstanceRatherThanType() {
    204            DefaultPicoContainer dpc = new DefaultPicoContainer();
    205
    206            // a simlation of what a web framework would essentially do in a thread-local way
    207            dpc.addComponent(new StubHttpRequest("chocolate", "Lindt"));
    208
    209            // this is the style being recomended for automatic request-params -> beans
    210            dpc.addAdapter(new ExampleRequestReader(Chocolate.class, "chocolate"));
    211
    212            dpc.addComponent(NeedsChocolate.class);
    213            NeedsChocolate needsChocolate = dpc.getComponent(NeedsChocolate.class);
    214            assertNotNull(needsChocolate);
    215            assertNotNull(needsChocolate.choc);
    216            assertEquals(true, needsChocolate.choc.milky);
    217            assertNotNull(needsChocolate.choc.cocaoBeans);
    218            assertEquals("Lindt", needsChocolate.choc.name);
    219        }
    220
    221
    222        public static class StubHttpRequest {
    223            private final String key;
    224            private final String value;
    225
    226            public StubHttpRequest(String key, String value) {
    227                this.key = key;
    228                this.value = value;
    229            }
    230
    231            public String getParameter(String name) {
    232                return name.equals(key) ? value : null;
    233            }
    234        }
    235
    236        public static class ExampleRequestReader extends ProviderAdapter {
    237            private final Class clazz;
    238            private final String paramName;
    239
    240            public ExampleRequestReader(Class clazz, String paramName) {
    241                this.clazz = clazz;
    242                this.paramName = paramName;
    243            }
    244
    245            public Class getComponentImplementation() {
    246                return clazz;
    247            }
    248
    249            public Object provide(StubHttpRequest req) {
    250                try {
    251                    return clazz.getConstructors()[0].newInstance(req.getParameter(paramName));
    252                } catch (Exception e) {
    253                    throw new RuntimeException(e);
    254                }
    255            }
    256        }
    257
    258
    259    }