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