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