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    }