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