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