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