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 }