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 DefaultPicoContainer pico = new DefaultPicoContainer();
270 pico.setName("parent");
271 pico.addAdapter(bAdapter);
272 pico.addAdapter(aAdapter);
273
274 try {
275 aAdapter.getComponentInstance(pico, ComponentAdapter.NOTHING.class);
276 } catch (AbstractInjector.UnsatisfiableDependenciesException e) {
277 String message = e.getMessage().replace("org.picocontainer.injectors.SetterInjectorTestCase$", "");
278 message = message.replace("interface java.util.List, class java.lang.String", "class java.lang.String, interface java.util.List");
279 assertEquals("A has unsatisfied dependencies [class java.lang.String, interface java.util.List] for members [public void A.setString(java.lang.String), public void A.setList(java.util.List)] from parent:2<|",
280 message);
281 }
282 }
283
284 @Test public void testAllUnsatisfiableDependenciesAreSignalled2() {
285 SetterInjector aAdapter = new SetterInjector(A2.class, A2.class, null, new NullComponentMonitor(),
286 "set", "", false, false);
287 SetterInjector bAdapter = new SetterInjector("b", B.class, null, new NullComponentMonitor(),
288 "set", "", false, false);
289
290 MutablePicoContainer pico = new DefaultPicoContainer();
291 pico.addComponent(List.class, ArrayList.class)
292 .addComponent(String.class, "foo")
293 .addAdapter(bAdapter)
294 .addAdapter(aAdapter);
295
296 aAdapter.getComponentInstance(pico, ComponentAdapter.NOTHING.class);
297
298 assertNotNull(aAdapter);
299
300 A2 a = pico.getComponent(A2.class);
301 assertTrue(a.getList() == null);
302 assertTrue(a.getString() == null);
303 }
304
305 public static class InitBurp {
306
307 private Wind wind;
308
309 public void initWind(Wind wind) {
310 this.wind = wind;
311 }
312 }
313
314 public static class SetterBurp {
315
316 private Wind wind;
317
318 public void setWind(Wind wind) {
319 this.wind = wind;
320 }
321 }
322
323 public static class Wind {
324
325 }
326
327 @Test public void testSetterMethodInjectionToContrastWithThatBelow() {
328
329 MutablePicoContainer pico = new DefaultPicoContainer();
330 pico.addAdapter(new SetterInjector(SetterBurp.class, SetterBurp.class, Parameter.DEFAULT, new NullComponentMonitor(),
331 "set", "", false, false));
332 pico.addComponent(Wind.class, new Wind());
333 SetterBurp burp = pico.getComponent(SetterBurp.class);
334 assertNotNull(burp);
335 assertNotNull(burp.wind);
336 }
337
338 @Test public void testNonSetterMethodInjection() {
339 MutablePicoContainer pico = new DefaultPicoContainer();
340 pico.addAdapter(new SetterInjector(InitBurp.class, InitBurp.class, Parameter.DEFAULT, new NullComponentMonitor(),
341 "set", "", false, false) {
342 protected String getInjectorPrefix() {
343 return "init";
344 }
345 });
346 pico.addComponent(Wind.class, new Wind());
347 InitBurp burp = pico.getComponent(InitBurp.class);
348 assertNotNull(burp);
349 assertNotNull(burp.wind);
350 }
351
352 @Test public void testNonSetterMethodInjectionWithoutOverridingSetterPrefix() {
353 MutablePicoContainer pico = new DefaultPicoContainer();
354 pico.addAdapter(new SetterInjector(InitBurp.class, InitBurp.class, new Parameter[0], new NullComponentMonitor(),
355 "set", "", false, false));
356 pico.addComponent(Wind.class, new Wind());
357 InitBurp burp = pico.getComponent(InitBurp.class);
358 assertNotNull(burp);
359 assertTrue(burp.wind == null);
360 }
361
362
363 public static class C {
364 private B b;
365 private List l;
366 private final boolean asBean;
367
368 public C() {
369 asBean = true;
370 }
371
372 public C(B b) {
373 this.l = new ArrayList();
374 this.b = b;
375 asBean = false;
376 }
377
378 public void setB(B b) {
379 this.b = b;
380 }
381
382 public B getB() {
383 return b;
384 }
385
386 public void setList(List l) {
387 this.l = l;
388 }
389
390 public List getList() {
391 return l;
392 }
393
394 public boolean instantiatedAsBean() {
395 return asBean;
396 }
397 }
398
399 @Test public void testHybridBeans() {
400 SetterInjector bAdapter = new SetterInjector("b", B.class, null, new NullComponentMonitor(),
401 "set", "", false, false);
402 SetterInjector cAdapter = new SetterInjector("c", C.class, null, new NullComponentMonitor(),
403 "set", "", false, false);
404 SetterInjector cNullAdapter = new SetterInjector("c0", C.class, null, new NullComponentMonitor(),
405 "set", "", false, false);
406
407 MutablePicoContainer pico = new DefaultPicoContainer();
408 pico.addAdapter(bAdapter);
409 pico.addAdapter(cAdapter);
410 pico.addAdapter(cNullAdapter);
411 pico.addComponent(ArrayList.class);
412
413 C c = (C) cAdapter.getComponentInstance(pico, ComponentAdapter.NOTHING.class);
414 assertTrue(c.instantiatedAsBean());
415 C c0 = (C) cNullAdapter.getComponentInstance(pico, ComponentAdapter.NOTHING.class);
416 assertTrue(c0.instantiatedAsBean());
417 }
418
419 public static class Yin {
420 private Yang yang;
421
422 public void setYin(Yang yang) {
423 this.yang = yang;
424 }
425
426 public Yang getYang() {
427 return yang;
428 }
429 }
430
431 public static class Yang {
432 private Yin yin;
433
434 public void setYang(Yin yin) {
435 this.yin = yin;
436 }
437
438 public Yin getYin() {
439 return yin;
440 }
441 }
442
443 //@Test http://jira.codehaus.org/browse/PICO-188
444 public void shouldBeAbleToHandleMutualDependenciesWithSetterInjection() {
445 MutablePicoContainer pico = new DefaultPicoContainer(new Caching().wrap(new SetterInjection()));
446
447 pico.addComponent(Yin.class);
448 pico.addComponent(Yang.class);
449
450 Yin yin = pico.getComponent(Yin.class);
451 Yang yang = pico.getComponent(Yang.class);
452
453 assertSame(yin, yang.getYin());
454 assertSame(yang, yin.getYang());
455 }
456
457 @Test
458 public void shouldProvideEmptyArgumentListForDefaultConstructor() throws Exception {
459 final Mockery mockery = new Mockery();
460 final ComponentMonitor componentMonitor = mockery.mock(ComponentMonitor.class);
461 final MutablePicoContainer pico = new DefaultPicoContainer(
462 new SetterInjection(), new NullLifecycleStrategy(), new EmptyPicoContainer(), componentMonitor
463 );
464
465 mockery.checking(new Expectations() {{
466 oneOf(componentMonitor).newInjector(
467 with(any(org.picocontainer.Injector.class))
468 ); will(returnSameInjector());
469 }});
470
471 pico.addComponent(B.class);
472
473 mockery.checking(new Expectations() {{
474 oneOf(componentMonitor).instantiating(
475 with(same(pico)), with(any(ComponentAdapter.class)), with(equal(B.class.getConstructor()))
476 ); will(new CustomAction("return same constructor") {
477 public Object invoke(Invocation invocation) {
478 return invocation.getParameter(2);
479 }
480 });
481 oneOf(componentMonitor).instantiated(
482 with(same(pico)), with(any(ComponentAdapter.class)), with(equal(B.class.getConstructor())),
483 with(any(Object.class)), with(equal(new Object[0])), with(any(long.class))
484 );
485 }});
486 pico.getComponent(B.class);
487
488 mockery.assertIsSatisfied();
489 }
490
491 private CustomAction returnSameInjector() {
492 return new CustomAction("return same injector") {
493 public Object invoke(Invocation invocation) {
494 return invocation.getParameter(0);
495 }
496 };
497 }
498 }