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.reflect.Field;
024    import java.lang.reflect.Member;
025    import java.lang.reflect.Type;
026    import java.util.ArrayList;
027    import java.util.Collection;
028    import java.util.HashMap;
029    import java.util.HashSet;
030    import java.util.LinkedList;
031    import java.util.List;
032    import java.util.Map;
033    import java.util.Properties;
034    
035    import org.junit.Test;
036    import org.picocontainer.annotations.Inject;
037    import org.picocontainer.behaviors.Caching;
038    import org.picocontainer.behaviors.Decorating;
039    import org.picocontainer.behaviors.FieldDecorating;
040    import org.picocontainer.containers.EmptyPicoContainer;
041    import org.picocontainer.injectors.AbstractInjector;
042    import org.picocontainer.injectors.ConstructorInjection;
043    import org.picocontainer.injectors.ConstructorInjector;
044    import org.picocontainer.injectors.FactoryInjector;
045    import org.picocontainer.injectors.MultiInjection;
046    import org.picocontainer.lifecycle.NullLifecycleStrategy;
047    import org.picocontainer.monitors.NullComponentMonitor;
048    import org.picocontainer.monitors.WriterComponentMonitor;
049    import org.picocontainer.tck.AbstractPicoContainerTest;
050    import org.picocontainer.testmodel.DecoratedTouchable;
051    import org.picocontainer.testmodel.DependsOnTouchable;
052    import org.picocontainer.testmodel.SimpleTouchable;
053    import org.picocontainer.testmodel.Touchable;
054    
055    /**
056     * @author Aslak Helles&oslashp;y
057     * @author Paul Hammant
058     * @author Ward Cunningham
059     * @author Mauro Talevi
060     */
061    @SuppressWarnings("serial")
062    public final class DefaultPicoContainerTestCase extends AbstractPicoContainerTest {
063    
064            protected MutablePicoContainer createPicoContainer(PicoContainer parent) {
065                    return new DefaultPicoContainer(parent);
066            }
067    
068            protected Properties[] getProperties() {
069                    return new Properties[0];
070            }
071    
072            @Test public void testInstantiationWithNullComponentFactory() {
073                    try {
074                            new DefaultPicoContainer((ComponentFactory) null, null);
075                            fail("NPE expected");
076                    } catch (NullPointerException e) {
077                            // expected
078                    }
079            }
080    
081            @Test public void testUpDownDependenciesCannotBeFollowed() {
082                    MutablePicoContainer parent = createPicoContainer(null);
083                    MutablePicoContainer child = createPicoContainer(parent);
084    
085                    // ComponentF -> ComponentA -> ComponentB+C
086                    child.addComponent(ComponentF.class);
087                    parent.addComponent(ComponentA.class);
088                    child.addComponent(ComponentB.class);
089                    child.addComponent(ComponentC.class);
090    
091                    try {
092                            child.getComponent(ComponentF.class);
093                            fail("Thrown "
094                                            + AbstractInjector.UnsatisfiableDependenciesException.class
095                                                            .getName() + " expected");
096                    } catch (final AbstractInjector.UnsatisfiableDependenciesException e) {
097                            assertEquals(ComponentB.class, e.getUnsatisfiedDependencyType());
098                    }
099    
100        }
101    
102            @Test public void testComponentsCanBeRemovedByInstance() {
103                    MutablePicoContainer pico = createPicoContainer(null);
104                    pico.addComponent(HashMap.class);
105                    pico.addComponent(ArrayList.class);
106                    List list = pico.getComponent(List.class);
107                    pico.removeComponentByInstance(list);
108                    assertEquals(1, pico.getComponentAdapters().size());
109                    assertEquals(1, pico.getComponents().size());
110                    assertEquals(HashMap.class, pico.getComponent(Serializable.class)
111                                    .getClass());
112            }
113    
114            @Test public void testComponentInstancesListIsReturnedForNullType() {
115                    MutablePicoContainer pico = createPicoContainer(null);
116                    List componentInstances = pico.getComponents(null);
117                    assertNotNull(componentInstances);
118                    assertEquals(0, componentInstances.size());
119            }
120    
121            @Test public void testComponentsWithCommonSupertypeWhichIsAConstructorArgumentCanBeLookedUpByConcreteType() {
122                    MutablePicoContainer pico = createPicoContainer(null);
123                    pico.addComponent(LinkedList.class, LinkedList.class, Parameter.ZERO);
124                    pico.addComponent(ArrayList.class);
125                    assertEquals(ArrayList.class, pico
126                                    .getComponent((Class) ArrayList.class).getClass());
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(),
198                                                    new NullLifecycleStrategy(), false));
199                    TransientComponent c1 = picoContainer
200                                    .getComponent(TransientComponent.class);
201                    TransientComponent c2 = picoContainer
202                                    .getComponent(TransientComponent.class);
203                    assertNotSame(c1, c2);
204                    assertSame(c1.service, c2.service);
205            }
206    
207            public static class DependsOnCollection {
208                    public DependsOnCollection(Collection c) {
209                    }
210            }
211    
212            @Test public void testShouldProvideInfoAboutDependingWhenAmbiguityHappens() {
213                    MutablePicoContainer pico = this.createPicoContainer(null);
214                    pico.addComponent(new ArrayList());
215                    pico.addComponent(new LinkedList());
216                    pico.addComponent(DependsOnCollection.class);
217                    try {
218                            pico.getComponent(DependsOnCollection.class);
219                            fail();
220                    } catch (AbstractInjector.AmbiguousComponentResolutionException expected) {
221                            String doc = DependsOnCollection.class.getName();
222                            assertEquals(
223                                            "class "
224                                                            + doc
225                                                            + " 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",
226                                            expected.getMessage());
227                    }
228            }
229    
230            @Test public void testInstantiationWithMonitorAndParent() {
231                    StringWriter writer = new StringWriter();
232                    ComponentMonitor monitor = new WriterComponentMonitor(writer);
233                    DefaultPicoContainer parent = new DefaultPicoContainer();
234                    DefaultPicoContainer child = new DefaultPicoContainer(monitor, parent);
235                    parent.addComponent("st", SimpleTouchable.class);
236                    child.addComponent("dot", DependsOnTouchable.class);
237                    DependsOnTouchable dot = (DependsOnTouchable) child.getComponent("dot");
238                    assertNotNull(dot);
239                    assertTrue("writer not empty", writer.toString().length() > 0);
240    
241        }
242    
243        @Test
244        public void testRepresentationOfContainerTree() {
245            StringWriter writer = new StringWriter();
246            DefaultPicoContainer parent = new DefaultPicoContainer();
247            parent.setName("parent");
248            DefaultPicoContainer child = new DefaultPicoContainer(parent);
249            child.setName("child");
250            parent.addComponent("st", SimpleTouchable.class);
251            child.addComponent("dot", DependsOnTouchable.class);
252            assertEquals("child:1<I<parent:1<|", child.toString());
253        }
254    
255        @SuppressWarnings("serial")
256            @Test public void testStartCapturedByMonitor() {
257                    final StringBuffer sb = new StringBuffer();
258                    DefaultPicoContainer dpc = new DefaultPicoContainer(
259                                    new NullComponentMonitor() {
260                                            public void invoking(PicoContainer container,
261                                                            ComponentAdapter componentAdapter, Member member,
262                                                            Object instance) {
263                                                    sb.append(member.toString());
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 componentAdapterType) {
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 testCanUseCustomLifecycleStrategyForClassRegistrations() {
460                    DefaultPicoContainer dpc = new DefaultPicoContainer(
461                                    new FailingLifecycleStrategy(), null);
462                    dpc.as(Characteristics.CACHE).addComponent(Startable.class,
463                                    MyStartable.class);
464                    try {
465                            dpc.start();
466                            fail("should have barfed");
467                    } catch (RuntimeException e) {
468                            assertEquals("foo", e.getMessage());
469                    }
470            }
471    
472            @Test public void testCanUseCustomLifecycleStrategyForInstanceRegistrations() {
473                    DefaultPicoContainer dpc = new DefaultPicoContainer(
474                                    new FailingLifecycleStrategy(), null);
475                    Startable myStartable = new MyStartable();
476                    dpc.addComponent(Startable.class, myStartable);
477                    try {
478                            dpc.start();
479                            fail("should have barfed");
480                    } catch (RuntimeException e) {
481                            assertEquals("foo", e.getMessage());
482                    }
483            }
484    
485            public static class FailingLifecycleStrategy implements LifecycleStrategy {
486                    public void start(Object component) {
487                            throw new RuntimeException("foo");
488                    }
489    
490                    public void stop(Object component) {
491                    }
492    
493                    public void dispose(Object component) {
494                    }
495    
496                    public boolean hasLifecycle(Class type) {
497                            return true;
498                    }
499    
500            }
501    
502            public static class MyStartable implements Startable {
503                    public MyStartable() {
504                    }
505    
506                    public void start() {
507                    }
508    
509                    public void stop() {
510                    }
511            }
512    
513            public static interface A {
514    
515            }
516    
517            public static class SimpleA implements A {
518    
519            }
520    
521            public static class WrappingA implements A {
522                    private final A wrapped;
523    
524                    public WrappingA(A wrapped) {
525                            this.wrapped = wrapped;
526                    }
527            }
528    
529            @Test public void testCanRegisterTwoComponentsImplementingSameInterfaceOneWithInterfaceAsKey()
530                            throws Exception {
531                    MutablePicoContainer container = createPicoContainer(null);
532    
533                    container.addComponent(SimpleA.class);
534                    container.addComponent(A.class, WrappingA.class);
535    
536                    container.start();
537    
538                    assertEquals(WrappingA.class, container.getComponent(A.class)
539                                    .getClass());
540            }
541    
542            @Test public void testCanRegisterTwoComponentsWithSameImplementionAndDifferentKey()
543                            throws Exception {
544                    MutablePicoContainer container = createPicoContainer(null);
545    
546                    container.addComponent(SimpleA.class);
547                    container.addComponent("A", SimpleA.class);
548    
549                    container.start();
550    
551                    assertNotNull(container.getComponent("A"));
552                    assertNotNull(container.getComponent(SimpleA.class));
553                    assertNotSame(container.getComponent("A"), container
554                                    .getComponent(SimpleA.class));
555            }
556    
557            @Test public void testPicoCanDifferentiateBetweenNamedStringsThatWouldOtherwiseBeAmbiguous() {
558                    MutablePicoContainer mpc = createPicoContainer(null);
559                    mpc.addComponent("greeting", "1");
560                    mpc.addComponent("message", "2");
561                    mpc.as(Characteristics.USE_NAMES).addComponent(
562                                    PicoCompositionException.class, PicoCompositionException.class);
563                    assertEquals("2", mpc.getComponent(PicoCompositionException.class)
564                                    .getMessage());
565            }
566    
567            @Test public void testPicoCanDifferentiateBetweenNamedObjectsThatWouldOtherwiseBeAmbiguous() {
568                    MutablePicoContainer mpc = createPicoContainer(null);
569                    Horse dobbin = new Horse();
570                    Horse redRum = new Horse();
571                    mpc.addComponent("dobbin", dobbin);
572                    mpc.addComponent("horse", redRum);
573                    mpc.as(Characteristics.USE_NAMES).addComponent(CdiTurtle.class);
574                    assertEquals(redRum, mpc.getComponent(CdiTurtle.class).horse);
575            }
576    
577            @Test public void testPicoCanDifferentiateBetweenNamedIntsThatWouldOtherwiseBeAmbiguous() {
578                    MutablePicoContainer mpc = createPicoContainer(null);
579                    mpc.addComponent("one", 1);
580                    mpc.addComponent("two", 2);
581                    mpc.as(Characteristics.USE_NAMES).addComponent(NeedsTwo.class);
582                    assertEquals(2, mpc.getComponent(NeedsTwo.class).two);
583            }
584    
585            public static class ListComponentsInStartClass implements Startable {
586                    private MutablePicoContainer _pico;
587    
588                    public void start() {
589                            List<SimpleTouchable> cps = _pico
590                                            .getComponents(SimpleTouchable.class);
591                            assertNotNull(cps);
592                    }
593    
594                    public void stop() {
595                    }
596    
597            }
598    
599            /**
600             * JIRA: PICO-295 reported by Erik Putrycz
601             */
602            @Test public void testListComponentsInStart() {
603                    DefaultPicoContainer dpc = new DefaultPicoContainer();
604                    dpc.addComponent(SimpleTouchable.class);
605                    ListComponentsInStartClass cl = new ListComponentsInStartClass();
606                    cl._pico = dpc;
607                    dpc.addComponent(cl);
608                    dpc.start();
609            }
610    
611            public static class NeedsTwo {
612                    private final int two;
613    
614                    public NeedsTwo(Integer two) {
615                            this.two = two;
616                    }
617            }
618    
619            public static class Horse {
620            }
621    
622            public static class CdiTurtle {
623                    public final Horse horse;
624    
625                    public CdiTurtle(Horse horse) {
626                            this.horse = horse;
627                    }
628            }
629    
630            public static class SdiDonkey {
631                    public Horse horse;
632    
633                    public void setHorse(Horse horse) {
634                            this.horse = horse;
635                    }
636            }
637    
638            public static class SdiRabbit {
639                    public Horse horse;
640    
641                    public void setHorse(Horse horse) {
642                            this.horse = horse;
643                    }
644            }
645    
646            @Test public void testMixingOfSDIandCDI() {
647    
648                    MutablePicoContainer container = createPicoContainer(null).change(
649                                    Characteristics.CACHE);
650                    container.addComponent(Horse.class);
651                    container.change(SDI);
652                    container.addComponent(SdiDonkey.class);
653                    container.addComponent(SdiRabbit.class);
654                    container.change(CDI);
655                    container.addComponent(CdiTurtle.class);
656    
657                    SdiDonkey donkey = container.getComponent(SdiDonkey.class);
658                    SdiRabbit rabbit = container.getComponent(SdiRabbit.class);
659                    CdiTurtle turtle = container.getComponent(CdiTurtle.class);
660    
661                    assertions(donkey, rabbit, turtle);
662            }
663    
664            @Test public void testMixingOfSDIandCDIDifferently() {
665    
666                    MutablePicoContainer container = createPicoContainer(null).change(
667                                    Characteristics.CACHE);
668                    container.addComponent(Horse.class);
669                    container.addComponent(CdiTurtle.class);
670                    container.change(SDI);
671                    container.addComponent(SdiDonkey.class);
672                    container.addComponent(SdiRabbit.class);
673    
674                    SdiDonkey donkey = container.getComponent(SdiDonkey.class);
675                    SdiRabbit rabbit = container.getComponent(SdiRabbit.class);
676                    CdiTurtle turtle = container.getComponent(CdiTurtle.class);
677    
678                    assertions(donkey, rabbit, turtle);
679            }
680    
681            @Test public void testMixingOfSDIandCDIInBuilderStyle() {
682    
683                    MutablePicoContainer container = createPicoContainer(null).change(
684                                    Characteristics.CACHE);
685                    container.addComponent(Horse.class).change(SDI).addComponent(
686                                    SdiDonkey.class).addComponent(SdiRabbit.class).change(CDI)
687                                    .addComponent(CdiTurtle.class);
688    
689                    SdiDonkey donkey = container.getComponent(SdiDonkey.class);
690                    SdiRabbit rabbit = container.getComponent(SdiRabbit.class);
691                    CdiTurtle turtle = container.getComponent(CdiTurtle.class);
692    
693                    assertions(donkey, rabbit, turtle);
694            }
695    
696            private void assertions(SdiDonkey donkey, SdiRabbit rabbit, CdiTurtle turtle) {
697                    assertNotNull(rabbit);
698                    assertNotNull(donkey);
699                    assertNotNull(turtle);
700                    assertNotNull(turtle.horse);
701                    assertNotNull(donkey.horse);
702                    assertNotNull(rabbit.horse);
703                    assertSame(donkey.horse, turtle.horse);
704                    assertSame(rabbit.horse, turtle.horse);
705            }
706    
707            @Test public void testMixingOfSDIandCDIWithTemporaryCharacterizations() {
708    
709                    MutablePicoContainer container = createPicoContainer(null).change(
710                                    Characteristics.CACHE);
711                    container.addComponent(Horse.class);
712                    container.addComponent(CdiTurtle.class);
713                    container.as(SDI).addComponent(SdiDonkey.class);
714                    container.as(SDI).addComponent(SdiRabbit.class);
715    
716                    SdiDonkey donkey = container.getComponent(SdiDonkey.class);
717                    SdiRabbit rabbit = container.getComponent(SdiRabbit.class);
718                    CdiTurtle turtle = container.getComponent(CdiTurtle.class);
719    
720                    assertions(donkey, rabbit, turtle);
721            }
722    
723            @Test public void testMixingOfSDIandCDIWithTemporaryCharacterizationsDifferently() {
724    
725                    MutablePicoContainer container = createPicoContainer(null).change(
726                                    Characteristics.CACHE);
727                    container.as(SDI).addComponent(SdiDonkey.class);
728                    container.as(SDI).addComponent(SdiRabbit.class);
729                    container.addComponent(Horse.class);
730                    container.addComponent(CdiTurtle.class);
731    
732                    SdiDonkey donkey = container.getComponent(SdiDonkey.class);
733                    SdiRabbit rabbit = container.getComponent(SdiRabbit.class);
734                    CdiTurtle turtle = container.getComponent(CdiTurtle.class);
735    
736                    assertions(donkey, rabbit, turtle);
737            }
738    
739            @SuppressWarnings("serial")
740            @Test public void testNoComponentIsMonitoredAndPotentiallyLateProvided() {
741                    final String[] missingKey = new String[1];
742    
743                    String foo = (String) new DefaultPicoContainer(
744                                    new NullComponentMonitor() {
745                                            public Object noComponentFound(
746                                                            MutablePicoContainer container, Object componentKey) {
747                                                    missingKey[0] = (String) componentKey;
748                                                    return "foo";
749                                            }
750                                    }).getComponent("missingKey");
751    
752                    assertNotNull(missingKey[0]);
753                    assertEquals("missingKey", missingKey[0]);
754                    assertEquals("foo", foo);
755    
756            }
757    
758            @Test public void testThatComponentCannotBeRemovedFromStartedContainer() {
759                    MutablePicoContainer container = createPicoContainer(null);
760                    container.addComponent(Map.class, HashMap.class);
761                    container.start();
762                    try {
763                            container.removeComponent(Map.class);
764                            fail("should have barfed");
765                    } catch (PicoCompositionException e) {
766                    }
767            }
768    
769            @Test public void testThatSimpleStringComponentIsAddedOnlyOnce() {
770                    MutablePicoContainer container = createPicoContainer(null);
771                    container.addComponent("foo bar");
772                    assertEquals(1, container.getComponentAdapters().size());
773            }
774    
775        @Test public void canInterceptImplementationViaNewInjectionFactoryMethodOnMonitor() {
776            DefaultPicoContainer dpc = new DefaultPicoContainer(new MyNullComponentMonitor());
777            dpc.addComponent(Collection.class, HashSet.class);
778            dpc.addComponent(List.class, ArrayList.class);
779            assertNotNull(dpc.getComponent(List.class));
780            assertEquals("doppleganger", dpc.getComponent(List.class).get(0));
781        }
782    
783        @SuppressWarnings("serial")
784        private static class MyNullComponentMonitor extends NullComponentMonitor {
785            public AbstractInjector newInjectionFactory(AbstractInjector abstractInjector) {
786                if (abstractInjector.getComponentKey() == List.class) {
787                    return new AbstractInjector(List.class, ArrayList.class, Parameter.DEFAULT, MyNullComponentMonitor.this, null, false) {
788                        public Object getComponentInstance(PicoContainer container) throws PicoCompositionException {
789                            return getComponentInstance(container, null);
790                        }
791    
792                        public Object getComponentInstance(PicoContainer container, Type into) throws PicoCompositionException {
793                            ArrayList list = new ArrayList();
794                            list.add("doppleganger");
795                            return list;
796                        }
797    
798                        public void decorateComponentInstance(PicoContainer container, Type into, Object instance) {
799                        }
800                    };
801                } else {
802                    return abstractInjector;
803                }
804            }
805        }
806    
807        public static interface Swede {
808        }
809        public static class Turnip2 extends Turnip {
810            public Turnip2(String foo, Swede swede) {
811                super(foo);
812                assertNotNull(swede);
813                super.swede = swede;
814            }
815        }
816        public static class Turnip {
817            @Inject
818            Swede swede;
819            private final String foo;
820    
821            public Turnip(String foo) {
822                this.foo = foo;
823            }
824    
825            public Swede getSwede() {
826                return swede;
827            }
828    
829            public String getFoo() {
830                return foo;
831            }
832        }
833    
834        @Test public void testThatComponentCanHaveAProvidedDependency() {
835            MutablePicoContainer container = new DefaultPicoContainer(new MultiInjection());
836            container.addComponent(String.class, "foo");
837            container.addComponent(Turnip.class);
838            container.addAdapter(new SwedeFactoryInjector());
839            Turnip t = container.getComponent(Turnip.class);
840            assertNotNull(t);
841            assertEquals("Swede for " + Turnip.class.getName(), t.getSwede().toString());
842            assertEquals("foo", t.getFoo());
843    
844        }
845    
846        @Test public void testThatComponentCanHaveAProvidedDependencyViaConstructor() {
847            MutablePicoContainer container = new DefaultPicoContainer();
848            container.addComponent(String.class, "foo");
849            container.addComponent(Turnip2.class);
850            container.addAdapter(new SwedeFactoryInjector());
851            Turnip2 t = container.getComponent(Turnip2.class);
852            assertNotNull(t);
853            assertEquals("Swede for " + Turnip2.class.getName(), t.getSwede().toString());
854            assertEquals("foo", t.getFoo());
855    
856        }
857    
858        @Test public void testThatComponentCanHaveAProvidedDependencyViaConstructorADifferentWay() {
859            MutablePicoContainer container = new DefaultPicoContainer();
860            container.addComponent(String.class, "foo");
861            container.addComponent(Turnip2.class);
862            container.addAdapter(new Swede2FactoryInjector()); // this injector defines Swede2 as key in its ctor
863            Turnip2 t = container.getComponent(Turnip2.class);
864            assertNotNull(t);
865            assertEquals("Swede for " + Turnip2.class.getName(), t.getSwede().toString());
866            assertEquals("foo", t.getFoo());
867    
868        }
869    
870        @Test
871        public void testThatComponentCanHaveAProvidedDependencyViaDecoratorBehavior() {
872            MutablePicoContainer container = new DefaultPicoContainer(new SwedeDecorating().wrap(new ConstructorInjection()));
873            container.addComponent(String.class, "foo");
874            container.addComponent(Turnip.class);
875            Turnip t = container.getComponent(Turnip.class);
876            assertNotNull(t);
877            assertNotNull(t.getSwede());
878            assertEquals("Swede:" + Turnip.class.getName(), t.getSwede().toString());
879            assertEquals("foo", t.getFoo());
880    
881        }
882    
883        @Test
884        public void testThatComponentCanHaveAProvidedDependencyViaFieldDecoratorBehavior() {
885            MutablePicoContainer container = new DefaultPicoContainer(
886                    new FieldDecorating(Swede.class) {
887                        public Object decorate(final Object instance) {
888                            return new Swede() {
889                                public String toString() {
890                                    return "Swede:" + instance.getClass().getName();
891                                }
892                            };
893                        }
894                    }.wrap(new ConstructorInjection()));
895            container.addComponent(String.class, "foo");
896            container.addComponent(Turnip.class);
897            Turnip t = container.getComponent(Turnip.class);
898            assertNotNull(t);
899            assertNotNull(t.getSwede());
900            assertEquals("Swede:" + Turnip.class.getName(), t.getSwede().toString());
901            assertEquals("foo", t.getFoo());
902    
903        }
904    
905        private static class SwedeDecorating extends Decorating {
906            public void decorate(final Object instance) {
907                Field[] fields = instance.getClass().getDeclaredFields();
908                for (int i = 0; i < fields.length; i++) {
909                    Field field = fields[i];
910                    if (field.getType() == Swede.class) {
911                        Swede value = new Swede() {
912                            public String toString() {
913                                return "Swede:" + instance.getClass().getName();
914                            }
915                        };
916                        field.setAccessible(true);
917                        try {
918                            field.set(instance, value);
919                        } catch (IllegalAccessException e) {
920                            throw new RuntimeException(e);
921                        }
922                    }
923    
924                }
925            }
926        }
927    
928        private static class SwedeFactoryInjector extends FactoryInjector<Swede> {
929            public Swede getComponentInstance(PicoContainer container, final Type into) throws PicoCompositionException {
930                // Mauro: you can do anything in here by way of startegy for injecting a specific logger :-)
931                return new Swede() {
932                    public String toString() {
933                        return "Swede for " + ((Class) into).getName();
934                    }
935                };
936            }
937        }
938    
939        private static class Swede2FactoryInjector extends FactoryInjector {
940            private Swede2FactoryInjector() {
941                super(Swede.class);
942            }
943            public Swede getComponentInstance(PicoContainer container, final Type into) throws PicoCompositionException {
944                // Mauro: you can do anything in here by way of startegy for injecting a specific logger :-)
945                return new Swede() {
946                    public String toString() {
947                        return "Swede for " + ((Class) into).getName();
948                    }
949                };
950            }
951        }
952    
953        private abstract class Footle<T> {
954            private class ServiceConnectionInjector extends FactoryInjector<T> {
955                public T getComponentInstance(PicoContainer container, Type into) {
956                    System.out.println("**** injector called for " + into);
957                    return null;
958                }
959            }
960            private void addAdapter(MutablePicoContainer mpc) {
961                mpc.addAdapter(new ServiceConnectionInjector());
962            }
963        }
964    
965        public static interface Tree {
966            String leafColor();
967        }
968        public static class OakTree implements Tree {
969            private String leafColor;
970    
971            public OakTree(String leafColor) {
972                this.leafColor = leafColor;
973            }
974    
975            public String leafColor() {
976                return leafColor;
977            }
978        }
979    
980        @Test public void ensureSophistcatedFactorInjectorCaseIsPossible() {
981    
982            DefaultPicoContainer pico = new DefaultPicoContainer();
983            pico.addConfig("leafColor", "green");
984            pico.addComponent(Tree.class, OakTree.class);
985    
986            Footle<Map> ft = new Footle<Map>(){};
987    
988            ft.addAdapter(pico);
989    
990            Tree tree = pico.getComponent(Tree.class);
991        }
992    
993    }