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