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 * 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.assertNull; 017 import static org.junit.Assert.assertSame; 018 import static org.junit.Assert.assertTrue; 019 import static org.junit.Assert.fail; 020 021 import java.io.ByteArrayInputStream; 022 import java.io.ByteArrayOutputStream; 023 import java.io.IOException; 024 import java.io.ObjectInputStream; 025 import java.io.ObjectOutputStream; 026 import java.io.Serializable; 027 import java.util.ArrayList; 028 import java.util.Arrays; 029 import java.util.Collection; 030 import java.util.HashMap; 031 import java.util.HashSet; 032 import java.util.LinkedList; 033 import java.util.List; 034 import java.util.Map; 035 import java.util.Properties; 036 import java.util.Set; 037 038 import org.junit.Test; 039 import org.picocontainer.Behavior; 040 import org.picocontainer.Characteristics; 041 import org.picocontainer.ComponentAdapter; 042 import org.picocontainer.ComponentFactory; 043 import org.picocontainer.DefaultPicoContainer; 044 import org.picocontainer.Disposable; 045 import org.picocontainer.MutablePicoContainer; 046 import org.picocontainer.NameBinding; 047 import org.picocontainer.Parameter; 048 import org.picocontainer.PicoCompositionException; 049 import org.picocontainer.PicoContainer; 050 import org.picocontainer.PicoException; 051 import org.picocontainer.PicoVerificationException; 052 import org.picocontainer.PicoVisitor; 053 import org.picocontainer.Startable; 054 import org.picocontainer.adapters.InstanceAdapter; 055 import org.picocontainer.behaviors.AbstractBehavior; 056 import org.picocontainer.behaviors.AdaptingBehavior; 057 import org.picocontainer.injectors.AbstractInjector; 058 import org.picocontainer.injectors.ConstructorInjector; 059 import org.picocontainer.lifecycle.NullLifecycleStrategy; 060 import org.picocontainer.monitors.NullComponentMonitor; 061 import org.picocontainer.parameters.BasicComponentParameter; 062 import org.picocontainer.parameters.ConstantParameter; 063 import org.picocontainer.testmodel.DependsOnTouchable; 064 import org.picocontainer.testmodel.SimpleTouchable; 065 import org.picocontainer.testmodel.Touchable; 066 import org.picocontainer.testmodel.Washable; 067 import org.picocontainer.testmodel.WashableTouchable; 068 import org.picocontainer.visitors.AbstractPicoVisitor; 069 import org.picocontainer.visitors.TraversalCheckingVisitor; 070 import org.picocontainer.visitors.VerifyingVisitor; 071 072 /** This test tests (at least it should) all the methods in MutablePicoContainer. */ 073 @SuppressWarnings("serial") 074 public abstract class AbstractPicoContainerTest { 075 076 protected abstract MutablePicoContainer createPicoContainer(PicoContainer parent); 077 078 protected final MutablePicoContainer createPicoContainerWithDependsOnTouchableOnly() throws PicoCompositionException { 079 MutablePicoContainer pico = createPicoContainer(null); 080 pico.addComponent(DependsOnTouchable.class); 081 return pico; 082 } 083 084 protected final MutablePicoContainer createPicoContainerWithTouchableAndDependsOnTouchable() throws PicoCompositionException { 085 MutablePicoContainer pico = createPicoContainerWithDependsOnTouchableOnly(); 086 pico.as(Characteristics.CACHE).addComponent(Touchable.class, SimpleTouchable.class); 087 return pico; 088 } 089 090 @Test public void testBasicInstantiationAndContainment() throws PicoException { 091 PicoContainer pico = createPicoContainerWithTouchableAndDependsOnTouchable(); 092 assertTrue("Component should be instance of Touchable", 093 Touchable.class.isAssignableFrom(pico.getComponentAdapter(Touchable.class, (NameBinding) null).getComponentImplementation())); 094 } 095 096 @Test public void testRegisteredComponentsExistAndAreTheCorrectTypes() throws PicoException { 097 PicoContainer pico = createPicoContainerWithTouchableAndDependsOnTouchable(); 098 assertNotNull("Container should have Touchable addComponent", 099 pico.getComponentAdapter(Touchable.class, (NameBinding) null)); 100 assertNotNull("Container should have DependsOnTouchable addComponent", 101 pico.getComponentAdapter(DependsOnTouchable.class, (NameBinding) null)); 102 assertTrue("Component should be instance of Touchable", 103 pico.getComponent(Touchable.class) != null); 104 assertTrue("Component should be instance of DependsOnTouchable", 105 pico.getComponent(DependsOnTouchable.class) != null); 106 assertNull("should not have non existent addComponent", pico.getComponentAdapter(Map.class, (NameBinding) null)); 107 } 108 109 @Test public void testRegistersSingleInstance() throws PicoException { 110 MutablePicoContainer pico = createPicoContainer(null); 111 StringBuffer sb = new StringBuffer(); 112 pico.addComponent(sb); 113 assertSame(sb, pico.getComponent(StringBuffer.class)); 114 } 115 116 @Test public void testContainerIsSerializable() throws PicoException, 117 IOException, ClassNotFoundException 118 { 119 120 getTouchableFromSerializedContainer(); 121 122 } 123 124 private Touchable getTouchableFromSerializedContainer() throws IOException, ClassNotFoundException { 125 MutablePicoContainer pico = createPicoContainerWithTouchableAndDependsOnTouchable(); 126 // Add a list too, using a constant parameter 127 pico.addComponent("list", ArrayList.class, new ConstantParameter(10)); 128 129 ByteArrayOutputStream baos = new ByteArrayOutputStream(); 130 ObjectOutputStream oos = new ObjectOutputStream(baos); 131 132 oos.writeObject(pico); 133 ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())); 134 135 pico = (MutablePicoContainer)ois.readObject(); 136 137 DependsOnTouchable dependsOnTouchable = pico.getComponent(DependsOnTouchable.class); 138 assertNotNull(dependsOnTouchable); 139 return pico.getComponent(Touchable.class); 140 } 141 142 @Test public void testSerializedContainerCanRetrieveImplementation() throws PicoException, 143 IOException, ClassNotFoundException 144 { 145 146 Touchable touchable = getTouchableFromSerializedContainer(); 147 148 SimpleTouchable simpleTouchable = (SimpleTouchable)touchable; 149 150 assertTrue(simpleTouchable.wasTouched); 151 } 152 153 154 @Test public void testGettingComponentWithMissingDependencyFails() throws PicoException { 155 PicoContainer picoContainer = createPicoContainerWithDependsOnTouchableOnly(); 156 try { 157 picoContainer.getComponent(DependsOnTouchable.class); 158 fail("should need a Touchable"); 159 } catch (AbstractInjector.UnsatisfiableDependenciesException e) { 160 assertSame(picoContainer.getComponentAdapter(DependsOnTouchable.class, (NameBinding) null).getComponentImplementation(), 161 e.getUnsatisfiableComponentAdapter().getComponentImplementation()); 162 final Set unsatisfiableDependencies = e.getUnsatisfiableDependencies(); 163 assertEquals(1, unsatisfiableDependencies.size()); 164 165 // Touchable.class is now inside a List (the list of unsatisfied parameters) -- mparaz 166 List unsatisfied = (List)unsatisfiableDependencies.iterator().next(); 167 assertEquals(1, unsatisfied.size()); 168 assertEquals(Touchable.class, unsatisfied.get(0)); 169 } 170 } 171 172 @Test public void testDuplicateRegistration() { 173 try { 174 MutablePicoContainer pico = createPicoContainer(null); 175 pico.addComponent(Object.class); 176 pico.addComponent(Object.class); 177 fail("Should have failed with duplicate registration"); 178 } catch (PicoCompositionException e) { 179 assertTrue("Wrong key", e.getMessage().indexOf(Object.class.toString()) > -1); 180 } 181 } 182 183 @Test public void testExternallyInstantiatedObjectsCanBeRegisteredAndLookedUp() throws PicoException { 184 MutablePicoContainer pico = createPicoContainer(null); 185 final HashMap map = new HashMap(); 186 pico.as(getProperties()).addComponent(Map.class, map); 187 assertSame(map, pico.getComponent(Map.class)); 188 } 189 190 @Test public void testAmbiguousResolution() throws PicoCompositionException { 191 MutablePicoContainer pico = createPicoContainer(null); 192 pico.addComponent("ping", String.class); 193 pico.addComponent("pong", "pang"); 194 try { 195 pico.getComponent(String.class); 196 } catch (AbstractInjector.AmbiguousComponentResolutionException e) { 197 assertTrue(e.getMessage().indexOf("java.lang.String") != -1); 198 assertTrue(e.getMessage().indexOf("<no-component>") != -1); 199 } 200 } 201 202 @Test public void testLookupWithUnregisteredKeyReturnsNull() throws PicoCompositionException { 203 MutablePicoContainer pico = createPicoContainer(null); 204 assertNull(pico.getComponent(String.class)); 205 } 206 207 @Test public void testLookupWithUnregisteredTypeReturnsNull() throws PicoCompositionException { 208 MutablePicoContainer pico = createPicoContainer(null); 209 assertNull(pico.getComponent(String.class)); 210 } 211 212 public static class ListAdder { 213 public ListAdder(Collection<String> list) { 214 list.add("something"); 215 } 216 } 217 218 @Test public void testUnsatisfiableDependenciesExceptionGivesVerboseEnoughErrorMessage() { 219 MutablePicoContainer pico = createPicoContainer(null); 220 pico.addComponent(ComponentD.class); 221 222 try { 223 pico.getComponent(ComponentD.class); 224 } catch (AbstractInjector.UnsatisfiableDependenciesException e) { 225 Set unsatisfiableDependencies = e.getUnsatisfiableDependencies(); 226 assertEquals(1, unsatisfiableDependencies.size()); 227 228 List list = (List)unsatisfiableDependencies.iterator().next(); 229 230 final List<Class> expectedList = new ArrayList<Class>(2); 231 expectedList.add(ComponentE.class); 232 expectedList.add(ComponentB.class); 233 234 assertEquals(expectedList, list); 235 } 236 } 237 238 @Test public void testUnsatisfiableDependenciesExceptionGivesUnsatisfiedDependencyTypes() { 239 MutablePicoContainer pico = createPicoContainer(null); 240 // D depends on E and B 241 pico.addComponent(ComponentD.class); 242 243 // first - do not register any dependency 244 // should yield first unsatisfied dependency 245 try { 246 pico.getComponent(ComponentD.class); 247 } catch (AbstractInjector.UnsatisfiableDependenciesException e) { 248 Set unsatisfiableDependencies = e.getUnsatisfiableDependencies(); 249 assertEquals(1, unsatisfiableDependencies.size()); 250 List list = (List)unsatisfiableDependencies.iterator().next(); 251 final List<Class> expectedList = new ArrayList<Class>(2); 252 expectedList.add(ComponentE.class); 253 expectedList.add(ComponentB.class); 254 assertEquals(expectedList, list); 255 256 Class unsatisfiedDependencyType = e.getUnsatisfiedDependencyType(); 257 assertNotNull(unsatisfiedDependencyType); 258 assertEquals(ComponentE.class, unsatisfiedDependencyType); 259 } 260 261 // now register only first dependency 262 // should yield second unsatisfied dependency 263 pico.addComponent(ComponentE.class); 264 try { 265 pico.getComponent(ComponentD.class); 266 } catch (AbstractInjector.UnsatisfiableDependenciesException e) { 267 Set unsatisfiableDependencies = e.getUnsatisfiableDependencies(); 268 assertEquals(1, unsatisfiableDependencies.size()); 269 List list = (List)unsatisfiableDependencies.iterator().next(); 270 final List<Class> expectedList = new ArrayList<Class>(2); 271 expectedList.add(ComponentE.class); 272 expectedList.add(ComponentB.class); 273 assertEquals(expectedList, list); 274 275 Class unsatisfiedDependencyType = e.getUnsatisfiedDependencyType(); 276 assertNotNull(unsatisfiedDependencyType); 277 assertEquals(ComponentB.class, unsatisfiedDependencyType); 278 } 279 } 280 281 @Test public void testCyclicDependencyThrowsCyclicDependencyException() { 282 assertCyclicDependencyThrowsCyclicDependencyException(createPicoContainer(null)); 283 } 284 285 private static void assertCyclicDependencyThrowsCyclicDependencyException(MutablePicoContainer pico) { 286 pico.addComponent(ComponentB.class); 287 pico.addComponent(ComponentD.class); 288 pico.addComponent(ComponentE.class); 289 290 try { 291 pico.getComponent(ComponentD.class); 292 fail("CyclicDependencyException expected"); 293 } catch (AbstractInjector.CyclicDependencyException e) { 294 // CyclicDependencyException reports now the stack. 295 //final List dependencies = Arrays.asList(ComponentD.class.getConstructors()[0].getParameterTypes()); 296 final List<Class> dependencies = Arrays.<Class>asList(ComponentD.class, ComponentE.class, ComponentD.class); 297 final List<Class> reportedDependencies = Arrays.asList(e.getDependencies()); 298 assertEquals(dependencies, reportedDependencies); 299 } catch (StackOverflowError e) { 300 fail(); 301 } 302 } 303 304 @Test public void testCyclicDependencyThrowsCyclicDependencyExceptionWithParentContainer() { 305 MutablePicoContainer pico = createPicoContainer(createPicoContainer(null)); 306 assertCyclicDependencyThrowsCyclicDependencyException(pico); 307 } 308 309 @Test public void testRemovalNonRegisteredComponentAdapterWorksAndReturnsNull() { 310 final MutablePicoContainer picoContainer = createPicoContainer(null); 311 assertNull(picoContainer.removeComponent("COMPONENT DOES NOT EXIST")); 312 } 313 314 /** Important! Nanning really, really depends on this! */ 315 @Test public void testComponentAdapterRegistrationOrderIsMaintained() throws NoSuchMethodException { 316 317 ConstructorInjector c1 = new ConstructorInjector("1", Object.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(), false); 318 ConstructorInjector c2 = new ConstructorInjector("2", String.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(), false); 319 320 MutablePicoContainer picoContainer = createPicoContainer(null); 321 picoContainer.addAdapter(c1).addAdapter(c2); 322 Collection<ComponentAdapter<?>> list2 = picoContainer.getComponentAdapters(); 323 //registration order should be maintained 324 assertEquals(2, list2.size()); 325 assertEquals(c1.getComponentKey(), ((ComponentAdapter)list2.toArray()[0]).getComponentKey()); 326 assertEquals(c2.getComponentKey(), ((ComponentAdapter)list2.toArray()[1]).getComponentKey()); 327 328 picoContainer.getComponents(); // create all the instances at once 329 assertFalse("instances should be created in same order as adapters are created", 330 picoContainer.getComponents().get(0) instanceof String); 331 assertTrue("instances should be created in same order as adapters are created", 332 picoContainer.getComponents().get(1) instanceof String); 333 334 MutablePicoContainer reversedPicoContainer = createPicoContainer(null); 335 reversedPicoContainer.addAdapter(c2); 336 reversedPicoContainer.addAdapter(c1); 337 //registration order should be maintained 338 list2 = reversedPicoContainer.getComponentAdapters(); 339 assertEquals(2, list2.size()); 340 assertEquals(c2.getComponentKey(), ((ComponentAdapter)list2.toArray()[0]).getComponentKey()); 341 assertEquals(c1.getComponentKey(), ((ComponentAdapter)list2.toArray()[1]).getComponentKey()); 342 343 reversedPicoContainer.getComponents(); // create all the instances at once 344 assertTrue("instances should be created in same order as adapters are created", 345 reversedPicoContainer.getComponents().get(0) instanceof String); 346 assertFalse("instances should be created in same order as adapters are created", 347 reversedPicoContainer.getComponents().get(1) instanceof String); 348 } 349 350 public static final class NeedsTouchable { 351 public final Touchable touchable; 352 353 public NeedsTouchable(Touchable touchable) { 354 this.touchable = touchable; 355 } 356 } 357 358 public static final class NeedsWashable { 359 public final Washable washable; 360 361 public NeedsWashable(Washable washable) { 362 this.washable = washable; 363 } 364 } 365 366 @Test public void testSameInstanceCanBeUsedAsDifferentTypeWhenCaching() { 367 MutablePicoContainer pico = createPicoContainer(null); 368 pico.as(Characteristics.CACHE).addComponent("wt", WashableTouchable.class); 369 pico.addComponent("nw", NeedsWashable.class); 370 pico.as(Characteristics.CACHE).addComponent("nt", NeedsTouchable.class); 371 372 NeedsWashable nw = (NeedsWashable)pico.getComponent("nw"); 373 NeedsTouchable nt = (NeedsTouchable)pico.getComponent("nt"); 374 assertSame(nw.washable, nt.touchable); 375 } 376 377 @Test public void testRegisterComponentWithObjectBadType() throws PicoCompositionException { 378 MutablePicoContainer pico = createPicoContainer(null); 379 380 try { 381 pico.addComponent(Serializable.class, new Object()); 382 fail("Shouldn't be able to register an Object.class as Serializable because it is not, " + 383 "it does not implement it, Object.class does not implement much."); 384 } catch (ClassCastException e) { 385 } 386 387 } 388 389 public static class JMSService { 390 public final String serverid; 391 public final String path; 392 393 public JMSService(String serverid, String path) { 394 this.serverid = serverid; 395 this.path = path; 396 } 397 } 398 399 // http://jira.codehaus.org/secure/ViewIssue.jspa?key=PICO-52 400 @Test public void testPico52() { 401 MutablePicoContainer pico = createPicoContainer(null); 402 403 pico.addComponent("foo", JMSService.class, new ConstantParameter("0"), new ConstantParameter("something")); 404 JMSService jms = (JMSService)pico.getComponent("foo"); 405 assertEquals("0", jms.serverid); 406 assertEquals("something", jms.path); 407 } 408 409 public static class ComponentA { 410 public final ComponentC c; 411 412 public ComponentA(ComponentB b, ComponentC c) { 413 this.c = c; 414 assertNotNull(b); 415 assertNotNull(c); 416 } 417 } 418 419 public static class ComponentB { 420 } 421 422 public static class ComponentC { 423 } 424 425 public static class ComponentD { 426 public ComponentD(ComponentE e, ComponentB b) { 427 assertNotNull(e); 428 assertNotNull(b); 429 } 430 } 431 432 public static class ComponentE { 433 public ComponentE(ComponentD d) { 434 assertNotNull(d); 435 } 436 } 437 438 public static class ComponentF { 439 public ComponentF(ComponentA a) { 440 assertNotNull(a); 441 } 442 } 443 444 @Test public void testAggregatedVerificationException() { 445 MutablePicoContainer pico = createPicoContainer(null); 446 pico.addComponent(ComponentA.class); 447 pico.addComponent(ComponentE.class); 448 try { 449 new VerifyingVisitor().traverse(pico); 450 fail("we expect a PicoVerificationException"); 451 } catch (PicoVerificationException e) { 452 List nested = e.getNestedExceptions(); 453 assertEquals(2, nested.size()); 454 assertTrue(-1 != e.getMessage().indexOf(ComponentA.class.getName())); 455 assertTrue(-1 != e.getMessage().indexOf(ComponentE.class.getName())); 456 } 457 } 458 459 // An adapter has no longer a hosting container. 460 461 // @Test public void testRegistrationOfAdapterSetsHostingContainerAsSelf() { 462 // final InstanceAdapter componentAdapter = new InstanceAdapter("", new Object()); 463 // final MutablePicoContainer picoContainer = createPicoContainer(null); 464 // picoContainer.addAdapter(componentAdapter); 465 // assertSame(picoContainer, componentAdapter.getContainer()); 466 // } 467 468 public static class ContainerDependency { 469 public ContainerDependency(PicoContainer container) { 470 assertNotNull(container); 471 } 472 } 473 474 // ImplicitPicoContainer injection is bad. It is an open door for hackers. Developers with 475 // special PicoContainer needs should specifically register() a comtainer they want components to 476 // be able to pick up on. 477 478 // @Test public void testImplicitPicoContainerInjection() { 479 // MutablePicoContainer pico = createPicoContainer(null); 480 // pico.addAdapter(ContainerDependency.class); 481 // ContainerDependency dep = (ContainerDependency) pico.getComponent(ContainerDependency.class); 482 // assertSame(pico, dep.pico); 483 // } 484 485 @Test public void testShouldReturnNullWhenUnregistereingUnmanagedComponent() { 486 final MutablePicoContainer pico = createPicoContainer(null); 487 assertNull(pico.removeComponentByInstance("yo")); 488 } 489 490 @Test public void testShouldReturnNullForComponentAdapterOfUnregisteredType() { 491 final MutablePicoContainer pico = createPicoContainer(null); 492 assertNull(pico.getComponent(List.class)); 493 } 494 495 @Test public void testShouldReturnNonMutableParent() { 496 DefaultPicoContainer parent = new DefaultPicoContainer(); 497 final MutablePicoContainer picoContainer = createPicoContainer(parent); 498 assertNotSame(parent, picoContainer.getParent()); 499 assertFalse(picoContainer.getParent() instanceof MutablePicoContainer); 500 } 501 502 class Foo implements Startable, Disposable { 503 public boolean started; 504 public boolean stopped; 505 public boolean disposed; 506 507 public void start() { 508 started = true; 509 } 510 511 public void stop() { 512 stopped = true; 513 } 514 515 public void dispose() { 516 disposed = true; 517 } 518 519 } 520 521 @Test public void testContainerCascadesDefaultLifecycle() { 522 final MutablePicoContainer picoContainer = createPicoContainer(null); 523 Foo foo = new Foo(); 524 picoContainer.addComponent(foo); 525 picoContainer.start(); 526 assertEquals(true, foo.started); 527 picoContainer.stop(); 528 assertEquals(true, foo.stopped); 529 picoContainer.dispose(); 530 assertEquals(true, foo.disposed); 531 } 532 533 @Test public void testComponentInstancesFromParentsAreNotDirectlyAccessible2() { 534 final MutablePicoContainer a = createPicoContainer(null); 535 final MutablePicoContainer b = createPicoContainer(a); 536 final MutablePicoContainer c = createPicoContainer(b); 537 538 Object ao = new Object(); 539 Object bo = new Object(); 540 Object co = new Object(); 541 542 a.addComponent("a", ao); 543 b.addComponent("b", bo); 544 c.addComponent("c", co); 545 546 assertEquals(1, a.getComponents().size()); 547 assertEquals(1, b.getComponents().size()); 548 assertEquals(1, c.getComponents().size()); 549 } 550 551 @Test public void testStartStopAndDisposeCascadedtoChildren() { 552 final MutablePicoContainer parent = createPicoContainer(null); 553 parent.addComponent(new StringBuffer()); 554 final MutablePicoContainer child = createPicoContainer(parent); 555 parent.addChildContainer(child); 556 child.addComponent(LifeCycleMonitoring.class); 557 parent.start(); 558 try { 559 child.start(); 560 fail("IllegalStateException expected"); 561 } catch (IllegalStateException e) { 562 assertEquals("child already started", "Cannot start. Current container state was: STARTED", e.getMessage()); 563 } 564 parent.stop(); 565 try { 566 child.stop(); 567 fail("IllegalStateException expected"); 568 } catch (IllegalStateException e) { 569 assertEquals("child not started", "Cannot stop. Current container state was: STOPPED", e.getMessage()); 570 } 571 parent.dispose(); 572 try { 573 child.dispose(); 574 fail("IllegalStateException expected"); 575 } catch (IllegalStateException e) { 576 assertEquals("child already disposed", "Cannot dispose. Current lifecycle state is: DISPOSED", e.getMessage()); 577 } 578 579 } 580 581 @Test public void testMakingOfChildContainer() { 582 final MutablePicoContainer parent = createPicoContainer(null); 583 MutablePicoContainer child = parent.makeChildContainer(); 584 assertNotNull(child); 585 } 586 587 @Test public void testMakingOfChildContainerPercolatesLifecycleManager() { 588 final MutablePicoContainer parent = createPicoContainer(null); 589 parent.addComponent("one", TestLifecycleComponent.class); 590 MutablePicoContainer child = parent.makeChildContainer(); 591 assertNotNull(child); 592 child.addComponent("two", TestLifecycleComponent.class); 593 parent.start(); 594 try { 595 child.start(); 596 } catch (IllegalStateException e) { 597 assertEquals("child already started", "Cannot start. Current container state was: STARTED", e.getMessage()); 598 } 599 //TODO - The Behavior reference in child containers is not used. Thus is is almost pointless 600 // The reason is because DefaultPicoContainer's accept() method visits child containers' on its own. 601 // This may be file for visiting components in a tree for general cases, but for lifecycle, we 602 // should hand to each Behavior's start(..) at each appropriate node. See mail-list discussion. 603 } 604 605 public static final class TestBehavior extends AbstractBehavior implements Behavior { 606 607 public final ArrayList<PicoContainer> started = new ArrayList<PicoContainer>(); 608 609 public TestBehavior(ComponentAdapter delegate) { 610 super(delegate); 611 } 612 613 public void start(PicoContainer node) { 614 started.add(node); 615 } 616 617 public void stop(PicoContainer node) { 618 } 619 620 public void dispose(PicoContainer node) { 621 } 622 623 public boolean componentHasLifecycle() { 624 return true; 625 } 626 627 public String getDescriptor() { 628 return null; 629 } 630 } 631 632 public static class TestLifecycleComponent implements Startable { 633 public boolean started; 634 635 public void start() { 636 started = true; 637 } 638 639 public void stop() { 640 } 641 } 642 643 @Test public void testStartStopAndDisposeNotCascadedtoRemovedChildren() { 644 final MutablePicoContainer parent = createPicoContainer(null); 645 parent.addComponent(new StringBuffer()); 646 StringBuffer sb = parent.getComponents(StringBuffer.class).get(0); 647 648 final MutablePicoContainer child = createPicoContainer(parent); 649 assertEquals(parent, parent.addChildContainer(child)); 650 child.addComponent(LifeCycleMonitoring.class); 651 assertTrue(parent.removeChildContainer(child)); 652 parent.start(); 653 assertTrue(sb.toString().indexOf("-started") == -1); 654 parent.stop(); 655 assertTrue(sb.toString().indexOf("-stopped") == -1); 656 parent.dispose(); 657 assertTrue(sb.toString().indexOf("-disposed") == -1); 658 } 659 660 @Test public void testShouldCascadeStartStopAndDisposeToChild() { 661 662 StringBuffer sb = new StringBuffer(); 663 final MutablePicoContainer parent = createPicoContainer(null); 664 parent.addComponent(sb); 665 parent.addComponent(Map.class, HashMap.class); 666 667 final MutablePicoContainer child = parent.makeChildContainer(); 668 child.addComponent(LifeCycleMonitoring.class); 669 670 Map map = parent.getComponent(Map.class); 671 assertNotNull(map); 672 parent.start(); 673 try { 674 child.start(); 675 fail("IllegalStateException expected"); 676 } catch (IllegalStateException e) { 677 assertEquals("child already started", "Cannot start. Current container state was: STARTED", e.getMessage()); 678 } 679 parent.stop(); 680 try { 681 child.stop(); 682 fail("IllegalStateException expected"); 683 } catch (IllegalStateException e) { 684 assertEquals("child not started", "Cannot stop. Current container state was: STOPPED", e.getMessage()); 685 } 686 parent.dispose(); 687 try { 688 child.dispose(); 689 fail("IllegalStateException expected"); 690 } catch (IllegalStateException e) { 691 assertEquals("child already disposed", "Cannot dispose. Current lifecycle state is: DISPOSED", e.getMessage()); 692 } 693 } 694 695 public static final class LifeCycleMonitoring implements Startable, Disposable { 696 final StringBuffer sb; 697 698 public LifeCycleMonitoring(StringBuffer sb) { 699 this.sb = sb; 700 sb.append("-instantiated"); 701 } 702 703 public void start() { 704 sb.append("-started"); 705 } 706 707 public void stop() { 708 sb.append("-stopped"); 709 } 710 711 public void dispose() { 712 sb.append("-disposed"); 713 } 714 } 715 716 public static class RecordingStrategyVisitor extends AbstractPicoVisitor { 717 718 private final List<Object> list; 719 720 public RecordingStrategyVisitor(List<Object> list) { 721 this.list = list; 722 } 723 724 public boolean visitContainer(PicoContainer pico) { 725 list.add(pico.getClass()); 726 return CONTINUE_TRAVERSAL; 727 } 728 729 public void visitComponentAdapter(ComponentAdapter componentAdapter) { 730 list.add(componentAdapter.getClass()); 731 } 732 733 public void visitComponentFactory(ComponentFactory componentFactory) { 734 list.add(componentFactory.getClass()); 735 } 736 737 public void visitParameter(Parameter parameter) { 738 list.add(parameter.getClass()); 739 } 740 741 } 742 743 protected abstract Properties[] getProperties(); 744 745 @Test public void testAcceptImplementsBreadthFirstStrategy() { 746 final MutablePicoContainer parent = createPicoContainer(null); 747 final MutablePicoContainer child = parent.makeChildContainer(); 748 ComponentAdapter hashMapAdapter = 749 parent.as(getProperties()).addAdapter(new ConstructorInjector(HashMap.class, HashMap.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(), false)) 750 .getComponentAdapter(HashMap.class, (NameBinding) null); 751 ComponentAdapter hashSetAdapter = 752 parent.as(getProperties()).addAdapter(new ConstructorInjector(HashSet.class, HashSet.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(), false)) 753 .getComponentAdapter(HashSet.class, (NameBinding) null); 754 InstanceAdapter instanceAdapter = new InstanceAdapter(String.class, "foo", 755 new NullLifecycleStrategy(), 756 new NullComponentMonitor()); 757 ComponentAdapter stringAdapter = parent.as(getProperties()).addAdapter(instanceAdapter).getComponentAdapter(instanceAdapter.getComponentKey()); 758 ComponentAdapter arrayListAdapter = 759 child.as(getProperties()).addAdapter(new ConstructorInjector(ArrayList.class, ArrayList.class, null, new NullComponentMonitor(), new NullLifecycleStrategy(), false)) 760 .getComponentAdapter(ArrayList.class, (NameBinding) null); 761 Parameter componentParameter = BasicComponentParameter.BASIC_DEFAULT; 762 Parameter throwableParameter = new ConstantParameter(new Throwable("bar")); 763 ConstructorInjector ci = new ConstructorInjector(Exception.class, Exception.class, new Parameter[] {componentParameter, 764 throwableParameter}, new NullComponentMonitor(), new NullLifecycleStrategy(), false); 765 ComponentAdapter exceptionAdapter = child.as(getProperties()).addAdapter(ci).getComponentAdapter(Exception.class, (NameBinding) null); 766 767 List<Class> expectedList = new ArrayList<Class>(); 768 769 addContainers(expectedList); 770 addDefaultComponentFactories(expectedList); 771 expectedList.add(hashMapAdapter.getClass()); 772 expectedList.add(hashSetAdapter.getClass()); 773 expectedList.add(stringAdapter.getClass()); 774 addContainers(expectedList); 775 addDefaultComponentFactories(expectedList); 776 expectedList.add(arrayListAdapter.getClass()); 777 expectedList.add(exceptionAdapter.getClass()); 778 expectedList.add(componentParameter.getClass()); 779 expectedList.add(throwableParameter.getClass()); 780 List<Object> visitedList = new LinkedList<Object>(); 781 PicoVisitor visitor = new RecordingStrategyVisitor(visitedList); 782 visitor.traverse(parent); 783 assertEquals(expectedList.size(), visitedList.size()); 784 for (Class c : expectedList) { 785 assertTrue(visitedList.remove(c)); 786 } 787 assertEquals(0, visitedList.size()); 788 } 789 790 /** 791 * Verifies that you can halt a container traversal. 792 */ 793 @Test 794 public void testAcceptIsAbortable() { 795 final MutablePicoContainer parent = createPicoContainer(null); 796 final MutablePicoContainer child = parent.makeChildContainer(); 797 child.addComponent("This is a test"); 798 799 TraversalCheckingVisitor parentComponentCountingVisitor = new TraversalCheckingVisitor() { 800 private int containerCount = 0; 801 802 private int componentInParentCount = 0; 803 804 @Override 805 public void visitComponentAdapter(ComponentAdapter<?> componentAdapter) { 806 if (containerCount == 0) { 807 fail("Should have visited a container first"); 808 } 809 fail("Should never have visited an adapter."); 810 } 811 812 @Override 813 public boolean visitContainer(PicoContainer pico) { 814 containerCount++; 815 if (containerCount > 1) { 816 return ABORT_TRAVERSAL; 817 } 818 819 return CONTINUE_TRAVERSAL; 820 } 821 822 }; 823 824 parentComponentCountingVisitor.traverse(parent); 825 } 826 827 protected void addContainers(List expectedList) { 828 expectedList.add(DefaultPicoContainer.class); 829 } 830 831 protected void addDefaultComponentFactories(List expectedList) { 832 expectedList.add(AdaptingBehavior.class); 833 } 834 835 @Test public void testAmbiguousDependencies() throws PicoCompositionException { 836 837 MutablePicoContainer pico = this.createPicoContainer(null); 838 839 // Register two Touchables that Fred will be confused about 840 pico.addComponent(SimpleTouchable.class); 841 pico.addComponent(DerivedTouchable.class); 842 843 // Register a confused DependsOnTouchable 844 pico.addComponent(DependsOnTouchable.class); 845 846 try { 847 pico.getComponent(DependsOnTouchable.class); 848 fail("DependsOnTouchable should have been confused about the two Touchables"); 849 } catch (AbstractInjector.AmbiguousComponentResolutionException e) { 850 List componentImplementations = Arrays.asList(e.getAmbiguousComponentKeys()); 851 assertTrue(componentImplementations.contains(DerivedTouchable.class)); 852 assertTrue(componentImplementations.contains(SimpleTouchable.class)); 853 854 assertTrue(e.getMessage().indexOf(DerivedTouchable.class.getName()) != -1); 855 } 856 } 857 858 859 public static class DerivedTouchable extends SimpleTouchable { 860 public DerivedTouchable() { 861 } 862 } 863 864 865 public static final class NonGreedyClass { 866 867 public final int value = 0; 868 869 public NonGreedyClass() { 870 //Do nothing. 871 } 872 873 public NonGreedyClass(ComponentA component) { 874 fail("Greedy Constructor should never have been called. Instead got: " + component); 875 } 876 877 878 } 879 880 @Test public void testNoArgConstructorToBeSelected() { 881 MutablePicoContainer pico = this.createPicoContainer(null); 882 pico.addComponent(ComponentA.class); 883 pico.addComponent(NonGreedyClass.class, NonGreedyClass.class, Parameter.ZERO); 884 885 886 NonGreedyClass instance = pico.getComponent(NonGreedyClass.class); 887 assertNotNull(instance); 888 } 889 890 }