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 * Idea by Rachel Davies, Original code by Aslak Hellesoy and Paul Hammant *
009 *****************************************************************************/
010 package org.picocontainer;
011
012 import static org.junit.Assert.*;
013 import org.junit.Test;
014 import static org.picocontainer.Characteristics.CDI;
015 import static org.picocontainer.Characteristics.SDI;
016 import org.picocontainer.injectors.FactoryInjector;
017 import org.picocontainer.annotations.Inject;
018 import org.picocontainer.behaviors.Caching;
019 import org.picocontainer.behaviors.Decorating;
020 import org.picocontainer.behaviors.FieldDecorating;
021 import org.picocontainer.containers.EmptyPicoContainer;
022 import org.picocontainer.injectors.*;
023 import org.picocontainer.lifecycle.NullLifecycleStrategy;
024 import org.picocontainer.monitors.NullComponentMonitor;
025 import org.picocontainer.monitors.WriterComponentMonitor;
026 import org.picocontainer.tck.AbstractPicoContainerTest;
027 import org.picocontainer.testmodel.DecoratedTouchable;
028 import org.picocontainer.testmodel.DependsOnTouchable;
029 import org.picocontainer.testmodel.SimpleTouchable;
030 import org.picocontainer.testmodel.Touchable;
031
032 import java.io.Serializable;
033 import java.io.StringWriter;
034 import java.lang.reflect.Field;
035 import java.lang.reflect.Member;
036 import java.lang.reflect.Type;
037 import java.util.ArrayList;
038 import java.util.Collection;
039 import java.util.HashMap;
040 import java.util.HashSet;
041 import java.util.LinkedList;
042 import java.util.List;
043 import java.util.Map;
044 import java.util.Properties;
045
046 /**
047 * @author Aslak Hellesøy
048 * @author Paul Hammant
049 * @author Ward Cunningham
050 * @author Mauro Talevi
051 */
052 public final class DefaultPicoContainerTestCase extends AbstractPicoContainerTest {
053
054 protected MutablePicoContainer createPicoContainer(PicoContainer parent) {
055 return new DefaultPicoContainer(parent);
056 }
057
058 protected Properties[] getProperties() {
059 return new Properties[0];
060 }
061
062 @Test public void testInstantiationWithNullComponentFactory() {
063 try {
064 new DefaultPicoContainer((ComponentFactory) null, null);
065 fail("NPE expected");
066 } catch (NullPointerException e) {
067 // expected
068 }
069 }
070
071 @Test public void testUpDownDependenciesCannotBeFollowed() {
072 MutablePicoContainer parent = createPicoContainer(null);
073 MutablePicoContainer child = createPicoContainer(parent);
074
075 // ComponentF -> ComponentA -> ComponentB+C
076 child.addComponent(ComponentF.class);
077 parent.addComponent(ComponentA.class);
078 child.addComponent(ComponentB.class);
079 child.addComponent(ComponentC.class);
080
081 try {
082 child.getComponent(ComponentF.class);
083 fail("Thrown "
084 + AbstractInjector.UnsatisfiableDependenciesException.class
085 .getName() + " expected");
086 } catch (final AbstractInjector.UnsatisfiableDependenciesException e) {
087 assertEquals(ComponentB.class, e.getUnsatisfiedDependencyType());
088 }
089 }
090
091 @Test public void testComponentsCanBeRemovedByInstance() {
092 MutablePicoContainer pico = createPicoContainer(null);
093 pico.addComponent(HashMap.class);
094 pico.addComponent(ArrayList.class);
095 List list = pico.getComponent(List.class);
096 pico.removeComponentByInstance(list);
097 assertEquals(1, pico.getComponentAdapters().size());
098 assertEquals(1, pico.getComponents().size());
099 assertEquals(HashMap.class, pico.getComponent(Serializable.class)
100 .getClass());
101 }
102
103 @Test public void testComponentInstancesListIsReturnedForNullType() {
104 MutablePicoContainer pico = createPicoContainer(null);
105 List componentInstances = pico.getComponents(null);
106 assertNotNull(componentInstances);
107 assertEquals(0, componentInstances.size());
108 }
109
110 @Test public void testComponentsWithCommonSupertypeWhichIsAConstructorArgumentCanBeLookedUpByConcreteType() {
111 MutablePicoContainer pico = createPicoContainer(null);
112 pico.addComponent(LinkedList.class, LinkedList.class, Parameter.ZERO);
113 pico.addComponent(ArrayList.class);
114 assertEquals(ArrayList.class, pico
115 .getComponent((Class) ArrayList.class).getClass());
116 }
117
118 /*
119 * When pico tries to resolve DecoratedTouchable it find as dependency
120 * itself and SimpleTouchable. Problem is basically the same as above. Pico
121 * should not consider self as solution.
122 *
123 * JS fixed it ( PICO-222 ) KP
124 */
125 @Test public void testUnambiguouSelfDependency() {
126 MutablePicoContainer pico = createPicoContainer(null);
127 pico.addComponent(SimpleTouchable.class);
128 pico.addComponent(DecoratedTouchable.class);
129 Touchable t = (Touchable) pico
130 .getComponent((Object) DecoratedTouchable.class);
131 assertNotNull(t);
132 }
133
134 @Test public void testPicoUsedInBuilderStyle() {
135 MutablePicoContainer pico = createPicoContainer(null);
136 Touchable t = pico.change(Characteristics.CACHE).addComponent(
137 SimpleTouchable.class).addComponent(DecoratedTouchable.class)
138 .getComponent(DecoratedTouchable.class);
139 SimpleTouchable t2 = pico.getComponent(SimpleTouchable.class);
140 assertNotNull(t);
141 assertNotNull(t2);
142 t.touch();
143 assertTrue(t2.wasTouched);
144 }
145
146 public static class Thingie {
147 public Thingie(List c) {
148 assertNotNull(c);
149 }
150 }
151
152 @Test public void testThangCanBeInstantiatedWithArrayList() {
153 MutablePicoContainer pico = new DefaultPicoContainer();
154 pico.addComponent(Thingie.class);
155 pico.addComponent(ArrayList.class);
156 assertNotNull(pico.getComponent(Thingie.class));
157 }
158
159 @Test public void testGetComponentAdaptersOfTypeNullReturnsEmptyList() {
160 DefaultPicoContainer pico = new DefaultPicoContainer();
161 List adapters = pico.getComponentAdapters(null);
162 assertNotNull(adapters);
163 assertEquals(0, adapters.size());
164 }
165
166 public static class Service {
167 }
168
169 public static final class TransientComponent {
170 private final Service service;
171
172 public TransientComponent(Service service) {
173 this.service = service;
174 }
175 }
176
177 @Test public void testDefaultPicoContainerReturnsNewInstanceForEachCallWhenUsingTransientComponentAdapter() {
178
179 DefaultPicoContainer picoContainer = new DefaultPicoContainer(
180 new Caching().wrap(new ConstructorInjection()));
181
182 picoContainer.addComponent(Service.class);
183 picoContainer.as(Characteristics.NO_CACHE).addAdapter(
184 new ConstructorInjector(TransientComponent.class,
185 TransientComponent.class, null,
186 new NullComponentMonitor(),
187 new NullLifecycleStrategy(), false));
188 TransientComponent c1 = picoContainer
189 .getComponent(TransientComponent.class);
190 TransientComponent c2 = picoContainer
191 .getComponent(TransientComponent.class);
192 assertNotSame(c1, c2);
193 assertSame(c1.service, c2.service);
194 }
195
196 public static class DependsOnCollection {
197 public DependsOnCollection(Collection c) {
198 }
199 }
200
201 @Test public void testShouldProvideInfoAboutDependingWhenAmbiguityHappens() {
202 MutablePicoContainer pico = this.createPicoContainer(null);
203 pico.addComponent(new ArrayList());
204 pico.addComponent(new LinkedList());
205 pico.addComponent(DependsOnCollection.class);
206 try {
207 pico.getComponent(DependsOnCollection.class);
208 fail();
209 } catch (AbstractInjector.AmbiguousComponentResolutionException expected) {
210 String doc = DependsOnCollection.class.getName();
211 assertEquals(
212 "class "
213 + doc
214 + " needs a 'java.util.Collection' injected, but there are too many choices to inject. These:[class java.util.ArrayList, class java.util.LinkedList], refer http://picocontainer.org/ambiguous-injectable-help.html",
215 expected.getMessage());
216 }
217 }
218
219 @Test public void testInstantiationWithMonitorAndParent() {
220 StringWriter writer = new StringWriter();
221 ComponentMonitor monitor = new WriterComponentMonitor(writer);
222 DefaultPicoContainer parent = new DefaultPicoContainer();
223 DefaultPicoContainer child = new DefaultPicoContainer(monitor, parent);
224 parent.addComponent("st", SimpleTouchable.class);
225 child.addComponent("dot", DependsOnTouchable.class);
226 DependsOnTouchable dot = (DependsOnTouchable) child.getComponent("dot");
227 assertNotNull(dot);
228 assertTrue("writer not empty", writer.toString().length() > 0);
229 }
230
231 @Test public void testStartCapturedByMonitor() {
232 final StringBuffer sb = new StringBuffer();
233 DefaultPicoContainer dpc = new DefaultPicoContainer(
234 new NullComponentMonitor() {
235 public void invoking(PicoContainer container,
236 ComponentAdapter componentAdapter, Member member,
237 Object instance) {
238 sb.append(member.toString());
239 }
240 });
241 dpc.as(Characteristics.CACHE).addComponent(DefaultPicoContainer.class);
242 dpc.start();
243 assertEquals(
244 "ComponentMonitor should have been notified that the component had been started",
245 "public abstract void org.picocontainer.Startable.start()", sb
246 .toString());
247 }
248
249 public static class StartableClazz implements Startable {
250 private MutablePicoContainer _pico;
251
252 public void start() {
253 List<SimpleTouchable> cps = _pico
254 .getComponents(SimpleTouchable.class);
255 assertNotNull(cps);
256 }
257
258 public void stop() {
259 }
260
261 }
262
263 @Test public void testListComponentsOnStart() {
264
265 // This is really discouraged. Breaks basic principals of IoC -
266 // components should not refer
267 // to their containers
268 //
269 // Might be deleted in due coure, along with adaptersClone stuff in DPC
270
271 DefaultPicoContainer dpc = new DefaultPicoContainer();
272 dpc.addComponent(SimpleTouchable.class);
273 StartableClazz cl = new StartableClazz();
274 cl._pico = dpc;
275 dpc.addComponent(cl);
276 dpc.start();
277 }
278
279 @Test public void testCanChangeMonitor() {
280 StringWriter writer1 = new StringWriter();
281 ComponentMonitor monitor1 = new WriterComponentMonitor(writer1);
282 DefaultPicoContainer pico = new DefaultPicoContainer(monitor1);
283 pico.addComponent("t1", SimpleTouchable.class);
284 pico.addComponent("t3", SimpleTouchable.class);
285 Touchable t1 = (Touchable) pico.getComponent("t1");
286 assertNotNull(t1);
287 final String s = writer1.toString();
288 assertTrue("writer not empty", s.length() > 0);
289 StringWriter writer2 = new StringWriter();
290 ComponentMonitor monitor2 = new WriterComponentMonitor(writer2);
291 pico.changeMonitor(monitor2);
292 pico.addComponent("t2", SimpleTouchable.class);
293 Touchable t2 = (Touchable) pico.getComponent("t2");
294 assertNotNull(t2);
295 final String s2 = writer2.toString();
296 assertTrue("writer not empty", s2.length() > 0);
297 assertTrue("writers of same length",
298 writer1.toString().length() == writer2.toString().length());
299 Touchable t3 = (Touchable) pico.getComponent("t3");
300 assertNotNull(t3);
301 assertTrue("old writer was used", writer1.toString().length() < writer2
302 .toString().length());
303 }
304
305 @Test public void testCanChangeMonitorOfChildContainers() {
306 StringWriter writer1 = new StringWriter();
307 ComponentMonitor monitor1 = new WriterComponentMonitor(writer1);
308 DefaultPicoContainer parent = new DefaultPicoContainer();
309 DefaultPicoContainer child = new DefaultPicoContainer(monitor1);
310 parent.addChildContainer(child);
311 child.addComponent("t1", SimpleTouchable.class);
312 child.addComponent("t3", SimpleTouchable.class);
313 Touchable t1 = (Touchable) child.getComponent("t1");
314 assertNotNull(t1);
315 assertTrue("writer not empty", writer1.toString().length() > 0);
316 StringWriter writer2 = new StringWriter();
317 ComponentMonitor monitor2 = new WriterComponentMonitor(writer2);
318 parent.changeMonitor(monitor2);
319 child.addComponent("t2", SimpleTouchable.class);
320 Touchable t2 = (Touchable) child.getComponent("t2");
321 assertNotNull(t2);
322 assertTrue("writer not empty", writer2.toString().length() > 0);
323 String s1 = writer1.toString();
324 String s2 = writer2.toString();
325 assertTrue("writers of same length", s1.length() == s2.length());
326 Touchable t3 = (Touchable) child.getComponent("t3");
327 assertNotNull(t3);
328 assertTrue("old writer was used", writer1.toString().length() < writer2
329 .toString().length());
330 }
331
332 @Test public void testChangeMonitorIsIgnoredIfNotSupportingStrategy() {
333 StringWriter writer = new StringWriter();
334 ComponentMonitor monitor = new WriterComponentMonitor(writer);
335 DefaultPicoContainer parent = new DefaultPicoContainer(
336 new ComponentFactoryWithNoMonitor(
337 new ComponentAdapterWithNoMonitor(new SimpleTouchable())));
338 parent.addChildContainer(new EmptyPicoContainer());
339 parent.addComponent("t1", SimpleTouchable.class);
340 parent.changeMonitor(monitor);
341 assertTrue("writer empty", writer.toString().length() == 0);
342 }
343
344 @Test public void testCanReturnCurrentMonitorFromComponentFactory() {
345 StringWriter writer1 = new StringWriter();
346 ComponentMonitor monitor1 = new WriterComponentMonitor(writer1);
347 DefaultPicoContainer pico = new DefaultPicoContainer(monitor1);
348 assertEquals(monitor1, pico.currentMonitor());
349 StringWriter writer2 = new StringWriter();
350 ComponentMonitor monitor2 = new WriterComponentMonitor(writer2);
351 pico.changeMonitor(monitor2);
352 assertEquals(monitor2, pico.currentMonitor());
353 }
354
355 private static final class ComponentFactoryWithNoMonitor implements ComponentFactory {
356 private final ComponentAdapter adapter;
357
358 public ComponentFactoryWithNoMonitor(ComponentAdapter adapter) {
359 this.adapter = adapter;
360 }
361
362 public ComponentAdapter createComponentAdapter(
363 ComponentMonitor componentMonitor,
364 LifecycleStrategy lifecycleStrategy,
365 Properties componentProperties, Object componentKey,
366 Class componentImplementation, Parameter... parameters)
367 throws PicoCompositionException {
368 return adapter;
369 }
370
371 public void verify(PicoContainer container) {
372 }
373
374 public void accept(PicoVisitor visitor) {
375 visitor.visitComponentFactory(this);
376 }
377 }
378
379 private static final class ComponentAdapterWithNoMonitor implements
380 ComponentAdapter {
381 private final Object instance;
382
383 public ComponentAdapterWithNoMonitor(Object instance) {
384 this.instance = instance;
385 }
386
387 public Object getComponentKey() {
388 return instance.getClass();
389 }
390
391 public Class getComponentImplementation() {
392 return instance.getClass();
393 }
394
395 public Object getComponentInstance(PicoContainer container) throws PicoCompositionException {
396 return getComponentInstance(container, null);
397 }
398
399 public Object getComponentInstance(PicoContainer container, Type into)
400 throws PicoCompositionException {
401 return instance;
402 }
403
404 public void verify(PicoContainer container)
405 throws PicoCompositionException {
406 }
407
408 public void accept(PicoVisitor visitor) {
409 }
410
411 public ComponentAdapter getDelegate() {
412 return null;
413 }
414
415 public ComponentAdapter findAdapterOfType(Class componentAdapterType) {
416 return null;
417 }
418
419 public String getDescriptor() {
420 return null;
421 }
422
423 }
424
425 @Test public void testMakeChildContainer() {
426 MutablePicoContainer parent = new DefaultPicoContainer();
427 parent.addComponent("t1", SimpleTouchable.class);
428 MutablePicoContainer child = parent.makeChildContainer();
429 Object t1 = child.getParent().getComponent("t1");
430 assertNotNull(t1);
431 assertTrue(t1 instanceof SimpleTouchable);
432 }
433
434 @Test public void testCanUseCustomLifecycleStrategyForClassRegistrations() {
435 DefaultPicoContainer dpc = new DefaultPicoContainer(
436 new FailingLifecycleStrategy(), null);
437 dpc.as(Characteristics.CACHE).addComponent(Startable.class,
438 MyStartable.class);
439 try {
440 dpc.start();
441 fail("should have barfed");
442 } catch (RuntimeException e) {
443 assertEquals("foo", e.getMessage());
444 }
445 }
446
447 @Test public void testCanUseCustomLifecycleStrategyForInstanceRegistrations() {
448 DefaultPicoContainer dpc = new DefaultPicoContainer(
449 new FailingLifecycleStrategy(), null);
450 Startable myStartable = new MyStartable();
451 dpc.addComponent(Startable.class, myStartable);
452 try {
453 dpc.start();
454 fail("should have barfed");
455 } catch (RuntimeException e) {
456 assertEquals("foo", e.getMessage());
457 }
458 }
459
460 public static class FailingLifecycleStrategy implements LifecycleStrategy {
461 public void start(Object component) {
462 throw new RuntimeException("foo");
463 }
464
465 public void stop(Object component) {
466 }
467
468 public void dispose(Object component) {
469 }
470
471 public boolean hasLifecycle(Class type) {
472 return true;
473 }
474
475 }
476
477 public static class MyStartable implements Startable {
478 public MyStartable() {
479 }
480
481 public void start() {
482 }
483
484 public void stop() {
485 }
486 }
487
488 public static interface A {
489
490 }
491
492 public static class SimpleA implements A {
493
494 }
495
496 public static class WrappingA implements A {
497 private final A wrapped;
498
499 public WrappingA(A wrapped) {
500 this.wrapped = wrapped;
501 }
502 }
503
504 @Test public void testCanRegisterTwoComponentsImplementingSameInterfaceOneWithInterfaceAsKey()
505 throws Exception {
506 MutablePicoContainer container = createPicoContainer(null);
507
508 container.addComponent(SimpleA.class);
509 container.addComponent(A.class, WrappingA.class);
510
511 container.start();
512
513 assertEquals(WrappingA.class, container.getComponent(A.class)
514 .getClass());
515 }
516
517 @Test public void testCanRegisterTwoComponentsWithSameImplementionAndDifferentKey()
518 throws Exception {
519 MutablePicoContainer container = createPicoContainer(null);
520
521 container.addComponent(SimpleA.class);
522 container.addComponent("A", SimpleA.class);
523
524 container.start();
525
526 assertNotNull(container.getComponent("A"));
527 assertNotNull(container.getComponent(SimpleA.class));
528 assertNotSame(container.getComponent("A"), container
529 .getComponent(SimpleA.class));
530 }
531
532 @Test public void testPicoCanDifferentiateBetweenNamedStringsThatWouldOtherwiseBeAmbiguous() {
533 MutablePicoContainer mpc = createPicoContainer(null);
534 mpc.addComponent("greeting", "1");
535 mpc.addComponent("message", "2");
536 mpc.as(Characteristics.USE_NAMES).addComponent(
537 PicoCompositionException.class, PicoCompositionException.class);
538 assertEquals("2", mpc.getComponent(PicoCompositionException.class)
539 .getMessage());
540 }
541
542 @Test public void testPicoCanDifferentiateBetweenNamedObjectsThatWouldOtherwiseBeAmbiguous() {
543 MutablePicoContainer mpc = createPicoContainer(null);
544 Horse dobbin = new Horse();
545 Horse redRum = new Horse();
546 mpc.addComponent("dobbin", dobbin);
547 mpc.addComponent("horse", redRum);
548 mpc.as(Characteristics.USE_NAMES).addComponent(CdiTurtle.class);
549 assertEquals(redRum, mpc.getComponent(CdiTurtle.class).horse);
550 }
551
552 @Test public void testPicoCanDifferentiateBetweenNamedIntsThatWouldOtherwiseBeAmbiguous() {
553 MutablePicoContainer mpc = createPicoContainer(null);
554 mpc.addComponent("one", 1);
555 mpc.addComponent("two", 2);
556 mpc.as(Characteristics.USE_NAMES).addComponent(NeedsTwo.class);
557 assertEquals(2, mpc.getComponent(NeedsTwo.class).two);
558 }
559
560 public static class ListComponentsInStartClass implements Startable {
561 private MutablePicoContainer _pico;
562
563 public void start() {
564 List<SimpleTouchable> cps = _pico
565 .getComponents(SimpleTouchable.class);
566 assertNotNull(cps);
567 }
568
569 public void stop() {
570 }
571
572 }
573
574 /**
575 * JIRA: PICO-295 reported by Erik Putrycz
576 */
577 @Test public void testListComponentsInStart() {
578 DefaultPicoContainer dpc = new DefaultPicoContainer();
579 dpc.addComponent(SimpleTouchable.class);
580 ListComponentsInStartClass cl = new ListComponentsInStartClass();
581 cl._pico = dpc;
582 dpc.addComponent(cl);
583 dpc.start();
584 }
585
586 public static class NeedsTwo {
587 private final int two;
588
589 public NeedsTwo(Integer two) {
590 this.two = two;
591 }
592 }
593
594 public static class Horse {
595 }
596
597 public static class CdiTurtle {
598 public final Horse horse;
599
600 public CdiTurtle(Horse horse) {
601 this.horse = horse;
602 }
603 }
604
605 public static class SdiDonkey {
606 public Horse horse;
607
608 public void setHorse(Horse horse) {
609 this.horse = horse;
610 }
611 }
612
613 public static class SdiRabbit {
614 public Horse horse;
615
616 public void setHorse(Horse horse) {
617 this.horse = horse;
618 }
619 }
620
621 @Test public void testMixingOfSDIandCDI() {
622
623 MutablePicoContainer container = createPicoContainer(null).change(
624 Characteristics.CACHE);
625 container.addComponent(Horse.class);
626 container.change(SDI);
627 container.addComponent(SdiDonkey.class);
628 container.addComponent(SdiRabbit.class);
629 container.change(CDI);
630 container.addComponent(CdiTurtle.class);
631
632 SdiDonkey donkey = container.getComponent(SdiDonkey.class);
633 SdiRabbit rabbit = container.getComponent(SdiRabbit.class);
634 CdiTurtle turtle = container.getComponent(CdiTurtle.class);
635
636 assertions(donkey, rabbit, turtle);
637 }
638
639 @Test public void testMixingOfSDIandCDIDifferently() {
640
641 MutablePicoContainer container = createPicoContainer(null).change(
642 Characteristics.CACHE);
643 container.addComponent(Horse.class);
644 container.addComponent(CdiTurtle.class);
645 container.change(SDI);
646 container.addComponent(SdiDonkey.class);
647 container.addComponent(SdiRabbit.class);
648
649 SdiDonkey donkey = container.getComponent(SdiDonkey.class);
650 SdiRabbit rabbit = container.getComponent(SdiRabbit.class);
651 CdiTurtle turtle = container.getComponent(CdiTurtle.class);
652
653 assertions(donkey, rabbit, turtle);
654 }
655
656 @Test public void testMixingOfSDIandCDIInBuilderStyle() {
657
658 MutablePicoContainer container = createPicoContainer(null).change(
659 Characteristics.CACHE);
660 container.addComponent(Horse.class).change(SDI).addComponent(
661 SdiDonkey.class).addComponent(SdiRabbit.class).change(CDI)
662 .addComponent(CdiTurtle.class);
663
664 SdiDonkey donkey = container.getComponent(SdiDonkey.class);
665 SdiRabbit rabbit = container.getComponent(SdiRabbit.class);
666 CdiTurtle turtle = container.getComponent(CdiTurtle.class);
667
668 assertions(donkey, rabbit, turtle);
669 }
670
671 private void assertions(SdiDonkey donkey, SdiRabbit rabbit, CdiTurtle turtle) {
672 assertNotNull(rabbit);
673 assertNotNull(donkey);
674 assertNotNull(turtle);
675 assertNotNull(turtle.horse);
676 assertNotNull(donkey.horse);
677 assertNotNull(rabbit.horse);
678 assertSame(donkey.horse, turtle.horse);
679 assertSame(rabbit.horse, turtle.horse);
680 }
681
682 @Test public void testMixingOfSDIandCDIWithTemporaryCharacterizations() {
683
684 MutablePicoContainer container = createPicoContainer(null).change(
685 Characteristics.CACHE);
686 container.addComponent(Horse.class);
687 container.addComponent(CdiTurtle.class);
688 container.as(SDI).addComponent(SdiDonkey.class);
689 container.as(SDI).addComponent(SdiRabbit.class);
690
691 SdiDonkey donkey = container.getComponent(SdiDonkey.class);
692 SdiRabbit rabbit = container.getComponent(SdiRabbit.class);
693 CdiTurtle turtle = container.getComponent(CdiTurtle.class);
694
695 assertions(donkey, rabbit, turtle);
696 }
697
698 @Test public void testMixingOfSDIandCDIWithTemporaryCharacterizationsDifferently() {
699
700 MutablePicoContainer container = createPicoContainer(null).change(
701 Characteristics.CACHE);
702 container.as(SDI).addComponent(SdiDonkey.class);
703 container.as(SDI).addComponent(SdiRabbit.class);
704 container.addComponent(Horse.class);
705 container.addComponent(CdiTurtle.class);
706
707 SdiDonkey donkey = container.getComponent(SdiDonkey.class);
708 SdiRabbit rabbit = container.getComponent(SdiRabbit.class);
709 CdiTurtle turtle = container.getComponent(CdiTurtle.class);
710
711 assertions(donkey, rabbit, turtle);
712 }
713
714 @Test public void testNoComponentIsMonitoredAndPotentiallyLateProvided() {
715 final String[] missingKey = new String[1];
716
717 String foo = (String) new DefaultPicoContainer(
718 new NullComponentMonitor() {
719 public Object noComponentFound(
720 MutablePicoContainer container, Object componentKey) {
721 missingKey[0] = (String) componentKey;
722 return "foo";
723 }
724 }).getComponent("missingKey");
725
726 assertNotNull(missingKey[0]);
727 assertEquals("missingKey", missingKey[0]);
728 assertEquals("foo", foo);
729
730 }
731
732 @Test public void testThatComponentCannotBeRemovedFromStartedContainer() {
733 MutablePicoContainer container = createPicoContainer(null);
734 container.addComponent(Map.class, HashMap.class);
735 container.start();
736 try {
737 container.removeComponent(Map.class);
738 fail("should have barfed");
739 } catch (PicoCompositionException e) {
740 }
741 }
742
743 @Test public void testThatSimpleStringComponentIsAddedOnlyOnce() {
744 MutablePicoContainer container = createPicoContainer(null);
745 container.addComponent("foo bar");
746 assertEquals(1, container.getComponentAdapters().size());
747 }
748
749 @Test public void canInterceptImplementationViaNewInjectionFactoryMethodOnMonitor() {
750 DefaultPicoContainer dpc = new DefaultPicoContainer(new MyNullComponentMonitor());
751 dpc.addComponent(Collection.class, HashSet.class);
752 dpc.addComponent(List.class, ArrayList.class);
753 assertNotNull(dpc.getComponent(List.class));
754 assertEquals("doppleganger", dpc.getComponent(List.class).get(0));
755 }
756
757 private static class MyNullComponentMonitor extends NullComponentMonitor {
758 public AbstractInjector newInjectionFactory(AbstractInjector abstractInjector) {
759 if (abstractInjector.getComponentKey() == List.class) {
760 return new AbstractInjector(List.class, ArrayList.class, Parameter.DEFAULT, MyNullComponentMonitor.this, null, false) {
761 public Object getComponentInstance(PicoContainer container) throws PicoCompositionException {
762 return getComponentInstance(container, null);
763 }
764
765 public Object getComponentInstance(PicoContainer container, Type into) throws PicoCompositionException {
766 ArrayList list = new ArrayList();
767 list.add("doppleganger");
768 return list;
769 }
770
771 public void decorateComponentInstance(PicoContainer container, Type into, Object instance) {
772 }
773 };
774 } else {
775 return abstractInjector;
776 }
777 }
778 }
779
780 public static interface Swede {
781 }
782 public static class Turnip2 extends Turnip {
783 public Turnip2(String foo, Swede swede) {
784 super(foo);
785 assertNotNull(swede);
786 super.swede = swede;
787 }
788 }
789 public static class Turnip {
790 @Inject
791 Swede swede;
792 private final String foo;
793
794 public Turnip(String foo) {
795 this.foo = foo;
796 }
797
798 public Swede getSwede() {
799 return swede;
800 }
801
802 public String getFoo() {
803 return foo;
804 }
805 }
806
807 @Test public void testThatComponentCanHaveAProvidedDependency() {
808 MutablePicoContainer container = new DefaultPicoContainer(new MultiInjection());
809 container.addComponent(String.class, "foo");
810 container.addComponent(Turnip.class);
811 container.addAdapter(new SwedeFactoryInjector());
812 Turnip t = container.getComponent(Turnip.class);
813 assertNotNull(t);
814 assertEquals("Swede for " + Turnip.class.getName(), t.getSwede().toString());
815 assertEquals("foo", t.getFoo());
816
817 }
818
819 @Test public void testThatComponentCanHaveAProvidedDependencyViaConstructor() {
820 MutablePicoContainer container = new DefaultPicoContainer();
821 container.addComponent(String.class, "foo");
822 container.addComponent(Turnip2.class);
823 container.addAdapter(new SwedeFactoryInjector());
824 Turnip2 t = container.getComponent(Turnip2.class);
825 assertNotNull(t);
826 assertEquals("Swede for " + Turnip2.class.getName(), t.getSwede().toString());
827 assertEquals("foo", t.getFoo());
828
829 }
830
831 @Test
832 public void testThatComponentCanHaveAProvidedDependencyViaDecoratorBehavior() {
833 MutablePicoContainer container = new DefaultPicoContainer(new SwedeDecorating().wrap(new ConstructorInjection()));
834 container.addComponent(String.class, "foo");
835 container.addComponent(Turnip.class);
836 Turnip t = container.getComponent(Turnip.class);
837 assertNotNull(t);
838 assertNotNull(t.getSwede());
839 assertEquals("Swede:" + Turnip.class.getName(), t.getSwede().toString());
840 assertEquals("foo", t.getFoo());
841
842 }
843
844 @Test
845 public void testThatComponentCanHaveAProvidedDependencyViaFieldDecoratorBehavior() {
846 MutablePicoContainer container = new DefaultPicoContainer(
847 new FieldDecorating(Swede.class) {
848 public Object decorate(final Object instance) {
849 return new Swede() {
850 public String toString() {
851 return "Swede:" + instance.getClass().getName();
852 }
853 };
854 }
855 }.wrap(new ConstructorInjection()));
856 container.addComponent(String.class, "foo");
857 container.addComponent(Turnip.class);
858 Turnip t = container.getComponent(Turnip.class);
859 assertNotNull(t);
860 assertNotNull(t.getSwede());
861 assertEquals("Swede:" + Turnip.class.getName(), t.getSwede().toString());
862 assertEquals("foo", t.getFoo());
863
864 }
865
866 private static class SwedeDecorating extends Decorating {
867 public void decorate(final Object instance) {
868 Field[] fields = instance.getClass().getDeclaredFields();
869 for (int i = 0; i < fields.length; i++) {
870 Field field = fields[i];
871 if (field.getType() == Swede.class) {
872 Swede value = new Swede() {
873 public String toString() {
874 return "Swede:" + instance.getClass().getName();
875 }
876 };
877 field.setAccessible(true);
878 try {
879 field.set(instance, value);
880 } catch (IllegalAccessException e) {
881 throw new RuntimeException(e);
882 }
883 }
884
885 }
886 }
887 }
888
889 private static class SwedeFactoryInjector extends FactoryInjector<Swede> {
890
891 public Swede getComponentInstance(PicoContainer container, final Type into) throws PicoCompositionException {
892 // Mauro: you can do anything in here by way of startegy for injecting a specific logger :-)
893 return new Swede() {
894 public String toString() {
895 return "Swede for " + ((Class) into).getName();
896 }
897 };
898 }
899 }
900 }