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