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