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 *****************************************************************************/
009package org.picocontainer.injectors;
010
011import org.junit.Test;
012import org.picocontainer.ComponentMonitor;
013import org.picocontainer.DefaultPicoContainer;
014import org.picocontainer.LifecycleStrategy;
015import org.picocontainer.MutablePicoContainer;
016import org.picocontainer.PicoCompositionException;
017import org.picocontainer.annotations.Nullable;
018import org.picocontainer.behaviors.Caching;
019import org.picocontainer.behaviors.ThreadCaching;
020import org.picocontainer.lifecycle.ReflectionLifecycleStrategy;
021import org.picocontainer.monitors.LifecycleComponentMonitor;
022
023import static org.junit.Assert.assertEquals;
024import static org.junit.Assert.assertNotNull;
025import static org.junit.Assert.assertTrue;
026import static org.junit.Assert.fail;
027
028public 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}