/***************************************************************************** * Copyright (c) PicoContainer Organization. All rights reserved. * * ------------------------------------------------------------------------- * * The software in this package is published under the terms of the BSD * * style license a copy of which has been included with this distribution in * * the license.html file. * * * * Idea by Rachel Davies, Original code by Aslak Hellesoy and Paul Hammant * *****************************************************************************/ package org.picocontainer.defaults; import junit.framework.Assert; import junit.framework.TestCase; import org.picocontainer.Disposable; import org.picocontainer.MutablePicoContainer; import org.picocontainer.PicoContainer; import org.picocontainer.Startable; import java.util.List; /** * This class tests the lifecycle aspects of DefaultPicoContainer. */ public class DefaultPicoContainerLifecycleTestCase extends TestCase { public abstract static class RecordingLifecycle implements Startable, Disposable { private final StringBuffer recording; protected RecordingLifecycle(StringBuffer recording) { this.recording = recording; } public void start() { recording.append("<" + code()); } public void stop() { recording.append(code() + ">"); } public void dispose() { recording.append("!" + code()); } private String code() { String name = getClass().getName(); return name.substring(name.indexOf('$') + 1); } } public static class One extends RecordingLifecycle { public One(StringBuffer sb) { super(sb); } } public static class Two extends RecordingLifecycle { public Two(StringBuffer sb, One one) { super(sb); } } public static class Three extends RecordingLifecycle { public Three(StringBuffer sb, One one, Two two) { super(sb); } } public static class Four extends RecordingLifecycle { public Four(StringBuffer sb, Two two, Three three, One one) { super(sb); } } public void testOrderOfInstantiationShouldBeDependencyOrder() throws Exception { DefaultPicoContainer pico = new DefaultPicoContainer(); pico.registerComponentImplementation("recording", StringBuffer.class); pico.registerComponentImplementation(Four.class); pico.registerComponentImplementation(Two.class); pico.registerComponentImplementation(One.class); pico.registerComponentImplementation(Three.class); final List componentInstances = pico.getComponentInstances(); // instantiation - would be difficult to do these in the wrong order!! assertEquals("Incorrect Order of Instantiation", One.class, componentInstances.get(1).getClass()); assertEquals("Incorrect Order of Instantiation", Two.class, componentInstances.get(2).getClass()); assertEquals("Incorrect Order of Instantiation", Three.class, componentInstances.get(3).getClass()); assertEquals("Incorrect Order of Instantiation", Four.class, componentInstances.get(4).getClass()); } public void testOrderOfStartShouldBeDependencyOrderAndStopAndDisposeTheOpposite() throws Exception { DefaultPicoContainer pico = new DefaultPicoContainer(); pico.registerComponentImplementation("recording", StringBuffer.class); pico.registerComponentImplementation(Four.class); pico.registerComponentImplementation(Two.class); pico.registerComponentImplementation(One.class); pico.registerComponentImplementation(Three.class); pico.start(); pico.stop(); pico.dispose(); assertEquals("Three>Two>One>!Four!Three!Two!One", pico.getComponentInstance("recording").toString()); } public void testStartStartShouldFail() throws Exception { DefaultPicoContainer pico = new DefaultPicoContainer(); pico.start(); try { pico.start(); fail("Should have failed"); } catch (IllegalStateException e) { // expected; } } public void testStartStopStopShouldFail() throws Exception { DefaultPicoContainer pico = new DefaultPicoContainer(); pico.start(); pico.stop(); try { pico.stop(); fail("Should have failed"); } catch (IllegalStateException e) { // expected; } } public void testStartStopDisposeDisposeShouldFail() throws Exception { DefaultPicoContainer pico = new DefaultPicoContainer(); pico.start(); pico.stop(); pico.dispose(); try { pico.dispose(); fail("Should have barfed"); } catch (IllegalStateException e) { // expected; } } public static class FooRunnable implements Runnable, Startable { private int runCount; private Thread thread = new Thread(); private boolean interrupted; public FooRunnable() { } public int runCount() { return runCount; } public boolean isInterrupted() { return interrupted; } public void start() { thread = new Thread(this); thread.start(); } public void stop() { thread.interrupt(); } // this would do something a bit more concrete // than counting in real life ! public void run() { runCount++; try { Thread.sleep(10000); } catch (InterruptedException e) { interrupted = true; } } } public void testStartStopOfDaemonizedThread() throws Exception { DefaultPicoContainer pico = new DefaultPicoContainer(); pico.registerComponentImplementation(FooRunnable.class); pico.getComponentInstances(); pico.start(); Thread.sleep(100); pico.stop(); FooRunnable foo = (FooRunnable) pico.getComponentInstance(FooRunnable.class); assertEquals(1, foo.runCount()); pico.start(); Thread.sleep(100); pico.stop(); assertEquals(2, foo.runCount()); } // This is the basic functionality for starting of child containers public void testDefaultPicoContainerRegisteredAsComponentGetsHostingContainerAsParent() { MutablePicoContainer parent = new DefaultPicoContainer(); parent.registerComponentImplementation("child", DefaultPicoContainer.class); PicoContainer child = (PicoContainer) parent.getComponentInstance("child"); assertSame(parent, child.getParent()); } public void testGetComponentInstancesOnParentContainerHostedChildContainerDoesntReturnParentAdapter() { MutablePicoContainer parent = new DefaultPicoContainer(); parent.registerComponentImplementation("child", DefaultPicoContainer.class); DefaultPicoContainer child = (DefaultPicoContainer) parent.getComponentInstance("child"); assertEquals(0, child.getComponentInstances().size()); } public void testComponentsAreStartedBreadthFirstAndStoppedAndDisposedDepthFirst() { // // // // // // // MutablePicoContainer parent = new DefaultPicoContainer(); parent.registerComponentImplementation(Two.class); parent.registerComponentImplementation("recording", StringBuffer.class); parent.registerComponentImplementation(One.class); parent.registerComponentImplementation("child", DefaultPicoContainer.class); DefaultPicoContainer child = (DefaultPicoContainer) parent.getComponentInstance("child"); child.registerComponentImplementation(Three.class); parent.start(); parent.stop(); parent.dispose(); assertEquals("Two>One>!Three!Two!One", parent.getComponentInstance("recording").toString()); } public static class NotStartable { public NotStartable() { Assert.fail("Shouldn't be instantiated"); } } public void testOnlyStartableComponentsAreInstantiatedOnStart() { MutablePicoContainer pico = new DefaultPicoContainer(); pico.registerComponentImplementation("recording", StringBuffer.class); pico.registerComponentImplementation(One.class); pico.registerComponentImplementation(NotStartable.class); pico.start(); pico.stop(); pico.dispose(); assertEquals("!One", pico.getComponentInstance("recording").toString()); } }