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 *****************************************************************************/ 010package org.picocontainer.tck; 011 012import static org.junit.Assert.assertEquals; 013import static org.junit.Assert.assertFalse; 014import static org.junit.Assert.assertNotNull; 015import static org.junit.Assert.assertNotSame; 016import static org.junit.Assert.assertSame; 017import static org.junit.Assert.assertTrue; 018import static org.junit.Assert.fail; 019 020import java.io.ByteArrayInputStream; 021import java.io.ByteArrayOutputStream; 022import java.io.IOException; 023import java.io.ObjectInputStream; 024import java.io.ObjectOutputStream; 025import java.lang.reflect.Constructor; 026import java.lang.reflect.Type; 027import java.util.ArrayList; 028import java.util.Collection; 029import java.util.HashSet; 030import java.util.Iterator; 031import java.util.LinkedList; 032import java.util.List; 033import java.util.Set; 034 035import junit.framework.Assert; 036import junit.framework.AssertionFailedError; 037 038import org.junit.Test; 039import org.picocontainer.ComponentAdapter; 040import org.picocontainer.ComponentFactory; 041import org.picocontainer.DefaultPicoContainer; 042import org.picocontainer.LifecycleStrategy; 043import org.picocontainer.MutablePicoContainer; 044import org.picocontainer.ObjectReference; 045import org.picocontainer.Parameter; 046import org.picocontainer.PicoCompositionException; 047import org.picocontainer.PicoContainer; 048import org.picocontainer.behaviors.AbstractBehavior; 049import org.picocontainer.injectors.AbstractInjector; 050import org.picocontainer.injectors.AdaptingInjection; 051import org.picocontainer.injectors.ConstructorInjection; 052import org.picocontainer.parameters.ConstantParameter; 053import org.picocontainer.references.SimpleReference; 054import org.picocontainer.visitors.AbstractPicoVisitor; 055 056import com.thoughtworks.xstream.XStream; 057import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider; 058import 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") 066public 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}