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