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