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