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     * Original code by                                                          *
009     *****************************************************************************/
010    package org.picocontainer.injectors;
011    
012    import static org.junit.Assert.assertEquals;
013    import static org.junit.Assert.assertNotNull;
014    import static org.junit.Assert.assertSame;
015    import static org.junit.Assert.assertTrue;
016    import static org.picocontainer.parameters.ComponentParameter.DEFAULT;
017    
018    import java.util.ArrayList;
019    import java.util.List;
020    
021    import org.junit.Test;
022    import org.picocontainer.Characteristics;
023    import org.picocontainer.ComponentAdapter;
024    import org.picocontainer.ComponentFactory;
025    import org.picocontainer.DefaultPicoContainer;
026    import org.picocontainer.MutablePicoContainer;
027    import org.picocontainer.NameBinding;
028    import org.picocontainer.Parameter;
029    import org.picocontainer.behaviors.Caching;
030    import org.picocontainer.lifecycle.NullLifecycleStrategy;
031    import org.picocontainer.monitors.AbstractComponentMonitor;
032    import org.picocontainer.monitors.NullComponentMonitor;
033    import org.picocontainer.parameters.ConstantParameter;
034    import org.picocontainer.tck.AbstractComponentAdapterTest;
035    import org.picocontainer.testmodel.NullLifecycle;
036    import org.picocontainer.testmodel.PersonBean;
037    import org.picocontainer.testmodel.PurseBean;
038    import org.picocontainer.testmodel.SimpleTouchable;
039    import org.picocontainer.testmodel.Touchable;
040    
041    
042    @SuppressWarnings("serial")
043    public class SetterInjectorTestCase
044        extends AbstractComponentAdapterTest {
045    
046        protected Class getComponentAdapterType() {
047            return SetterInjector.class;
048        }
049    
050        protected ComponentFactory createDefaultComponentFactory() {
051            return new Caching().wrap(new SetterInjection());
052        }
053    
054        protected ComponentAdapter prepDEF_verifyWithoutDependencyWorks(MutablePicoContainer picoContainer) {
055            return new SetterInjector(PersonBean.class, PersonBean.class, new Parameter[] {new ConstantParameter(
056                    "Pico Container")}, new NullComponentMonitor(), new NullLifecycleStrategy(), "set", false);
057        }
058    
059        protected ComponentAdapter prepDEF_verifyDoesNotInstantiate(MutablePicoContainer picoContainer) {
060            picoContainer.addComponent("Pico Container");
061            return new SetterInjector(DeadBody.class, DeadBody.class, new Parameter[] {DEFAULT}, new NullComponentMonitor(), new NullLifecycleStrategy(),
062                                      "set", false);
063        }
064    
065        protected ComponentAdapter prepDEF_visitable() {
066            return new SetterInjector(PersonBean.class, PersonBean.class, new Parameter[] {new ConstantParameter(
067                    "Pico Container")}, new NullComponentMonitor(), new NullLifecycleStrategy(), "set", false);
068    
069        }
070    
071        protected ComponentAdapter prepSER_isSerializable(MutablePicoContainer picoContainer) {
072            picoContainer.addComponent("Pico Container");
073            return new SetterInjector(PersonBean.class, PersonBean.class, new Parameter[] {DEFAULT}, new NullComponentMonitor(), new NullLifecycleStrategy(),
074                                      "set", false);
075        }
076    
077        protected ComponentAdapter prepSER_isXStreamSerializable(MutablePicoContainer picoContainer) {
078            return prepSER_isSerializable(picoContainer);
079        }
080    
081        protected ComponentAdapter prepDEF_isAbleToTakeParameters(MutablePicoContainer picoContainer) {
082            picoContainer.addComponent("Pico Container");
083            picoContainer.addComponent(PersonBean.class);
084            SetterInjector componentAdapter = new SetterInjector(
085                    PurseBean.class, MoneyPurse.class, new Parameter[] {DEFAULT, new ConstantParameter(100.0)}, new NullComponentMonitor(), new NullLifecycleStrategy(),
086                    "set", false);
087            return picoContainer.as(Characteristics.NO_CACHE).addAdapter(componentAdapter).getComponentAdapter(PurseBean.class, (NameBinding) null);
088        }
089    
090        public static class MoneyPurse
091                extends PurseBean {
092            double money;
093    
094            public double getMoney() {
095                return money;
096            }
097    
098            public void setMoney(double money) {
099                this.money = money;
100            }
101        }
102    
103        protected ComponentAdapter prepVER_verificationFails(MutablePicoContainer picoContainer) {
104            picoContainer.addComponent("Pico Container");
105            picoContainer.addComponent(PersonBean.class);
106            SetterInjector componentAdapter = new SetterInjector(
107                    PurseBean.class, MoneyPurse.class, new Parameter[] {DEFAULT},new NullComponentMonitor(), new NullLifecycleStrategy(),
108                    "set", false);
109            return picoContainer.addAdapter(componentAdapter).getComponentAdapter(PurseBean.class, (NameBinding) null);
110        }
111    
112        protected ComponentAdapter prepINS_createsNewInstances(MutablePicoContainer picoContainer) {
113            picoContainer.addComponent("Pico Container");
114            return new SetterInjector(PersonBean.class, PersonBean.class, new Parameter[] {DEFAULT}, new NullComponentMonitor(), new NullLifecycleStrategy(),
115                                      "set", false);
116        }
117    
118        public static class Ghost
119                extends PersonBean {
120            public Ghost() {
121                throw new VerifyError("test");
122            }
123        }
124    
125        protected ComponentAdapter prepINS_errorIsRethrown(MutablePicoContainer picoContainer) {
126            picoContainer.addComponent("Pico Container");
127            return new SetterInjector(Ghost.class, Ghost.class, new Parameter[] {DEFAULT}, new NullComponentMonitor(), new NullLifecycleStrategy(),
128                                      "set", false);
129        }
130    
131        public static class DeadBody
132                extends PersonBean {
133            public DeadBody() {
134                throw new RuntimeException("test");
135            }
136        }
137    
138        protected ComponentAdapter prepINS_runtimeExceptionIsRethrown(MutablePicoContainer picoContainer) {
139            picoContainer.addComponent("Pico Container");
140            return new SetterInjector(DeadBody.class, DeadBody.class, new Parameter[] {DEFAULT}, new NullComponentMonitor(), new NullLifecycleStrategy(),
141                                      "set", false);
142        }
143    
144        public static class HidingPersion
145                extends PersonBean {
146            public HidingPersion() throws Exception {
147                throw new Exception("test");
148            }
149        }
150    
151        protected ComponentAdapter prepINS_normalExceptionIsRethrownInsidePicoInitializationException(
152                MutablePicoContainer picoContainer) {
153            picoContainer.addComponent("Pico Container");
154            return new SetterInjector(
155                    HidingPersion.class, HidingPersion.class, new Parameter[] {DEFAULT}, new NullComponentMonitor(), new NullLifecycleStrategy(),
156                    "set", false);
157        }
158    
159        protected ComponentAdapter prepRES_dependenciesAreResolved(MutablePicoContainer picoContainer) {
160            picoContainer.addComponent("Pico Container");
161            picoContainer.addComponent(PersonBean.class);
162            return new SetterInjector(PurseBean.class, PurseBean.class, new Parameter[] {DEFAULT}, new NullComponentMonitor(), new NullLifecycleStrategy(),
163                                      "set", false);
164        }
165    
166        public static class WealthyPerson
167                extends PersonBean {
168            PurseBean purse;
169    
170            public PurseBean getPurse() {
171                return purse;
172            }
173    
174            public void setPurse(PurseBean purse) {
175                this.purse = purse;
176            }
177        }
178    
179        protected ComponentAdapter prepRES_failingVerificationWithCyclicDependencyException(MutablePicoContainer picoContainer) {
180            picoContainer.addComponent("Pico Container");
181            picoContainer.addComponent(PersonBean.class, WealthyPerson.class);
182            SetterInjector componentAdapter = new SetterInjector(
183                    PurseBean.class, PurseBean.class, new Parameter[] {DEFAULT}, new NullComponentMonitor(), new NullLifecycleStrategy(),
184                    "set", false);
185            return picoContainer.as(Characteristics.NO_CACHE).addAdapter(componentAdapter).getComponentAdapter(PurseBean.class, (NameBinding) null);
186        }
187    
188        protected ComponentAdapter prepRES_failingInstantiationWithCyclicDependencyException(MutablePicoContainer picoContainer) {
189            picoContainer.addComponent("Pico Container");
190            picoContainer.addComponent(PersonBean.class, WealthyPerson.class);
191            SetterInjector componentAdapter = new SetterInjector(
192                    PurseBean.class, PurseBean.class, new Parameter[] {DEFAULT}, new NullComponentMonitor(), new NullLifecycleStrategy(),
193                    "set", false);
194            return picoContainer.as(Characteristics.NO_CACHE).addAdapter(componentAdapter).getComponentAdapter(PurseBean.class, (NameBinding) null);
195        }
196    
197        public static class A {
198            private B b;
199            private String string;
200            private List list;
201    
202            public void setB(B b) {
203                this.b = b;
204            }
205    
206            public B getB() {
207                return b;
208            }
209    
210            public String getString() {
211                return string;
212            }
213    
214            public void setString(String string) {
215                this.string = string;
216            }
217    
218            public List getList() {
219                return list;
220            }
221    
222            public void setList(List list) {
223                this.list = list;
224            }
225        }
226    
227        public static class A2 {
228            private B b;
229            private String string;
230            private List list;
231    
232            public void injectB(B b) {
233                this.b = b;
234            }
235    
236            public B getB() {
237                return b;
238            }
239    
240            public String getString() {
241                return string;
242            }
243    
244            public void injectString(String string) {
245                this.string = string;
246            }
247    
248            public List getList() {
249                return list;
250            }
251    
252            public void injectList(List list) {
253                this.list = list;
254            }
255        }
256    
257    
258        public static class B {
259        }
260    
261        @Test public void testAllUnsatisfiableDependenciesAreSignalled() {
262            SetterInjector aAdapter = new SetterInjector("a", A.class, Parameter.DEFAULT, new NullComponentMonitor(), new NullLifecycleStrategy(),
263                                                         "set", false);
264            SetterInjector bAdapter = new SetterInjector("b", B.class, Parameter.DEFAULT, new NullComponentMonitor(), new NullLifecycleStrategy(),
265                                                         "set", false);
266    
267            MutablePicoContainer pico = new DefaultPicoContainer();
268            pico.addAdapter(bAdapter);
269            pico.addAdapter(aAdapter);
270    
271            try {
272                aAdapter.getComponentInstance(pico, ComponentAdapter.NOTHING.class);
273            } catch (AbstractInjector.UnsatisfiableDependenciesException e) {
274                assertTrue(e.getUnsatisfiableDependencies().contains(List.class));
275                assertTrue(e.getUnsatisfiableDependencies().contains(String.class));
276            }
277        }
278    
279        @Test public void testAllUnsatisfiableDependenciesAreSignalled2() {
280            SetterInjector aAdapter = new SetterInjector(A2.class, A2.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(),
281                                                         "set", false);
282            SetterInjector bAdapter = new SetterInjector("b", B.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(),
283                                                         "set", false);
284    
285            MutablePicoContainer pico = new DefaultPicoContainer();
286            pico.addComponent(List.class, ArrayList.class)
287                .addComponent(String.class, "foo")
288                .addAdapter(bAdapter)
289                .addAdapter(aAdapter);
290    
291            aAdapter.getComponentInstance(pico, ComponentAdapter.NOTHING.class);
292    
293            assertNotNull(aAdapter);
294    
295            A2 a = pico.getComponent(A2.class);
296            assertTrue(a.getList() == null);
297            assertTrue(a.getString() == null);
298        }
299    
300        public static class InitBurp {
301    
302            private Wind wind;
303    
304            public void initWind(Wind wind) {
305                this.wind = wind;
306            }
307        }
308    
309        public static class SetterBurp {
310    
311            private Wind wind;
312    
313            public void setWind(Wind wind) {
314                this.wind = wind;
315            }
316        }
317    
318        public static class Wind {
319    
320        }
321    
322        @Test public void testSetterMethodInjectionToContrastWithThatBelow() {
323    
324            MutablePicoContainer pico = new DefaultPicoContainer();
325            pico.addAdapter(new SetterInjector(SetterBurp.class, SetterBurp.class, Parameter.DEFAULT, new NullComponentMonitor(), new NullLifecycleStrategy(),
326                                               "set", false));
327            pico.addComponent(Wind.class, new Wind());
328            SetterBurp burp = pico.getComponent(SetterBurp.class);
329            assertNotNull(burp);
330            assertNotNull(burp.wind);
331        }
332    
333        @Test public void testNonSetterMethodInjection() {
334            MutablePicoContainer pico = new DefaultPicoContainer();
335            pico.addAdapter(new SetterInjector(InitBurp.class, InitBurp.class, Parameter.DEFAULT, new NullComponentMonitor(), new NullLifecycleStrategy(),
336                                               "set", false) {
337                protected String getInjectorPrefix() {
338                    return "init";
339                }
340            });
341            pico.addComponent(Wind.class, new Wind());
342            InitBurp burp = pico.getComponent(InitBurp.class);
343            assertNotNull(burp);
344            assertNotNull(burp.wind);
345        }
346    
347        @Test public void testNonSetterMethodInjectionWithoutOverridingSetterPrefix() {
348            MutablePicoContainer pico = new DefaultPicoContainer();
349            pico.addAdapter(new SetterInjector(InitBurp.class, InitBurp.class, Parameter.ZERO, new NullComponentMonitor(), new NullLifecycleStrategy(),
350                                               "set", false));
351            pico.addComponent(Wind.class, new Wind());
352            InitBurp burp = pico.getComponent(InitBurp.class);
353            assertNotNull(burp);
354            assertTrue(burp.wind == null);
355        }
356    
357    
358        public static class C {
359            private B b;
360            private List l;
361            private final boolean asBean;
362    
363            public C() {
364                asBean = true;
365            }
366    
367            public C(B b) {
368                this.l = new ArrayList();
369                this.b = b;
370                asBean = false;
371            }
372    
373            public void setB(B b) {
374                this.b = b;
375            }
376    
377            public B getB() {
378                return b;
379            }
380    
381            public void setList(List l) {
382                this.l = l;
383            }
384    
385            public List getList() {
386                return l;
387            }
388    
389            public boolean instantiatedAsBean() {
390                return asBean;
391            }
392        }
393    
394        @Test public void testHybridBeans() {
395            SetterInjector bAdapter = new SetterInjector("b", B.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(),
396                                                         "set", false);
397            SetterInjector cAdapter = new SetterInjector("c", C.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(),
398                                                         "set", false);
399            SetterInjector cNullAdapter = new SetterInjector("c0", C.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(),
400                                                             "set", false);
401    
402            MutablePicoContainer pico = new DefaultPicoContainer();
403            pico.addAdapter(bAdapter);
404            pico.addAdapter(cAdapter);
405            pico.addAdapter(cNullAdapter);
406            pico.addComponent(ArrayList.class);
407    
408            C c = (C) cAdapter.getComponentInstance(pico, ComponentAdapter.NOTHING.class);
409            assertTrue(c.instantiatedAsBean());
410            C c0 = (C) cNullAdapter.getComponentInstance(pico, ComponentAdapter.NOTHING.class);
411            assertTrue(c0.instantiatedAsBean());
412        }
413    
414        public static class Yin {
415            private Yang yang;
416    
417            public void setYin(Yang yang) {
418                this.yang = yang;
419            }
420    
421            public Yang getYang() {
422                return yang;
423            }
424        }
425    
426        public static class Yang {
427            private Yin yin;
428    
429            public void setYang(Yin yin) {
430                this.yin = yin;
431            }
432    
433            public Yin getYin() {
434                return yin;
435            }
436        }
437    
438        //@Test  http://jira.codehaus.org/browse/PICO-188
439        public void shouldBeAbleToHandleMutualDependenciesWithSetterInjection() {
440            MutablePicoContainer pico = new DefaultPicoContainer(new Caching().wrap(new SetterInjection()));
441    
442            pico.addComponent(Yin.class);
443            pico.addComponent(Yang.class);
444    
445            Yin yin = pico.getComponent(Yin.class);
446            Yang yang = pico.getComponent(Yang.class);
447    
448            assertSame(yin, yang.getYin());
449            assertSame(yang, yin.getYang());
450        }
451        
452        @Test public void testCustomLifecycleCanBeInjected() throws NoSuchMethodException {
453            RecordingLifecycleStrategy strategy = new RecordingLifecycleStrategy(new StringBuffer());
454            SetterInjector sica = new SetterInjector(
455                    NullLifecycle.class, NullLifecycle.class, new Parameter[0],
456                    new AbstractComponentMonitor(), strategy, "set", false);
457            Touchable touchable = new SimpleTouchable();
458            sica.start(touchable);
459            sica.stop(touchable);
460            sica.dispose(touchable);
461            assertEquals("<start<stop<dispose", strategy.recording());
462        }
463    
464    }