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