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     * Original code by                                                          *
009     *****************************************************************************/
010    package org.picocontainer.tck;
011    
012    import java.io.ByteArrayInputStream;
013    import java.io.ByteArrayOutputStream;
014    import java.io.IOException;
015    import java.io.ObjectInputStream;
016    import java.io.ObjectOutputStream;
017    import java.io.Serializable;
018    import java.util.ArrayList;
019    import java.util.Arrays;
020    import java.util.Collection;
021    import java.util.HashMap;
022    import java.util.HashSet;
023    import java.util.LinkedList;
024    import java.util.List;
025    import java.util.Map;
026    import java.util.Set;
027    
028    import junit.framework.Assert;
029    
030    import org.picocontainer.ComponentAdapter;
031    import org.picocontainer.Disposable;
032    import org.picocontainer.LifecycleManager;
033    import org.picocontainer.MutablePicoContainer;
034    import org.picocontainer.Parameter;
035    import org.picocontainer.PicoContainer;
036    import org.picocontainer.PicoException;
037    import org.picocontainer.PicoInitializationException;
038    import org.picocontainer.PicoIntrospectionException;
039    import org.picocontainer.PicoRegistrationException;
040    import org.picocontainer.PicoVerificationException;
041    import org.picocontainer.PicoVisitor;
042    import org.picocontainer.Startable;
043    import org.picocontainer.defaults.AbstractPicoVisitor;
044    import org.picocontainer.defaults.AmbiguousComponentResolutionException;
045    import org.picocontainer.defaults.AssignabilityRegistrationException;
046    import org.picocontainer.defaults.BasicComponentParameter;
047    import org.picocontainer.defaults.ConstantParameter;
048    import org.picocontainer.defaults.ConstructorInjectionComponentAdapter;
049    import org.picocontainer.defaults.CyclicDependencyException;
050    import org.picocontainer.defaults.DefaultPicoContainer;
051    import org.picocontainer.defaults.DuplicateComponentKeyRegistrationException;
052    import org.picocontainer.defaults.InstanceComponentAdapter;
053    import org.picocontainer.defaults.NotConcreteRegistrationException;
054    import org.picocontainer.defaults.UnsatisfiableDependenciesException;
055    import org.picocontainer.defaults.VerifyingVisitor;
056    import org.picocontainer.testmodel.DependsOnTouchable;
057    import org.picocontainer.testmodel.SimpleTouchable;
058    import org.picocontainer.testmodel.Touchable;
059    import org.picocontainer.testmodel.Washable;
060    import org.picocontainer.testmodel.WashableTouchable;
061    
062    import org.jmock.MockObjectTestCase;
063    
064    /**
065     * This test tests (at least it should) all the methods in MutablePicoContainer.
066     */
067    public abstract class AbstractPicoContainerTestCase extends MockObjectTestCase {
068    
069        protected abstract MutablePicoContainer createPicoContainer(PicoContainer parent);
070    
071        protected final MutablePicoContainer createPicoContainerWithDependsOnTouchableOnly() throws
072                PicoRegistrationException, PicoIntrospectionException {
073            MutablePicoContainer pico = createPicoContainer(null);
074            pico.registerComponentImplementation(DependsOnTouchable.class);
075            return pico;
076    
077        }
078    
079        protected final MutablePicoContainer createPicoContainerWithTouchableAndDependsOnTouchable() throws
080                PicoRegistrationException, PicoIntrospectionException {
081            MutablePicoContainer pico = createPicoContainerWithDependsOnTouchableOnly();
082            pico.registerComponentImplementation(Touchable.class, SimpleTouchable.class);
083            return pico;
084        }
085    
086        public void testBasicInstantiationAndContainment() throws PicoException, PicoRegistrationException {
087            PicoContainer pico = createPicoContainerWithTouchableAndDependsOnTouchable();    
088            assertTrue("Component should be instance of Touchable", Touchable.class.isAssignableFrom(pico.getComponentAdapterOfType(Touchable.class).getComponentImplementation()));
089        }
090    
091        public void testRegisteredComponentsExistAndAreTheCorrectTypes() throws PicoException, PicoRegistrationException {
092            PicoContainer pico = createPicoContainerWithTouchableAndDependsOnTouchable();
093            assertNotNull("Container should have Touchable component",
094                    pico.getComponentAdapter(Touchable.class));
095            assertNotNull("Container should have DependsOnTouchable component",
096                    pico.getComponentAdapter(DependsOnTouchable.class));
097            assertTrue("Component should be instance of Touchable",
098                    pico.getComponentInstance(Touchable.class) instanceof Touchable);
099            assertTrue("Component should be instance of DependsOnTouchable",
100                    pico.getComponentInstance(DependsOnTouchable.class) instanceof DependsOnTouchable);
101            assertNull("should not have non existent component", pico.getComponentAdapter(Map.class));
102        }
103    
104        public void testRegistersSingleInstance() throws PicoException, PicoInitializationException {
105            MutablePicoContainer pico = createPicoContainer(null);
106            StringBuffer sb = new StringBuffer();
107            pico.registerComponentInstance(sb);
108            assertSame(sb, pico.getComponentInstance(StringBuffer.class));
109        }
110    
111        public void testContainerIsSerializable() throws PicoException, PicoInitializationException,
112                IOException, ClassNotFoundException {
113    
114            getTouchableFromSerializedContainer();
115    
116        }
117    
118        private Touchable getTouchableFromSerializedContainer() throws IOException, ClassNotFoundException {
119            MutablePicoContainer pico = createPicoContainerWithTouchableAndDependsOnTouchable();
120            // Add a list too, using a constant parameter
121            pico.registerComponentImplementation("list", ArrayList.class, new Parameter[]{new ConstantParameter(new Integer(10))});
122    
123            ByteArrayOutputStream baos = new ByteArrayOutputStream();
124            ObjectOutputStream oos = new ObjectOutputStream(baos);
125    
126            oos.writeObject(pico);
127            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray()));
128    
129            pico = (MutablePicoContainer) ois.readObject();
130    
131            DependsOnTouchable dependsOnTouchable = (DependsOnTouchable) pico.getComponentInstance(DependsOnTouchable.class);
132            assertNotNull(dependsOnTouchable);
133            return (Touchable) pico.getComponentInstance(Touchable.class);
134        }
135    
136        public void testSerializedContainerCanRetrieveImplementation() throws PicoException, PicoInitializationException,
137                IOException, ClassNotFoundException {
138    
139            Touchable touchable = getTouchableFromSerializedContainer();
140    
141            SimpleTouchable simpleTouchable = (SimpleTouchable) touchable;
142    
143            assertTrue(simpleTouchable.wasTouched);
144        }
145    
146    
147        public void testGettingComponentWithMissingDependencyFails() throws PicoException, PicoRegistrationException {
148            PicoContainer picoContainer = createPicoContainerWithDependsOnTouchableOnly();
149            try {
150                picoContainer.getComponentInstance(DependsOnTouchable.class);
151                fail("should need a Touchable");
152            } catch (UnsatisfiableDependenciesException e) {
153                assertSame(picoContainer.getComponentAdapterOfType(DependsOnTouchable.class).getComponentImplementation(), e.getUnsatisfiableComponentAdapter().getComponentImplementation());
154                final Set unsatisfiableDependencies = e.getUnsatisfiableDependencies();
155                assertEquals(1, unsatisfiableDependencies.size());
156    
157                // Touchable.class is now inside a List (the list of unsatisfied parameters) -- mparaz
158                List unsatisfied = (List) unsatisfiableDependencies.iterator().next();
159                assertEquals(1, unsatisfied.size());
160                assertEquals(Touchable.class, unsatisfied.get(0));
161            }
162        }
163    
164        public void testDuplicateRegistration() throws Exception {
165            try {
166                MutablePicoContainer pico = createPicoContainer(null);
167                pico.registerComponentImplementation(Object.class);
168                pico.registerComponentImplementation(Object.class);
169                fail("Should have failed with duplicate registration");
170            } catch (DuplicateComponentKeyRegistrationException e) {
171                assertTrue("Wrong key", e.getDuplicateKey() == Object.class);
172            }
173        }
174    
175        public void testExternallyInstantiatedObjectsCanBeRegistgeredAndLookedUp() throws PicoException, PicoInitializationException {
176            MutablePicoContainer pico = createPicoContainer(null);
177            final HashMap map = new HashMap();
178            pico.registerComponentInstance(Map.class, map);
179            assertSame(map, pico.getComponentInstance(Map.class));
180        }
181    
182        public void testAmbiguousResolution() throws PicoRegistrationException, PicoInitializationException {
183            MutablePicoContainer pico = createPicoContainer(null);
184            pico.registerComponentImplementation("ping", String.class);
185            pico.registerComponentInstance("pong", "pang");
186            try {
187                pico.getComponentInstance(String.class);
188            } catch (AmbiguousComponentResolutionException e) {
189                assertTrue(e.getMessage().indexOf("java.lang.String") != -1);
190            }
191        }
192    
193        public void testLookupWithUnregisteredKeyReturnsNull() throws PicoIntrospectionException, PicoInitializationException, AssignabilityRegistrationException, NotConcreteRegistrationException {
194            MutablePicoContainer pico = createPicoContainer(null);
195            assertNull(pico.getComponentInstance(String.class));
196        }
197    
198        public void testLookupWithUnregisteredTypeReturnsNull() throws PicoIntrospectionException, PicoInitializationException, AssignabilityRegistrationException, NotConcreteRegistrationException {
199            MutablePicoContainer pico = createPicoContainer(null);
200            assertNull(pico.getComponentInstanceOfType(String.class));
201        }
202    
203        public static class ListAdder {
204            public ListAdder(Collection list) {
205                list.add("something");
206            }
207        }
208    
209        public void testUnsatisfiableDependenciesExceptionGivesVerboseEnoughErrorMessage() {
210            MutablePicoContainer pico = createPicoContainer(null);
211            pico.registerComponentImplementation(ComponentD.class);
212    
213            try {
214                pico.getComponentInstance(ComponentD.class);
215            } catch (UnsatisfiableDependenciesException e) {
216                Set unsatisfiableDependencies = e.getUnsatisfiableDependencies();
217                assertEquals(1, unsatisfiableDependencies.size());
218    
219                List list = (List) unsatisfiableDependencies.iterator().next();
220    
221                final List expectedList = new ArrayList(2);
222                expectedList.add(ComponentE.class);
223                expectedList.add(ComponentB.class);
224    
225                assertEquals(expectedList, list);
226            }
227        }
228    
229        public void testUnsatisfiableDependenciesExceptionGivesUnsatisfiedDependencyTypes() {
230            MutablePicoContainer pico = createPicoContainer(null);
231            // D depends on E and B
232            pico.registerComponentImplementation(ComponentD.class);
233    
234            // first - do not register any dependency
235            // should yield first unsatisfied dependency
236            try {
237                pico.getComponentInstance(ComponentD.class);
238            } catch (UnsatisfiableDependenciesException e) {
239                Set unsatisfiableDependencies = e.getUnsatisfiableDependencies();
240                assertEquals(1, unsatisfiableDependencies.size());
241                List list = (List) unsatisfiableDependencies.iterator().next();
242                final List expectedList = new ArrayList(2);
243                expectedList.add(ComponentE.class);
244                expectedList.add(ComponentB.class);
245                assertEquals(expectedList, list);
246    
247                Class unsatisfiedDependencyType = e.getUnsatisfiedDependencyType();
248                assertNotNull(unsatisfiedDependencyType);
249                assertEquals(ComponentE.class, unsatisfiedDependencyType);
250            }
251            
252            // now register only first dependency
253            // should yield second unsatisfied dependency
254            pico.registerComponentImplementation(ComponentE.class);
255            try {
256                pico.getComponentInstance(ComponentD.class);
257            } catch (UnsatisfiableDependenciesException e) {
258                Set unsatisfiableDependencies = e.getUnsatisfiableDependencies();
259                assertEquals(1, unsatisfiableDependencies.size());
260                List list = (List) unsatisfiableDependencies.iterator().next();
261                final List expectedList = new ArrayList(2);
262                expectedList.add(ComponentE.class);
263                expectedList.add(ComponentB.class);
264                assertEquals(expectedList, list);
265    
266                Class unsatisfiedDependencyType = e.getUnsatisfiedDependencyType();
267                assertNotNull(unsatisfiedDependencyType);
268                assertEquals(ComponentB.class, unsatisfiedDependencyType);
269            }        
270        }
271        
272        public void testCyclicDependencyThrowsCyclicDependencyException() {
273            assertCyclicDependencyThrowsCyclicDependencyException(createPicoContainer(null));
274        }
275    
276        private static void assertCyclicDependencyThrowsCyclicDependencyException(MutablePicoContainer pico) {
277            pico.registerComponentImplementation(ComponentB.class);
278            pico.registerComponentImplementation(ComponentD.class);
279            pico.registerComponentImplementation(ComponentE.class);
280    
281            try {
282                pico.getComponentInstance(ComponentD.class);
283                fail("CyclicDependencyException expected");
284            } catch (CyclicDependencyException e) {
285                // CyclicDependencyException reports now the stack.
286                //final List dependencies = Arrays.asList(ComponentD.class.getConstructors()[0].getParameterTypes());
287                final List dependencies = Arrays.asList(new Class[]{ComponentD.class, ComponentE.class, ComponentD.class});
288                final List reportedDependencies = Arrays.asList(e.getDependencies());
289                assertEquals(dependencies, reportedDependencies);
290            } catch (StackOverflowError e) {
291                fail();
292            }
293        }
294    
295        public void testCyclicDependencyThrowsCyclicDependencyExceptionWithParentContainer() {
296            MutablePicoContainer pico = createPicoContainer(createPicoContainer(null));
297            assertCyclicDependencyThrowsCyclicDependencyException(pico);
298        }
299    
300        public void testRemovalNonRegisteredComponentAdapterWorksAndReturnsNull() {
301            final MutablePicoContainer picoContainer = createPicoContainer(null);
302            assertNull(picoContainer.unregisterComponent("COMPONENT DOES NOT EXIST"));
303        }
304    
305        /**
306         * Important! Nanning really, really depends on this!
307         */
308        public void testComponentAdapterRegistrationOrderIsMaintained() {
309    
310            ConstructorInjectionComponentAdapter c1 = new ConstructorInjectionComponentAdapter("1", Object.class);
311            ConstructorInjectionComponentAdapter c2 = new ConstructorInjectionComponentAdapter("2", String.class);
312    
313            MutablePicoContainer picoContainer = createPicoContainer(null);
314            picoContainer.registerComponent(c1);
315            picoContainer.registerComponent(c2);
316            assertEquals("registration order should be maintained",
317                    Arrays.asList(new Object[]{c1, c2}), picoContainer.getComponentAdapters());
318    
319            picoContainer.getComponentInstances(); // create all the instances at once
320            assertFalse("instances should be created in same order as adapters are created",
321                    picoContainer.getComponentInstances().get(0) instanceof String);
322            assertTrue("instances should be created in same order as adapters are created",
323                    picoContainer.getComponentInstances().get(1) instanceof String);
324    
325            MutablePicoContainer reversedPicoContainer = createPicoContainer(null);
326            reversedPicoContainer.registerComponent(c2);
327            reversedPicoContainer.registerComponent(c1);
328            assertEquals("registration order should be maintained",
329                    Arrays.asList(new Object[]{c2, c1}), reversedPicoContainer.getComponentAdapters());
330    
331            reversedPicoContainer.getComponentInstances(); // create all the instances at once
332            assertTrue("instances should be created in same order as adapters are created",
333                    reversedPicoContainer.getComponentInstances().get(0) instanceof String);
334            assertFalse("instances should be created in same order as adapters are created",
335                    reversedPicoContainer.getComponentInstances().get(1) instanceof String);
336        }
337    
338        public static class NeedsTouchable {
339            public Touchable touchable;
340    
341            public NeedsTouchable(Touchable touchable) {
342                this.touchable = touchable;
343            }
344        }
345    
346        public static class NeedsWashable {
347            public Washable washable;
348    
349            public NeedsWashable(Washable washable) {
350                this.washable = washable;
351            }
352        }
353    
354        public void testSameInstanceCanBeUsedAsDifferentTypeWhenCaching() {
355            MutablePicoContainer pico = createPicoContainer(null);
356            pico.registerComponentImplementation("wt", WashableTouchable.class);
357            pico.registerComponentImplementation("nw", NeedsWashable.class);
358            pico.registerComponentImplementation("nt", NeedsTouchable.class);
359    
360            NeedsWashable nw = (NeedsWashable) pico.getComponentInstance("nw");
361            NeedsTouchable nt = (NeedsTouchable) pico.getComponentInstance("nt");
362            assertSame(nw.washable, nt.touchable);
363        }
364    
365        public void testRegisterComponentWithObjectBadType() throws PicoIntrospectionException {
366            MutablePicoContainer pico = createPicoContainer(null);
367    
368            try {
369                pico.registerComponentInstance(Serializable.class, new Object());
370                fail("Shouldn't be able to register an Object.class as Serializable because it is not, " +
371                        "it does not implement it, Object.class does not implement much.");
372            } catch (AssignabilityRegistrationException e) {
373            }
374    
375        }
376    
377        public static class JMSService {
378            public final String serverid;
379            public final String path;
380    
381            public JMSService(String serverid, String path) {
382                this.serverid = serverid;
383                this.path = path;
384            }
385        }
386    
387        // http://jira.codehaus.org/secure/ViewIssue.jspa?key=PICO-52
388        public void testPico52() {
389            MutablePicoContainer pico = createPicoContainer(null);
390    
391            pico.registerComponentImplementation("foo", JMSService.class, new Parameter[]{
392                new ConstantParameter("0"),
393                new ConstantParameter("something"),
394            });
395            JMSService jms = (JMSService) pico.getComponentInstance("foo");
396            assertEquals("0", jms.serverid);
397            assertEquals("something", jms.path);
398        }
399    
400        public static class ComponentA {
401            public ComponentA(ComponentB b, ComponentC c) {
402                Assert.assertNotNull(b);
403                Assert.assertNotNull(c);
404            }
405        }
406    
407        public static class ComponentB {
408        }
409    
410        public static class ComponentC {
411        }
412    
413        public static class ComponentD {
414            public ComponentD(ComponentE e, ComponentB b) {
415                Assert.assertNotNull(e);
416                Assert.assertNotNull(b);
417            }
418        }
419    
420        public static class ComponentE {
421            public ComponentE(ComponentD d) {
422                Assert.assertNotNull(d);
423            }
424        }
425    
426        public static class ComponentF {
427            public ComponentF(ComponentA a) {
428                Assert.assertNotNull(a);
429            }
430        }
431    
432        public void testAggregatedVerificationException() {
433            MutablePicoContainer pico = createPicoContainer(null);
434            pico.registerComponentImplementation(ComponentA.class);
435            pico.registerComponentImplementation(ComponentE.class);
436            try {
437                new VerifyingVisitor().traverse(pico);
438                fail("we expect a PicoVerificationException");
439            } catch (PicoVerificationException e) {
440                List nested = e.getNestedExceptions();
441                assertEquals(2, nested.size());
442                assertTrue(-1 != e.getMessage().indexOf(ComponentA.class.getName()));
443                assertTrue(-1 != e.getMessage().indexOf(ComponentE.class.getName()));
444            }
445        }
446    
447        // An adapter has no longer a hosting container.
448    
449    //    public void testRegistrationOfAdapterSetsHostingContainerAsSelf() {
450    //        final InstanceComponentAdapter componentAdapter = new InstanceComponentAdapter("", new Object());
451    //        final MutablePicoContainer picoContainer = createPicoContainer(null);
452    //        picoContainer.registerComponent(componentAdapter);
453    //        assertSame(picoContainer, componentAdapter.getContainer());
454    //    }
455    
456        public static class ContainerDependency {
457            public ContainerDependency(PicoContainer container) {
458                assertNotNull(container);
459            }
460        }
461    
462        // ImplicitPicoContainer injection is bad. It is an open door for hackers. Developers with
463        // special PicoContainer needs should specifically register() a comtainer they want components to
464        // be able to pick up on.
465    
466    //    public void testImplicitPicoContainerInjection() {
467    //        MutablePicoContainer pico = createPicoContainer(null);
468    //        pico.registerComponentImplementation(ContainerDependency.class);
469    //        ContainerDependency dep = (ContainerDependency) pico.getComponentInstance(ContainerDependency.class);
470    //        assertSame(pico, dep.pico);
471    //    }
472    
473        public void testShouldReturnNullWhenUnregistereingUnmanagedComponent() {
474            final MutablePicoContainer pico = createPicoContainer(null);
475            assertNull(pico.unregisterComponentByInstance("yo"));
476        }
477    
478        public void testShouldReturnNullForComponentAdapterOfUnregisteredType() {
479            final MutablePicoContainer pico = createPicoContainer(null);
480            assertNull(pico.getComponentInstanceOfType(List.class));
481        }
482    
483        public void testShouldReturnNonMutableParent() {
484            DefaultPicoContainer parent = new DefaultPicoContainer();
485            final MutablePicoContainer picoContainer = createPicoContainer(parent);
486            assertNotSame(parent, picoContainer.getParent());
487            assertFalse(picoContainer.getParent() instanceof MutablePicoContainer);
488        }
489    
490        class Foo implements Startable, Disposable {
491            public boolean started;
492            public boolean stopped;
493            public boolean disposed;
494    
495            public void start() {
496                started = true;
497            }
498    
499            public void stop() {
500                stopped = true;
501            }
502    
503            public void dispose() {
504                disposed = true;
505            }
506    
507        }
508    
509        public void testContainerCascadesDefaultLifecycle() {
510            final MutablePicoContainer picoContainer = createPicoContainer(null);
511            Foo foo = new Foo();
512            picoContainer.registerComponentInstance(foo);
513            picoContainer.start();
514            assertEquals(true, foo.started);
515            picoContainer.stop();
516            assertEquals(true, foo.stopped);
517            picoContainer.dispose();
518            assertEquals(true, foo.disposed);
519        }
520    
521        public void testComponentInstancesFromParentsAreNotDirectlyAccessible2() {
522            final MutablePicoContainer a = createPicoContainer(null);
523            final MutablePicoContainer b = createPicoContainer(a);
524            final MutablePicoContainer c = createPicoContainer(b);
525    
526            Object ao = new Object();
527            Object bo = new Object();
528            Object co = new Object();
529    
530            a.registerComponentInstance("a", ao);
531            b.registerComponentInstance("b", bo);
532            c.registerComponentInstance("c", co);
533    
534            assertEquals(1, a.getComponentInstances().size());
535            assertEquals(1, b.getComponentInstances().size());
536            assertEquals(1, c.getComponentInstances().size());
537        }
538    
539        public void testStartStopAndDisposeCascadedtoChildren() {
540            final MutablePicoContainer parent = createPicoContainer(null);
541            parent.registerComponentInstance(new StringBuffer());
542            final MutablePicoContainer child = createPicoContainer(parent);
543            parent.addChildContainer(child);
544            child.registerComponentImplementation(LifeCycleMonitoring.class);
545            parent.start();
546            try {
547                child.start();
548                fail("IllegalStateException expected");
549            } catch (IllegalStateException e) {
550                assertEquals("child already started", "Already started", e.getMessage());
551            }
552            parent.stop();
553            try {
554                child.stop();
555                fail("IllegalStateException expected");
556            } catch (IllegalStateException e) {
557                assertEquals("child not started", "Not started", e.getMessage());
558            }
559            parent.dispose();
560            try {
561                child.dispose();
562                fail("IllegalStateException expected");
563            } catch (IllegalStateException e) {
564                assertEquals("child already disposed", "Already disposed", e.getMessage());
565            }
566    
567        }
568    
569        public void testMakingOfChildContainer() {
570            final MutablePicoContainer parent = createPicoContainer(null);
571            MutablePicoContainer child = parent.makeChildContainer();
572            assertNotNull(child);
573        }
574    
575        public void testMakingOfChildContainerPercolatesLifecycleManager() {
576            final MutablePicoContainer parent = createPicoContainer(null);
577            parent.registerComponentImplementation("one", TestLifecycleComponent.class);
578            MutablePicoContainer child = parent.makeChildContainer();
579            assertNotNull(child);
580            child.registerComponentImplementation("two", TestLifecycleComponent.class);
581            parent.start();
582            try {
583                child.start();
584            } catch (IllegalStateException e) {
585                assertEquals("child already started", "Already started", e.getMessage());
586            }
587            //TODO - The LifecycleManager reference in child containers is not used. Thus is is almost pointless
588            // The reason is because DefaultPicoContainer's accept() method visits child containers' on its own.
589            // This may be file for visiting components in a tree for general cases, but for lifecycle, we
590            // should hand to each LifecycleManager's start(..) at each appropriate node. See mail-list discussion.
591        }
592    
593        public static class TestLifecycleManager implements LifecycleManager {
594            public ArrayList started = new ArrayList();
595            public void start(PicoContainer node) {
596                started.add(node);
597            }
598    
599            public void stop(PicoContainer node) {
600            }
601    
602            public void dispose(PicoContainer node) {
603            }
604    
605            public boolean hasLifecycle() {
606                return true;
607            }
608        }
609    
610        public static class TestLifecycleComponent implements Startable {
611            public boolean started;
612            public void start() {
613                started = true;
614            }
615            public void stop() {
616            }
617        }
618    
619        public void testStartStopAndDisposeNotCascadedtoRemovedChildren() {
620            final MutablePicoContainer parent = createPicoContainer(null);
621            parent.registerComponentInstance(new StringBuffer());
622            StringBuffer sb = (StringBuffer) parent.getComponentInstancesOfType(StringBuffer.class).get(0);
623    
624            final MutablePicoContainer child = createPicoContainer(parent);
625            assertTrue(parent.addChildContainer(child));
626            child.registerComponentImplementation(LifeCycleMonitoring.class);
627            assertTrue(parent.removeChildContainer(child));
628            parent.start();
629            assertTrue(sb.toString().indexOf("-started") == -1);
630            parent.stop();
631            assertTrue(sb.toString().indexOf("-stopped") == -1);
632            parent.dispose();
633            assertTrue(sb.toString().indexOf("-disposed") == -1);
634        }
635    
636        public void testShouldCascadeStartStopAndDisposeToChild() {
637    
638            StringBuffer sb = new StringBuffer();
639            final MutablePicoContainer parent = createPicoContainer(null);
640            parent.registerComponentInstance(sb);
641            parent.registerComponentImplementation(Map.class, HashMap.class);
642    
643            final MutablePicoContainer child = parent.makeChildContainer();
644            child.registerComponentImplementation(LifeCycleMonitoring.class);
645    
646            Map map = (Map) parent.getComponentInstance(Map.class);
647            assertNotNull(map);
648            parent.start();
649            try {
650                child.start();
651                fail("IllegalStateException expected");
652            } catch (IllegalStateException e) {
653                assertEquals("child already started", "Already started", e.getMessage());
654            }
655            parent.stop();
656            try {
657                child.stop();
658                fail("IllegalStateException expected");
659            } catch (IllegalStateException e) {
660                assertEquals("child not started", "Not started", e.getMessage());
661            }
662            parent.dispose();
663            try {
664                child.dispose();
665                fail("IllegalStateException expected");
666            } catch (IllegalStateException e) {
667                assertEquals("child already disposed", "Already disposed", e.getMessage());
668            }
669        }
670    
671        public static class LifeCycleMonitoring implements Startable, Disposable {
672            StringBuffer sb;
673    
674            public LifeCycleMonitoring(StringBuffer sb) {
675                this.sb = sb;
676                sb.append("-instantiated");
677            }
678    
679            public void start() {
680                sb.append("-started");
681            }
682    
683            public void stop() {
684                sb.append("-stopped");
685            }
686    
687            public void dispose() {
688                sb.append("-disposed");
689            }
690        }
691    
692        public static class RecordingStrategyVisitor extends AbstractPicoVisitor {
693    
694            private final List list;
695    
696            public RecordingStrategyVisitor(List list) {
697                this.list = list;
698            }
699    
700            public void visitContainer(PicoContainer pico) {
701                list.add(pico);
702            }
703    
704            public void visitComponentAdapter(ComponentAdapter componentAdapter) {
705                list.add(componentAdapter);
706            }
707    
708            public void visitParameter(Parameter parameter) {
709                list.add(parameter);
710            }
711    
712        }
713    
714        public void testAcceptImplementsBreadthFirstStrategy() {
715            final MutablePicoContainer parent = createPicoContainer(null);
716            final MutablePicoContainer child = parent.makeChildContainer();
717            ComponentAdapter hashMapAdapter = parent.registerComponent(new ConstructorInjectionComponentAdapter(HashMap.class, HashMap.class));
718            ComponentAdapter hashSetAdapter = parent.registerComponent(new ConstructorInjectionComponentAdapter(HashSet.class, HashSet.class));
719            ComponentAdapter stringAdapter = parent.registerComponent(new InstanceComponentAdapter(String.class, "foo"));
720            ComponentAdapter arrayListAdapter = child.registerComponent(new ConstructorInjectionComponentAdapter(ArrayList.class, ArrayList.class));
721            Parameter componentParameter = BasicComponentParameter.BASIC_DEFAULT;
722            Parameter throwableParameter = new ConstantParameter(new Throwable("bar"));
723            ComponentAdapter exceptionAdapter = child.registerComponent(new ConstructorInjectionComponentAdapter(Exception.class, Exception.class, new Parameter[]{
724                componentParameter,
725                throwableParameter
726            }));
727    
728            List expectedList = Arrays.asList(new Object[]{
729                parent,
730                hashMapAdapter,
731                hashSetAdapter,
732                stringAdapter,
733                child,
734                arrayListAdapter,
735                exceptionAdapter,
736                componentParameter,
737                throwableParameter
738            });
739            List visitedList = new LinkedList();
740            PicoVisitor visitor = new RecordingStrategyVisitor(visitedList);
741            visitor.traverse(parent);
742            assertEquals(expectedList, visitedList);
743        }
744    
745        public void testAmbiguousDependencies() throws PicoRegistrationException, PicoInitializationException {
746    
747            MutablePicoContainer pico = this.createPicoContainer(null);
748    
749            // Register two Touchables that Fred will be confused about
750            pico.registerComponentImplementation(SimpleTouchable.class);
751            pico.registerComponentImplementation(DerivedTouchable.class);
752    
753            // Register a confused DependsOnTouchable
754            pico.registerComponentImplementation(DependsOnTouchable.class);
755    
756            try {
757                pico.getComponentInstance(DependsOnTouchable.class);
758                fail("DependsOnTouchable should have been confused about the two Touchables");
759            } catch (AmbiguousComponentResolutionException e) {
760                List componentImplementations = Arrays.asList(e.getAmbiguousComponentKeys());
761                assertTrue(componentImplementations.contains(DerivedTouchable.class));
762                assertTrue(componentImplementations.contains(SimpleTouchable.class));
763    
764                assertTrue(e.getMessage().indexOf(DerivedTouchable.class.getName()) != -1);
765            }
766        }
767    
768    
769        public static class DerivedTouchable extends SimpleTouchable {
770            public DerivedTouchable() {
771            }    
772        }
773        
774        
775        public static class NonGreedyClass {
776            
777            public int value = 0;
778            
779            public NonGreedyClass() {
780                //Do nothing.
781            }
782            
783            public NonGreedyClass(ComponentA component) {
784                fail("Greedy Constructor should never have been called.  Instead got: " + component);
785            }
786                
787            
788        };
789        
790        public void testNoArgConstructorToBeSelected() {
791            MutablePicoContainer pico = this.createPicoContainer(null);
792            pico.registerComponentImplementation(ComponentA.class);
793            pico.registerComponentImplementation(NonGreedyClass.class, NonGreedyClass.class, new Parameter[] {});
794            
795    
796            NonGreedyClass instance = (NonGreedyClass) pico.getComponentInstance(NonGreedyClass.class);
797            assertNotNull(instance);
798        }
799    
800    }