Including call chaining.
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);
+ }
+ }
+ }
}
_required = new ArrayList<RequiredInterface>();
_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;
_context = aContext + "." + _context;
}
}
+
+ public String getContext() {
+ return _context;
+ }
@Override
public String getQualifiedName() {
private static final Log LOG = LogFactory.getLog(Container.class);
- private Component[] _components;
+ private List<Component> _components;
+ private boolean _sealed;
public static RequiredInterface[] filterRequiredServices(
ProvidedInterface aProvided,
}
/**
- * Construcst the container
+ * Constructs the container
*
* @param aName
* Name of the container
public Container(String aName, Component[] aComponents,
ProvidedInterface[] aProvided, RequiredInterface[] aRequired) {
super(aName, aProvided, aRequired);
- _components = aComponents;
+ _components = new ArrayList<Component>(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<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
for (Component component : _components) {
provided.addAll(Arrays.asList(component.getProvidedInterfaces()));
}
}
- 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
@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);
}
}
return clients;
}
+
+ private void checkSealed() {
+ if ( _sealed ) {
+ throw new SystemAssemblyException("Container is sealed");
+ }
+ }
}
*/
package org.wamblee.system.core;
+import java.io.IOException;
import java.io.Serializable;
import java.util.Arrays;
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(
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);
+ }
}