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