now allowing components and interfaces to be added after construction.
authorerik <erik@77661180-640e-0410-b3a8-9f9b13e6d0e0>
Sun, 13 Apr 2008 18:08:02 +0000 (18:08 +0000)
committererik <erik@77661180-640e-0410-b3a8-9f9b13e6d0e0>
Sun, 13 Apr 2008 18:08:02 +0000 (18:08 +0000)
Including call chaining.

support/general/src/test/java/org/wamblee/test/AssertionUtils.java
system/general/src/main/java/org/wamblee/system/core/AbstractComponent.java
system/general/src/main/java/org/wamblee/system/core/Container.java
system/general/src/test/java/org/wamblee/system/core/ContainerTest.java

index fc4040721d9aabeffac4017ad0b5f4907caa0529..767f679c9be45026010a959110afdf7328aaac54 100644 (file)
@@ -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);
+               }
+       }
+    }
 }
index b8e4e6be9b2f5276b041d44e9e61434c1c503ac0..9608f4179d2aca1a07b4ed4ed81d96d6217d9113 100644 (file)
@@ -58,7 +58,21 @@ public abstract class AbstractComponent<Type> implements Component<Type> {
                _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;
@@ -72,6 +86,10 @@ public abstract class AbstractComponent<Type> implements Component<Type> {
                        _context = aContext + "." + _context;
                }
        }
+       
+       public String getContext() {
+               return _context;
+       }
 
        @Override
        public String getQualifiedName() {
index f1b8b1ec0380fe4204f7f2021dd97c9bf8e53de9..a4b0620fccc589ab5fd48796aa3ca8ba34d17768 100644 (file)
@@ -33,7 +33,8 @@ public class Container extends AbstractComponent<Scope> {
 
        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,
@@ -59,7 +60,7 @@ public class Container extends AbstractComponent<Scope> {
        }
 
        /**
-        * Construcst the container
+        * Constructs the container
         * 
         * @param aName
         *            Name of the container
@@ -73,20 +74,46 @@ public class Container extends AbstractComponent<Scope> {
        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()));
@@ -177,8 +204,32 @@ public class Container extends AbstractComponent<Scope> {
                }
        }
        
-       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<Scope> {
 
        @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<Scope> {
                }
                return clients; 
        }
+       
+       private void checkSealed() { 
+               if ( _sealed ) { 
+                       throw new SystemAssemblyException("Container is sealed");
+               }
+       }
 }
index 74eb43101d0fa461b30e8bd4b31dcf25fd68f479..7eb66757772fb31d1746e6348855c242d1eaf94e 100644 (file)
@@ -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); 
+       }
 }