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    
017    import org.picocontainer.*;
018    import org.picocontainer.annotations.Nullable;
019    import org.picocontainer.behaviors.ThreadCaching;
020    import org.picocontainer.lifecycle.ReflectionLifecycleStrategy;
021    import org.picocontainer.monitors.LifecycleComponentMonitor;
022    
023    public class ProviderTestCase {
024        
025        @Test
026        public void provideMethodCanParticipateInInjection() {
027            DefaultPicoContainer dpc = new DefaultPicoContainer();
028            dpc.addAdapter(new Chocolatier(true));
029            dpc.addComponent(NeedsChocolate.class);
030            dpc.addComponent(CocaoBeans.class);
031            dpc.addComponent(String.class, "Cadbury's"); // the only string in the set of components
032            NeedsChocolate needsChocolate = dpc.getComponent(NeedsChocolate.class);
033            assertNotNull(needsChocolate);
034            assertNotNull(needsChocolate.choc);
035            assertEquals(true, needsChocolate.choc.milky);
036            assertNotNull(needsChocolate.choc.cocaoBeans);
037            assertEquals("Cadbury's", needsChocolate.choc.name);
038        }
039    
040        @Test
041        public void provideMethodCanDisambiguateUsingParameterNames() {
042            DefaultPicoContainer dpc = new DefaultPicoContainer();
043            dpc.addAdapter(new Chocolatier(true));
044            dpc.addComponent(NeedsChocolate.class);
045            dpc.addComponent(CocaoBeans.class);
046            dpc.addComponent("color", "Red"); // not used by virtue of key
047            dpc.addComponent("name", "Cadbury's");
048            dpc.addComponent("band", "Abba"); // not used by virtue of key
049            NeedsChocolate needsChocolate = dpc.getComponent(NeedsChocolate.class);
050            assertNotNull(needsChocolate);
051            assertNotNull(needsChocolate.choc);
052            assertEquals(true, needsChocolate.choc.milky);
053            assertNotNull(needsChocolate.choc.cocaoBeans);
054            assertEquals("Cadbury's", needsChocolate.choc.name);
055        }
056    
057        @Test
058        public void providerBarfsIfProvideMethodsParamsCanNotBeSatisfied() {
059            DefaultPicoContainer dpc = new DefaultPicoContainer();
060            dpc.addAdapter(new Chocolatier(true));
061            dpc.addComponent(NeedsChocolate.class);
062            try {
063                dpc.getComponent(NeedsChocolate.class);
064            } catch (PicoCompositionException e) {
065                assertTrue(e.getMessage().contains("Parameter 0 "));
066                assertTrue(e.getMessage().contains("cannot be null"));
067            }
068        }
069    
070        @Test
071        public void providerDoesNotBarfIfProvideMethodsParamsCanNotBeSatisfiedButNullbleAnnotationUsed() {
072            DefaultPicoContainer dpc = new DefaultPicoContainer();
073            dpc.addAdapter(new NullableChocolatier());
074            dpc.addComponent(NeedsChocolate.class);
075            NeedsChocolate nc = dpc.getComponent(NeedsChocolate.class);
076            assertNotNull(nc);
077            assertNotNull(nc.choc);
078            assertTrue(nc.choc.cocaoBeans == null);
079        }
080    
081        public static class CocaoBeans {
082        }
083    
084        public static class Chocolate {
085            private boolean milky;
086            private final CocaoBeans cocaoBeans;
087            private final String name;
088    
089            public Chocolate(String name) {
090                this(true, new CocaoBeans(), name);
091            }
092    
093            public Chocolate(boolean milky, CocaoBeans cocaoBeans, String name) {
094                this.milky = milky;
095                this.cocaoBeans = cocaoBeans;
096                this.name = name;
097            }
098        }
099    
100        public static class Chocolatier extends ProviderAdapter {
101            private final boolean milky;
102            public Chocolatier(boolean milky) {
103                this.milky = milky;
104            }
105            public Chocolate provide(CocaoBeans cocaoBeans, String name) {
106                return new Chocolate(milky, cocaoBeans, name);
107            }
108            @Override
109            protected boolean useNames() {
110                return true;
111            }
112        }
113    
114        public static class NullableChocolatier extends Chocolatier {
115            public NullableChocolatier() {
116                super(true);
117            }
118    
119            public Chocolate provide(@Nullable CocaoBeans cocaoBeans, @Nullable String name) {
120                return super.provide(cocaoBeans, name);
121            }
122        }
123    
124        public static class NeedsChocolate {
125            private Chocolate choc;
126            public NeedsChocolate(Chocolate choc) {
127                this.choc = choc;
128            }
129        }
130    
131        @Test
132        public void providerBarfsIfNoProvideMethod() {
133            DefaultPicoContainer dpc = new DefaultPicoContainer();
134            try {
135                dpc.addAdapter(new ProviderWithoutProvideMethod());
136                fail("should have barfed");
137            } catch (PicoCompositionException e) {
138                assertEquals("There must be a method named 'provide' in the AbstractProvider implementation", e.getMessage());
139            }
140        }
141    
142        @Test
143        public void providerBarfsIfBadProvideMethod() {
144            DefaultPicoContainer dpc = new DefaultPicoContainer();
145            try {
146                dpc.addAdapter(new ProviderWithBadProvideMethod());
147                fail("should have barfed");
148            } catch (PicoCompositionException e) {
149                assertEquals("There must be a non void returning method named 'provide' in the AbstractProvider implementation", e.getMessage());
150            }
151        }
152    
153        @Test
154        public void providerBarfsIfTooManyProvideMethod() {
155            DefaultPicoContainer dpc = new DefaultPicoContainer();
156            try {
157                dpc.addAdapter(new ProviderWithTooManyProvideMethods());
158                fail("should have barfed");
159            } catch (PicoCompositionException e) {
160                assertEquals("There must be only one method named 'provide' in the AbstractProvider implementation", e.getMessage());
161            }
162        }
163    
164        public static class ProviderWithoutProvideMethod extends ProviderAdapter {
165        }
166        public static class ProviderWithBadProvideMethod extends ProviderAdapter {
167            public void provide() {
168    
169            }
170        }
171        public static class ProviderWithTooManyProvideMethods extends ProviderAdapter {
172            public String provide(String str) {
173                return null;
174            }
175            public Integer provide() {
176                return null;
177            }
178        }
179    
180        @Test
181        public void provideMethodCanParticipateInInjectionWhenNotExtendingProviderAdapter() {
182            DefaultPicoContainer dpc = new DefaultPicoContainer();
183            dpc.addAdapter(new ProviderAdapter(new Chocolatier2(true)));
184            dpc.addComponent(NeedsChocolate.class);
185            dpc.addComponent(CocaoBeans.class);
186            dpc.addComponent(String.class, "Cadbury's"); // the only string in the set of components
187            NeedsChocolate needsChocolate = dpc.getComponent(NeedsChocolate.class);
188            assertNotNull(needsChocolate);
189            assertNotNull(needsChocolate.choc);
190            assertEquals(true, needsChocolate.choc.milky);
191            assertNotNull(needsChocolate.choc.cocaoBeans);
192            assertEquals("Cadbury's", needsChocolate.choc.name);
193        }
194    
195        public static class Chocolatier2 implements Provider {
196            private final boolean milky;
197            public Chocolatier2(boolean milky) {
198                this.milky = milky;
199            }
200            public Chocolate provide(CocaoBeans cocaoBeans, String name) {
201                return new Chocolate(milky, cocaoBeans, name);
202            }
203        }
204    
205        @Test
206        public void providedTypeCanBeDyanamicallyDeterminedFromInstanceRatherThanType() {
207            DefaultPicoContainer dpc = new DefaultPicoContainer();
208    
209            // a simlation of what a web framework would essentially do in a thread-local way
210            dpc.addComponent(new StubHttpRequest("chocolate", "Lindt"));
211    
212            // this is the style being recomended for automatic request-params -> beans
213            dpc.addAdapter(new ExampleRequestReader(Chocolate.class, "chocolate"));
214    
215            dpc.addComponent(NeedsChocolate.class);
216            NeedsChocolate needsChocolate = dpc.getComponent(NeedsChocolate.class);
217            assertNotNull(needsChocolate);
218            assertNotNull(needsChocolate.choc);
219            assertEquals(true, needsChocolate.choc.milky);
220            assertNotNull(needsChocolate.choc.cocaoBeans);
221            assertEquals("Lindt", needsChocolate.choc.name);
222        }
223    
224    
225        public static class StubHttpRequest {
226            private final String key;
227            private final String value;
228    
229            public StubHttpRequest(String key, String value) {
230                this.key = key;
231                this.value = value;
232            }
233    
234            public String getParameter(String name) {
235                return name.equals(key) ? value : null;
236            }
237        }
238    
239        public static class ExampleRequestReader extends ProviderAdapter {
240            private final Class clazz;
241            private final String paramName;
242    
243            public ExampleRequestReader(Class clazz, String paramName) {
244                this.clazz = clazz;
245                this.paramName = paramName;
246            }
247    
248            public Class getComponentImplementation() {
249                return clazz;
250            }
251    
252            public Object provide(StubHttpRequest req) {
253                try {
254                    return clazz.getConstructor(String.class).newInstance(req.getParameter(paramName));
255                } catch (Exception e) {
256                    throw new RuntimeException(e);  
257                }
258            }
259        }
260    
261        @Test
262        public void providersCanHaveLifecyclesToo() {
263            ComponentMonitor componentMonitor = new LifecycleComponentMonitor();
264            LifecycleStrategy lifecycleStrategy = new
265                    ReflectionLifecycleStrategy(componentMonitor);
266    
267            MutablePicoContainer pico = new DefaultPicoContainer(new
268                    ThreadCaching(), lifecycleStrategy, null);
269    
270            StringBuilder sb = new StringBuilder();
271            pico.addComponent(Configuration.class);
272            pico.addAdapter(new ProviderAdapter(lifecycleStrategy, new ComponentProvider(sb)));
273            Object foo = pico.getComponent(Component.class);
274            pico.start();
275            pico.stop();
276            assertEquals("@<>", sb.toString());
277    
278        }
279    
280        public class ComponentProvider implements Provider {
281            private StringBuilder sb;
282    
283            public ComponentProvider(StringBuilder sb) {
284                this.sb = sb;
285            }
286    
287            public Component provide(Configuration config) {
288                return new ComponentImpl(sb, config.getHost(), config.getPort());
289            }
290        }
291    
292        public static class Configuration {
293    
294            public String getHost() {
295                return "hello";
296            }
297    
298            public int getPort() {
299                return 99;
300            }
301    
302            public void start() {
303            }
304    
305            public void stop() {
306            }
307    
308        }
309    
310        public static interface Component {
311    
312            public void start();
313    
314            public void stop();
315    
316        }
317    
318        public static class ComponentImpl implements Component {
319    
320            private StringBuilder sb;
321    
322            public ComponentImpl(StringBuilder sb, String host, int port) {
323                this.sb = sb.append("@");
324            }
325    
326            public void start() {
327                sb.append("<");
328            }
329            public void stop() {
330                sb.append(">");
331            }
332    
333        }
334    
335    
336    }