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