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