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.getConstructor(String.class).newInstance(req.getParameter(paramName));
252                } catch (Exception e) {
253                    throw new RuntimeException(e);  
254                }
255            }
256        }
257    
258    
259    }