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 Joerg Schaible                                           *
009     *****************************************************************************/
010    package org.picocontainer.tck;
011    
012    import com.thoughtworks.xstream.XStream;
013    import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;
014    import com.thoughtworks.xstream.io.xml.XppDriver;
015    
016    import junit.framework.Assert;
017    import junit.framework.AssertionFailedError;
018    
019    import org.picocontainer.ComponentAdapter;
020    import org.picocontainer.MutablePicoContainer;
021    import org.picocontainer.Parameter;
022    import org.picocontainer.PicoContainer;
023    import org.picocontainer.PicoInitializationException;
024    import org.picocontainer.PicoIntrospectionException;
025    import org.picocontainer.defaults.AbstractPicoVisitor;
026    import org.picocontainer.defaults.ComponentAdapterFactory;
027    import org.picocontainer.defaults.ConstantParameter;
028    import org.picocontainer.defaults.ConstructorInjectionComponentAdapterFactory;
029    import org.picocontainer.defaults.CyclicDependencyException;
030    import org.picocontainer.defaults.DecoratingComponentAdapter;
031    import org.picocontainer.defaults.DefaultComponentAdapterFactory;
032    import org.picocontainer.defaults.DefaultPicoContainer;
033    import org.picocontainer.defaults.LifecycleStrategy;
034    import org.picocontainer.defaults.ObjectReference;
035    import org.picocontainer.defaults.PicoInvocationTargetInitializationException;
036    import org.picocontainer.defaults.SimpleReference;
037    
038    import org.jmock.MockObjectTestCase;
039    
040    import java.io.ByteArrayInputStream;
041    import java.io.ByteArrayOutputStream;
042    import java.io.IOException;
043    import java.io.ObjectInputStream;
044    import java.io.ObjectOutputStream;
045    import java.lang.reflect.Constructor;
046    import java.util.ArrayList;
047    import java.util.Collection;
048    import java.util.HashSet;
049    import java.util.Iterator;
050    import java.util.LinkedList;
051    import java.util.List;
052    import java.util.Set;
053    
054    
055    /**
056     * Test suite for a ComponentAdapter implementation.
057     * 
058     * @author Jörg Schaible
059     * @since 1.1
060     */
061    public abstract class AbstractComponentAdapterTestCase extends MockObjectTestCase {
062    
063        public static int SERIALIZABLE = 1;
064        public static int VERIFYING = 2;
065        public static int INSTANTIATING = 4;
066        public static int RESOLVING = 8;
067    
068        protected abstract Class getComponentAdapterType();
069    
070        protected int getComponentAdapterNature() {
071            return SERIALIZABLE | VERIFYING | INSTANTIATING | RESOLVING;
072        }
073    
074        protected ComponentAdapterFactory createDefaultComponentAdapterFactory() {
075            return new DefaultComponentAdapterFactory();
076        }
077    
078        // ============================================
079        // Default
080        // ============================================
081    
082        /**
083         * Prepare the test <em>verifyWithoutDependencyWorks</em>.
084         * 
085         * @param picoContainer container, may probably not be used.
086         * @return a ComponentAdapter of the type to test for a component without dependencies. Registration in the pico is
087         *         not necessary.
088         */
089        protected abstract ComponentAdapter prepDEF_verifyWithoutDependencyWorks(MutablePicoContainer picoContainer);
090    
091        final public void testDEF_verifyWithoutDependencyWorks() {
092            final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
093            final ComponentAdapter componentAdapter = prepDEF_verifyWithoutDependencyWorks(picoContainer);
094            assertSame(getComponentAdapterType(), componentAdapter.getClass());
095            componentAdapter.verify(picoContainer);
096        }
097    
098        /**
099         * Prepare the test <em>verifyDoesNotInstantiate</em>.
100         * 
101         * @param picoContainer container, may probably not be used.
102         * @return a ComponentAdapter of the type to test for a component that may throw on instantiation. Registration in
103         *         the pico is not necessary.
104         */
105        protected abstract ComponentAdapter prepDEF_verifyDoesNotInstantiate(MutablePicoContainer picoContainer);
106    
107        final public void testDEF_verifyDoesNotInstantiate() {
108            final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
109            final ComponentAdapter componentAdapter = prepDEF_verifyDoesNotInstantiate(picoContainer);
110            assertSame(getComponentAdapterType(), componentAdapter.getClass());
111            final ComponentAdapter notInstantiatablecomponentAdapter = new NotInstantiatableComponentAdapter(
112                    componentAdapter);
113            final PicoContainer wrappedPicoContainer = wrapComponentInstances(
114                    NotInstantiatableComponentAdapter.class, picoContainer, null);
115            notInstantiatablecomponentAdapter.verify(wrappedPicoContainer);
116        }
117    
118        /**
119         * Prepare the test <em>visitable</em>.
120         * 
121         * @return a ComponentAdapter of the type to test. If the ComponentAdapter supports {@link Parameter}, you have to
122         *         select a component, that have some.
123         */
124        protected abstract ComponentAdapter prepDEF_visitable();
125    
126        final public void testDEF_visitable() {
127            final ComponentAdapter componentAdapter = prepDEF_visitable();
128            final Class type = getComponentAdapterType();
129            assertSame(type, componentAdapter.getClass());
130            boolean hasParameters = supportsParameters(type);
131            final RecordingVisitor visitor = new RecordingVisitor();
132            visitor.traverse(componentAdapter);
133            final List visitedElements = new ArrayList(visitor.getVisitedElements());
134            assertSame(componentAdapter, visitedElements.get(0));
135            if (hasParameters) {
136                hasParameters = false;
137                for (final Iterator iter = visitedElements.iterator(); iter.hasNext() && !hasParameters;) {
138                    hasParameters = Parameter.class.isAssignableFrom(iter.next().getClass());
139                }
140                assertTrue("ComponentAdapter " + type + " supports parameters, provide some", hasParameters);
141            }
142        }
143    
144        /**
145         * Prepare the test <em>isAbleToTakeParameters</em>. Overload this function, if the ComponentAdapter to test
146         * supports {@link Parameter}.
147         * 
148         * @param picoContainer container, may probably not be used.
149         * @return a ComponentAdapter of the type to test. Select a component, that has some parameters. Registration in the
150         *         pico is not necessary.
151         */
152        protected ComponentAdapter prepDEF_isAbleToTakeParameters(MutablePicoContainer picoContainer) {
153            final Class type = getComponentAdapterType();
154            boolean hasParameters = supportsParameters(type);
155            if (hasParameters) {
156                fail("You have to overwrite this method for a useful test");
157            }
158            return null;
159        }
160    
161        final public void testDEF_isAbleToTakeParameters() {
162            final Class type = getComponentAdapterType();
163            boolean hasParameters = supportsParameters(type);
164            if (hasParameters) {
165                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
166                final ComponentAdapter componentAdapter = prepDEF_isAbleToTakeParameters(picoContainer);
167                assertSame(getComponentAdapterType(), componentAdapter.getClass());
168                final RecordingVisitor visitor = new RecordingVisitor();
169                visitor.traverse(componentAdapter);
170                final List visitedElements = visitor.getVisitedElements();
171                if (hasParameters) {
172                    hasParameters = false;
173                    for (final Iterator iter = visitedElements.iterator(); iter.hasNext() && !hasParameters;) {
174                        hasParameters = Parameter.class.isAssignableFrom(iter.next().getClass());
175                    }
176                    assertTrue("ComponentAdapter " + type + " supports parameters, provide some", hasParameters);
177                }
178                final Object instance = componentAdapter.getComponentInstance(picoContainer);
179                assertNotNull(instance);
180            }
181        }
182    
183        // ============================================
184        // Serializable
185        // ============================================
186    
187        /**
188         * Prepare the test <em>isSerializable</em>. Overload this function, if the ComponentAdapter supports
189         * serialization.
190         * 
191         * @param picoContainer container, may probably not be used.
192         * @return a ComponentAdapter of the type to test. Registration in the pico is not necessary.
193         */
194        protected ComponentAdapter prepSER_isSerializable(MutablePicoContainer picoContainer) {
195            throw new AssertionFailedError("You have to overwrite this method for a useful test");
196        }
197    
198        final public void testSER_isSerializable() throws IOException, ClassNotFoundException {
199            if ((getComponentAdapterNature() & SERIALIZABLE) > 0) {
200                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
201                final ComponentAdapter componentAdapter = prepSER_isSerializable(picoContainer);
202                assertSame(getComponentAdapterType(), componentAdapter.getClass());
203                final Object instance = componentAdapter.getComponentInstance(picoContainer);
204                assertNotNull(instance);
205                final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
206                final ObjectOutputStream outputStream = new ObjectOutputStream(byteArrayOutputStream);
207                outputStream.writeObject(componentAdapter);
208                outputStream.close();
209                final ObjectInputStream inputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream
210                        .toByteArray()));
211                final ComponentAdapter serializedComponentAdapter = (ComponentAdapter)inputStream.readObject();
212                inputStream.close();
213                assertEquals(componentAdapter.getComponentKey(), serializedComponentAdapter.getComponentKey());
214                final Object instanceAfterSerialization = serializedComponentAdapter.getComponentInstance(picoContainer);
215                assertNotNull(instanceAfterSerialization);
216                assertSame(instance.getClass(), instanceAfterSerialization.getClass());
217            }
218        }
219    
220        /**
221         * Prepare the test <em>isXStreamSerializable</em>. Overload this function, if the ComponentAdapter supports
222         * serialization.
223         * 
224         * @param picoContainer container, may probably not be used.
225         * @return a ComponentAdapter of the type to test. Registration in the pico is not necessary.
226         */
227        protected ComponentAdapter prepSER_isXStreamSerializable(MutablePicoContainer picoContainer) {
228            throw new AssertionFailedError("You have to overwrite this method for a useful test");
229        }
230    
231        final public void testSER_isXStreamSerializableWithPureReflection() {
232            if ((getComponentAdapterNature() & SERIALIZABLE) > 0) {
233                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
234                final ComponentAdapter componentAdapter = prepSER_isXStreamSerializable(picoContainer);
235                assertSame(getComponentAdapterType(), componentAdapter.getClass());
236                final Object instance = componentAdapter.getComponentInstance(picoContainer);
237                assertNotNull(instance);
238                final XStream xstream = new XStream(new PureJavaReflectionProvider(), new XppDriver());
239                final String xml = xstream.toXML(componentAdapter);
240                final ComponentAdapter serializedComponentAdapter = (ComponentAdapter)xstream.fromXML(xml);
241                assertEquals(componentAdapter.getComponentKey(), serializedComponentAdapter.getComponentKey());
242                final Object instanceAfterSerialization = serializedComponentAdapter.getComponentInstance(picoContainer);
243                assertNotNull(instanceAfterSerialization);
244                assertSame(instance.getClass(), instanceAfterSerialization.getClass());
245            }
246        }
247    
248        final public void testSER_isXStreamSerializable() {
249            if ((getComponentAdapterNature() & SERIALIZABLE) > 0) {
250                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
251                final ComponentAdapter componentAdapter = prepSER_isXStreamSerializable(picoContainer);
252                assertSame(getComponentAdapterType(), componentAdapter.getClass());
253                final Object instance = componentAdapter.getComponentInstance(picoContainer);
254                assertNotNull(instance);
255                final XStream xstream = new XStream(new XppDriver());
256                final String xml = xstream.toXML(componentAdapter);
257                final ComponentAdapter serializedComponentAdapter = (ComponentAdapter)xstream.fromXML(xml);
258                assertEquals(componentAdapter.getComponentKey(), serializedComponentAdapter.getComponentKey());
259                final Object instanceAfterSerialization = serializedComponentAdapter.getComponentInstance(picoContainer);
260                assertNotNull(instanceAfterSerialization);
261                assertSame(instance.getClass(), instanceAfterSerialization.getClass());
262            }
263        }
264    
265        // ============================================
266        // Verifying
267        // ============================================
268    
269        /**
270         * Prepare the test <em>verificationFailsWithUnsatisfiedDependency</em>. Overload this function, if the
271         * ComponentAdapter's verification can fail e.g. due to an unresolved dependency.
272         * 
273         * @param picoContainer container, may probably not be used.
274         * @return a ComponentAdapter of the type to test, that fails for the verification, e.g. because of a compoennt with
275         *         missing dependencies. Registration in the pico is not necessary.
276         */
277        protected ComponentAdapter prepVER_verificationFails(MutablePicoContainer picoContainer) {
278            throw new AssertionFailedError("You have to overwrite this method for a useful test");
279        }
280    
281        final public void testVER_verificationFails() {
282            if ((getComponentAdapterNature() & VERIFYING) > 0) {
283                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
284                final ComponentAdapter componentAdapter = prepVER_verificationFails(picoContainer);
285                assertSame(getComponentAdapterType(), componentAdapter.getClass());
286                try {
287                    componentAdapter.verify(picoContainer);
288                    fail("PicoIntrospectionException expected");
289                } catch (PicoIntrospectionException e) {
290                } catch (Exception e) {
291                    fail("PicoIntrospectionException expected, but got " + e.getClass().getName());
292                }
293                try {
294                    componentAdapter.getComponentInstance(picoContainer);
295                    fail("PicoInitializationException or PicoIntrospectionException expected");
296                } catch (PicoInitializationException e) {
297                } catch (PicoIntrospectionException e) {
298                } catch (Exception e) {
299                    fail("PicoInitializationException or PicoIntrospectionException expected, but got "
300                            + e.getClass().getName());
301                }
302            }
303        }
304    
305        // ============================================
306        // Instantiating
307        // ============================================
308    
309        /**
310         * Prepare the test <em>createsNewInstances</em>. Overload this function, if the ComponentAdapter is
311         * instantiating. It should create a new instance with every call.
312         * 
313         * @param picoContainer container, may probably not be used.
314         * @return a ComponentAdapter of the type to test. Registration in the pico is not necessary.
315         */
316        protected ComponentAdapter prepINS_createsNewInstances(MutablePicoContainer picoContainer) {
317            throw new AssertionFailedError("You have to overwrite this method for a useful test");
318        }
319    
320        final public void testINS_createsNewInstances() {
321            if ((getComponentAdapterNature() & INSTANTIATING) > 0) {
322                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
323                final ComponentAdapter componentAdapter = prepINS_createsNewInstances(picoContainer);
324                assertSame(getComponentAdapterType(), componentAdapter.getClass());
325                final Object instance = componentAdapter.getComponentInstance(picoContainer);
326                assertNotNull(instance);
327                assertNotSame(instance, componentAdapter.getComponentInstance(picoContainer));
328                assertSame(instance.getClass(), componentAdapter.getComponentInstance(picoContainer).getClass());
329            }
330        }
331    
332        /**
333         * Prepare the test <em>errorIsRethrown</em>. Overload this function, if the ComponentAdapter is instantiating.
334         * 
335         * @param picoContainer container, may probably not be used.
336         * @return a ComponentAdapter of the type to test with a component that fails with an {@link Error} at
337         *         instantiation. Registration in the pico is not necessary.
338         */
339        protected ComponentAdapter prepINS_errorIsRethrown(MutablePicoContainer picoContainer) {
340            throw new AssertionFailedError("You have to overwrite this method for a useful test");
341        }
342    
343        final public void testINS_errorIsRethrown() {
344            if ((getComponentAdapterNature() & INSTANTIATING) > 0) {
345                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
346                final ComponentAdapter componentAdapter = prepINS_errorIsRethrown(picoContainer);
347                assertSame(getComponentAdapterType(), componentAdapter.getClass());
348                try {
349                    componentAdapter.getComponentInstance(picoContainer);
350                    fail("Thrown Error excpected");
351                } catch (final Error e) {
352                    assertEquals("test", e.getMessage());
353                }
354            }
355        }
356    
357        /**
358         * Prepare the test <em>runtimeExceptionIsRethrown</em>. Overload this function, if the ComponentAdapter is
359         * instantiating.
360         * 
361         * @param picoContainer container, may probably not be used.
362         * @return a ComponentAdapter of the type to test with a component that fails with a {@link RuntimeException} at
363         *         instantiation. Registration in the pico is not necessary.
364         */
365        protected ComponentAdapter prepINS_runtimeExceptionIsRethrown(MutablePicoContainer picoContainer) {
366            throw new AssertionFailedError("You have to overwrite this method for a useful test");
367        }
368    
369        final public void testINS_runtimeExceptionIsRethrown() {
370            if ((getComponentAdapterNature() & INSTANTIATING) > 0) {
371                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
372                final ComponentAdapter componentAdapter = prepINS_runtimeExceptionIsRethrown(picoContainer);
373                assertSame(getComponentAdapterType(), componentAdapter.getClass());
374                try {
375                    componentAdapter.getComponentInstance(picoContainer);
376                    fail("Thrown RuntimeException excpected");
377                } catch (final RuntimeException e) {
378                    assertEquals("test", e.getMessage());
379                }
380            }
381        }
382    
383        /**
384         * Prepare the test <em>normalExceptionIsRethrownInsidePicoInvocationTargetInitializationException</em>. Overload
385         * this function, if the ComponentAdapter is instantiating.
386         * 
387         * @param picoContainer container, may probably not be used.
388         * @return a ComponentAdapter of the type to test with a component that fails with a
389         *         {@link PicoInvocationTargetInitializationException} at instantiation. Registration in the pico is not
390         *         necessary.
391         */
392        protected ComponentAdapter prepINS_normalExceptionIsRethrownInsidePicoInvocationTargetInitializationException(
393                MutablePicoContainer picoContainer) {
394            throw new AssertionFailedError("You have to overwrite this method for a useful test");
395        }
396    
397        final public void testINS_normalExceptionIsRethrownInsidePicoInvocationTargetInitializationException() {
398            if ((getComponentAdapterNature() & INSTANTIATING) > 0) {
399                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
400                final ComponentAdapter componentAdapter = prepINS_normalExceptionIsRethrownInsidePicoInvocationTargetInitializationException(picoContainer);
401                assertSame(getComponentAdapterType(), componentAdapter.getClass());
402                try {
403                    componentAdapter.getComponentInstance(picoContainer);
404                    fail("Thrown PicoInvocationTargetInitializationException excpected");
405                } catch (final PicoInvocationTargetInitializationException e) {
406                    assertTrue(e.getMessage().endsWith("test"));
407                    assertTrue(e.getCause() instanceof Exception);
408                }
409            }
410        }
411    
412        // ============================================
413        // Resolving
414        // ============================================
415    
416        /**
417         * Prepare the test <em>dependenciesAreResolved</em>. Overload this function, if the ComponentAdapter is resolves
418         * dependencies.
419         * 
420         * @param picoContainer container, used to register dependencies.
421         * @return a ComponentAdapter of the type to test with a component that has dependencies. Registration in the pico
422         *         is not necessary.
423         */
424        protected ComponentAdapter prepRES_dependenciesAreResolved(MutablePicoContainer picoContainer) {
425            throw new AssertionFailedError("You have to overwrite this method for a useful test");
426        }
427    
428        final public void testRES_dependenciesAreResolved() {
429            if ((getComponentAdapterNature() & RESOLVING) > 0) {
430                final List dependencies = new LinkedList();
431                final Object[] wrapperDependencies = new Object[]{dependencies};
432                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
433                final ComponentAdapter componentAdapter = prepRES_dependenciesAreResolved(picoContainer);
434                assertSame(getComponentAdapterType(), componentAdapter.getClass());
435                assertFalse(picoContainer.getComponentAdapters().contains(componentAdapter));
436                final PicoContainer wrappedPicoContainer = wrapComponentInstances(
437                        CollectingComponentAdapter.class, picoContainer, wrapperDependencies);
438                final Object instance = componentAdapter.getComponentInstance(wrappedPicoContainer);
439                assertNotNull(instance);
440                assertTrue(dependencies.size() > 0);
441            }
442        }
443    
444        /**
445         * Prepare the test <em>failingVerificationWithCyclicDependencyException</em>. Overload this function, if the
446         * ComponentAdapter is resolves dependencies.
447         * 
448         * @param picoContainer container, used to register dependencies.
449         * @return a ComponentAdapter of the type to test with a component that has cyclic dependencies. You have to
450         *         register the component itself in the pico.
451         */
452        protected ComponentAdapter prepRES_failingVerificationWithCyclicDependencyException(
453                MutablePicoContainer picoContainer) {
454            throw new AssertionFailedError("You have to overwrite this method for a useful test");
455        }
456    
457        final public void testRES_failingVerificationWithCyclicDependencyException() {
458            if ((getComponentAdapterNature() & RESOLVING) > 0) {
459                final Set cycleInstances = new HashSet();
460                final ObjectReference cycleCheck = new SimpleReference();
461                final Object[] wrapperDependencies = new Object[]{cycleInstances, cycleCheck};
462                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
463                final ComponentAdapter componentAdapter = prepRES_failingVerificationWithCyclicDependencyException(picoContainer);
464                assertSame(getComponentAdapterType(), componentAdapter.getClass());
465                assertTrue(picoContainer.getComponentAdapters().contains(componentAdapter));
466                final PicoContainer wrappedPicoContainer = wrapComponentInstances(
467                        CycleDetectorComponentAdapter.class, picoContainer, wrapperDependencies);
468                try {
469                    componentAdapter.verify(wrappedPicoContainer);
470                    fail("Thrown PicoVerificationException excpected");
471                } catch (final CyclicDependencyException cycle) {
472                    final Class[] dependencies = cycle.getDependencies();
473                    assertSame(dependencies[0], dependencies[dependencies.length - 1]);
474                }
475            }
476        }
477    
478        /**
479         * Prepare the test <em>failingInstantiationWithCyclicDependencyException</em>. Overload this function, if the
480         * ComponentAdapter is resolves dependencies.
481         * 
482         * @param picoContainer container, used to register dependencies.
483         * @return a ComponentAdapter of the type to test with a component that has cyclic dependencies. You have to
484         *         register the component itself in the pico.
485         */
486        protected ComponentAdapter prepRES_failingInstantiationWithCyclicDependencyException(
487                MutablePicoContainer picoContainer) {
488            throw new AssertionFailedError("You have to overwrite this method for a useful test");
489        }
490    
491        final public void testRES_failingInstantiationWithCyclicDependencyException() {
492            if ((getComponentAdapterNature() & RESOLVING) > 0) {
493                final Set cycleInstances = new HashSet();
494                final ObjectReference cycleCheck = new SimpleReference();
495                final Object[] wrapperDependencies = new Object[]{cycleInstances, cycleCheck};
496                final MutablePicoContainer picoContainer = new DefaultPicoContainer(createDefaultComponentAdapterFactory());
497                final ComponentAdapter componentAdapter = prepRES_failingInstantiationWithCyclicDependencyException(picoContainer);
498                assertSame(getComponentAdapterType(), componentAdapter.getClass());
499                assertTrue(picoContainer.getComponentAdapters().contains(componentAdapter));
500                final PicoContainer wrappedPicoContainer = wrapComponentInstances(
501                        CycleDetectorComponentAdapter.class, picoContainer, wrapperDependencies);
502                try {
503                    componentAdapter.getComponentInstance(wrappedPicoContainer);
504                    fail("Thrown CyclicDependencyException excpected");
505                } catch (final CyclicDependencyException e) {
506                    final Class[] dependencies = e.getDependencies();
507                    assertSame(dependencies[0], dependencies[dependencies.length - 1]);
508                }
509            }
510        }
511    
512        // ============================================
513        // Model & Helpers
514        // ============================================
515    
516        static class RecordingVisitor extends AbstractPicoVisitor {
517            private final List visitedElements = new LinkedList();
518    
519            public void visitContainer(PicoContainer pico) {
520                visitedElements.add(pico);
521            }
522    
523            public void visitComponentAdapter(ComponentAdapter componentAdapter) {
524                visitedElements.add(componentAdapter);
525            }
526    
527            public void visitParameter(Parameter parameter) {
528                visitedElements.add(parameter);
529            }
530    
531            List getVisitedElements() {
532                return visitedElements;
533            }
534        }
535    
536        static public class NotInstantiatableComponentAdapter extends DecoratingComponentAdapter {
537            public NotInstantiatableComponentAdapter(final ComponentAdapter delegate) {
538                super(delegate);
539            }
540    
541            public Object getComponentInstance(final PicoContainer container) {
542                Assert.fail("Not instantiatable");
543                return null;
544            }
545        }
546    
547        static public class CollectingComponentAdapter extends DecoratingComponentAdapter {
548            final List list;
549    
550            public CollectingComponentAdapter(final ComponentAdapter delegate, final List list) {
551                super(delegate);
552                this.list = list;
553            }
554    
555            public Object getComponentInstance(final PicoContainer container) {
556                final Object result = super.getComponentInstance(container);
557                list.add(result);
558                return result;
559            }
560        }
561    
562        static public class CycleDetectorComponentAdapter extends DecoratingComponentAdapter {
563            private final Set set;
564            private final ObjectReference reference;
565    
566            public CycleDetectorComponentAdapter(
567                    final ComponentAdapter delegate, final Set set, final ObjectReference reference) {
568                super(delegate);
569                this.set = set;
570                this.reference = reference;
571            }
572    
573            public Object getComponentInstance(final PicoContainer container) {
574                if (set.contains(this)) {
575                    reference.set(this);
576                } else {
577                    set.add(this);
578                }
579                return super.getComponentInstance(container);
580            }
581        }
582    
583        public static class RecordingLifecycleStrategy implements LifecycleStrategy {
584            private StringBuffer recorder;
585            
586            public RecordingLifecycleStrategy(StringBuffer recorder) {
587                this.recorder = recorder;
588            }
589        
590            public void start(Object component) {
591                recorder.append("<start");
592            }
593        
594            public void stop(Object component) {
595                recorder.append("<stop");
596            }
597        
598            public void dispose(Object component) {
599                recorder.append("<dispose");
600            }
601            
602            public boolean hasLifecycle(Class type) {
603                return true;
604            }
605            
606            public String recording() {
607                return recorder.toString();
608            }
609        }
610    
611        final protected PicoContainer wrapComponentInstances(
612                final Class decoratingComponentAdapterClass, final PicoContainer picoContainer,
613                final Object[] wrapperDependencies) {
614            assertTrue(DecoratingComponentAdapter.class.isAssignableFrom(decoratingComponentAdapterClass));
615            final MutablePicoContainer mutablePicoContainer = new DefaultPicoContainer();
616            final int size = (wrapperDependencies != null ? wrapperDependencies.length : 0) + 1;
617            final Collection allComponentAdapters = picoContainer.getComponentAdapters();
618            for (final Iterator iter = allComponentAdapters.iterator(); iter.hasNext();) {
619                final Parameter[] parameters = new Parameter[size];
620                parameters[0] = new ConstantParameter(iter.next());
621                for (int i = 1; i < parameters.length; i++) {
622                    parameters[i] = new ConstantParameter(wrapperDependencies[i - 1]);
623                }
624                final MutablePicoContainer instantiatingPicoContainer = new DefaultPicoContainer(
625                        new ConstructorInjectionComponentAdapterFactory());
626                instantiatingPicoContainer.registerComponentImplementation(
627                        "decorator", decoratingComponentAdapterClass, parameters);
628                mutablePicoContainer.registerComponent((ComponentAdapter)instantiatingPicoContainer
629                        .getComponentInstance("decorator"));
630            }
631            return mutablePicoContainer;
632        }
633    
634        private boolean supportsParameters(final Class type) {
635            boolean hasParameters = false;
636            final Constructor[] constructors = type.getConstructors();
637            for (int i = 0; i < constructors.length && !hasParameters; i++) {
638                final Constructor constructor = constructors[i];
639                final Class[] parameterTypes = constructor.getParameterTypes();
640                for (int j = 0; j < parameterTypes.length; j++) {
641                    final Class parameterType = parameterTypes[j];
642                    if (Parameter.class.isAssignableFrom(parameterType)
643                            || (parameterType.isArray() && Parameter.class.isAssignableFrom(parameterType
644                                    .getComponentType()))) {
645                        hasParameters = true;
646                        break;
647                    }
648                }
649            }
650            return hasParameters;
651        }
652    }