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