From ce71ffcb89bd0ed7f1fc73cebec1b0e7659e6028 Mon Sep 17 00:00:00 2001 From: erik Date: Sun, 13 Apr 2008 18:08:02 +0000 Subject: [PATCH 1/1] now allowing components and interfaces to be added after construction. Including call chaining. --- .../java/org/wamblee/test/AssertionUtils.java | 23 ++++++ .../system/core/AbstractComponent.java | 20 ++++- .../org/wamblee/system/core/Container.java | 73 +++++++++++++++++-- .../wamblee/system/core/ContainerTest.java | 47 ++++++++++++ 4 files changed, 154 insertions(+), 9 deletions(-) diff --git a/support/general/src/test/java/org/wamblee/test/AssertionUtils.java b/support/general/src/test/java/org/wamblee/test/AssertionUtils.java index fc404072..767f679c 100644 --- a/support/general/src/test/java/org/wamblee/test/AssertionUtils.java +++ b/support/general/src/test/java/org/wamblee/test/AssertionUtils.java @@ -137,4 +137,27 @@ public final class AssertionUtils { aExpectedMap.get(key), aActual.get(key)); } } + + public static interface ErroneousCode { + void run() throws Exception; + } + + /** + * Asserts that an exception occurs. + * @param aRunnable Test cases should create a subclass of this which contains the + * code that should throw an exception. + * @param aType Type of exception that is expected. + */ + public static void assertException(ErroneousCode aObject, Class aType) { + try { + aObject.run(); + } catch (Throwable t) { + if ( aType.isInstance(t)) { + return; // ok + } + else { + throw new RuntimeException(t); + } + } + } } diff --git a/system/general/src/main/java/org/wamblee/system/core/AbstractComponent.java b/system/general/src/main/java/org/wamblee/system/core/AbstractComponent.java index b8e4e6be..9608f417 100644 --- a/system/general/src/main/java/org/wamblee/system/core/AbstractComponent.java +++ b/system/general/src/main/java/org/wamblee/system/core/AbstractComponent.java @@ -58,7 +58,21 @@ public abstract class AbstractComponent implements Component { _required = new ArrayList(); _required.addAll(Arrays.asList(aRequired)); } - + + protected AbstractComponent(String aName) { + this(aName, new ProvidedInterface[0], new RequiredInterface[0]); + } + + protected AbstractComponent addProvidedInterface(ProvidedInterface aProvided) { + _provided.add(aProvided); + return this; + } + + protected AbstractComponent addRequiredInterface(RequiredInterface aRequired) { + _required.add(aRequired); + return this; + } + @Override public final String getName() { return _name; @@ -72,6 +86,10 @@ public abstract class AbstractComponent implements Component { _context = aContext + "." + _context; } } + + public String getContext() { + return _context; + } @Override public String getQualifiedName() { diff --git a/system/general/src/main/java/org/wamblee/system/core/Container.java b/system/general/src/main/java/org/wamblee/system/core/Container.java index f1b8b1ec..a4b0620f 100644 --- a/system/general/src/main/java/org/wamblee/system/core/Container.java +++ b/system/general/src/main/java/org/wamblee/system/core/Container.java @@ -33,7 +33,8 @@ public class Container extends AbstractComponent { private static final Log LOG = LogFactory.getLog(Container.class); - private Component[] _components; + private List _components; + private boolean _sealed; public static RequiredInterface[] filterRequiredServices( ProvidedInterface aProvided, @@ -59,7 +60,7 @@ public class Container extends AbstractComponent { } /** - * Construcst the container + * Constructs the container * * @param aName * Name of the container @@ -73,20 +74,46 @@ public class Container extends AbstractComponent { public Container(String aName, Component[] aComponents, ProvidedInterface[] aProvided, RequiredInterface[] aRequired) { super(aName, aProvided, aRequired); - _components = aComponents; + _components = new ArrayList(Arrays.asList(aComponents)); for (Component component : aComponents) { component.addContext(getQualifiedName()); } + _sealed = false; validate(); } + + public Container(String aName) { + this(aName, new Component[0], new ProvidedInterface[0], new RequiredInterface[0]); + } + + public Container addComponent(Component aComponent) { + checkSealed(); + _components.add(aComponent); + return this; + } + @Override + protected Container addProvidedInterface(ProvidedInterface aProvided) { + checkSealed(); + super.addProvidedInterface(aProvided); + return this; + } + + @Override + protected Container addRequiredInterface(RequiredInterface aRequired) { + checkSealed(); + super.addRequiredInterface(aRequired); + return this; + } + /** * Validates the components together to check that there are no required * services not in the required list and no services in the provided list * that cannot be provided. Also logs a warning in case of superfluous * requirements. + * @throws SystemAssemblyException in case of any validation problems. */ - private void validate() { + public void validate() { List provided = new ArrayList(); for (Component component : _components) { provided.addAll(Arrays.asList(component.getProvidedInterfaces())); @@ -177,8 +204,32 @@ public class Container extends AbstractComponent { } } - public Scope start() { - return super.start(new DefaultScope(new ProvidedInterface[0])); + /** + * Starts the container. After the container is started, the container becomes sealed + * meaning that no further components, required or provided interfaces may be added. + * @return Scope. + */ + public Scope start() { + checkSealed(); + validate(); + Scope scope = super.start(new DefaultScope(new ProvidedInterface[0])); + seal(); + return scope; + } + + /** + * Seal the container, meaning that no further components or interfaces may be added. + */ + public void seal() { + _sealed = true; + } + + /** + * Checks if the container is sealed. + * @return True iff the container is sealed. + */ + public boolean isSealed() { + return _sealed; } @Override @@ -278,8 +329,8 @@ public class Container extends AbstractComponent { @Override protected void doStop(Scope aScope) { - for (int i = _components.length - 1; i >= 0; i--) { - Component component = _components[i]; + for (int i = _components.size() - 1; i >= 0; i--) { + Component component = _components.get(i); Object runtime = aScope.getRuntime(component); component.stop(runtime); } @@ -320,4 +371,10 @@ public class Container extends AbstractComponent { } return clients; } + + private void checkSealed() { + if ( _sealed ) { + throw new SystemAssemblyException("Container is sealed"); + } + } } diff --git a/system/general/src/test/java/org/wamblee/system/core/ContainerTest.java b/system/general/src/test/java/org/wamblee/system/core/ContainerTest.java index 74eb4310..7eb66757 100644 --- a/system/general/src/test/java/org/wamblee/system/core/ContainerTest.java +++ b/system/general/src/test/java/org/wamblee/system/core/ContainerTest.java @@ -15,6 +15,7 @@ */ package org.wamblee.system.core; +import java.io.IOException; import java.io.Serializable; import java.util.Arrays; @@ -84,6 +85,24 @@ public class ContainerTest extends TestCase { environment, application }, new ProvidedInterface[0], new RequiredInterface[0]); + Scope scope = container.start(); + assertTrue(container.isSealed()); + AssertionUtils.assertEquals(new String[] { "start.environment", + "start.application" }, _tracker.getEvents( + Thread.currentThread()).toArray(new String[0])); + assertEquals(0, scope.getProvidedInterfaces().length); + + assertEquals(environment.getString(), application.getString()); + assertEquals(environment.getInteger(), application.getInteger()); + + } + + public void testEnvironmentApplicationSimpleConstructor() { + Environment environment = new Environment(_tracker); + Application application = new Application(_tracker); + Container container = new Container("root"). + addComponent(environment).addComponent(application); + Scope scope = container.start(); AssertionUtils.assertEquals(new String[] { "start.environment", "start.application" }, _tracker.getEvents( @@ -363,4 +382,32 @@ public class ContainerTest extends TestCase { assertSame(env.getProvidedInterfaces()[0], application.getRequiredInterfaces()[0].getProvider()); assertSame(env.getProvidedInterfaces()[1], application.getRequiredInterfaces()[1].getProvider()); } + + public void testSealed() { + final Container container = new Container("xx"); + assertFalse(container.isSealed()); + container.start(); + assertTrue(container.isSealed()); + + AssertionUtils.assertException(new AssertionUtils.ErroneousCode() { + @Override + public void run() throws Exception { + container.addComponent(new Application()); + } + }, SystemAssemblyException.class); + + AssertionUtils.assertException(new AssertionUtils.ErroneousCode() { + @Override + public void run() throws Exception { + container.addProvidedInterface(new DefaultProvidedInterface("xx", String.class)); + } + }, SystemAssemblyException.class); + + AssertionUtils.assertException(new AssertionUtils.ErroneousCode() { + @Override + public void run() throws Exception { + container.addRequiredInterface(new DefaultRequiredInterface("xx", String.class)); + } + }, SystemAssemblyException.class); + } } -- 2.31.1