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