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 }