added default container to support classes directly.
authorErik Brakkee <erik@brakkee.org>
Tue, 15 Apr 2008 21:19:41 +0000 (21:19 +0000)
committerErik Brakkee <erik@brakkee.org>
Tue, 15 Apr 2008 21:19:41 +0000 (21:19 +0000)
Also added test cases for duplicate components and for adding components
that are already part of another hierarchy.

system/general/src/main/java/org/wamblee/system/adapters/DefaultContainer.java [new file with mode: 0644]
system/general/src/main/java/org/wamblee/system/core/AbstractComponent.java
system/general/src/main/java/org/wamblee/system/core/Component.java
system/general/src/main/java/org/wamblee/system/core/Container.java
system/general/src/main/java/org/wamblee/system/core/DefaultScope.java
system/general/src/main/java/org/wamblee/system/core/Scope.java
system/general/src/test/java/org/wamblee/system/adapters/DefaultContainerTest.java [new file with mode: 0644]
system/general/src/test/java/org/wamblee/system/core/Application.java
system/general/src/test/java/org/wamblee/system/core/ContainerTest.java

diff --git a/system/general/src/main/java/org/wamblee/system/adapters/DefaultContainer.java b/system/general/src/main/java/org/wamblee/system/adapters/DefaultContainer.java
new file mode 100644 (file)
index 0000000..c54206e
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2008 the original author or authors.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wamblee.system.adapters;
+
+import org.wamblee.system.core.Component;
+import org.wamblee.system.core.Container;
+import org.wamblee.system.core.ProvidedInterface;
+import org.wamblee.system.core.RequiredInterface;
+
+public class DefaultContainer extends Container {
+
+    public DefaultContainer(String aName) {
+        super(aName);
+    }
+
+    @Override
+    public DefaultContainer addComponent(Component aComponent) {
+        super.addComponent(aComponent);
+        return this;
+    }
+
+    public DefaultContainer addComponent(String aName, Class aClass) {
+        return addComponent(new ClassAdapter(aName, new ClassConfiguration(
+                aClass)));
+    }
+    
+    public DefaultContainer addComponent(String aName, ClassConfiguration aConfiguration) {
+        return addComponent(new ClassAdapter(aName, aConfiguration));
+    }
+
+    @Override
+    public DefaultContainer addRequiredInterface(RequiredInterface aRequired) {
+        super.addRequiredInterface(aRequired);
+        return this;
+    }
+
+    @Override
+    public DefaultContainer addProvidedInterface(ProvidedInterface aProvided) {
+        super.addProvidedInterface(aProvided);
+        return this;
+    }
+
+}
index 9608f4179d2aca1a07b4ed4ed81d96d6217d9113..28bb6ff19b28b0f93988695525f672d9fec35bb5 100644 (file)
@@ -63,12 +63,12 @@ public abstract class AbstractComponent<Type> implements Component<Type> {
                this(aName, new ProvidedInterface[0], new RequiredInterface[0]);
        }
        
-       protected AbstractComponent addProvidedInterface(ProvidedInterface aProvided) { 
+       public AbstractComponent addProvidedInterface(ProvidedInterface aProvided) { 
                _provided.add(aProvided);
                return this; 
        }
        
-       protected AbstractComponent addRequiredInterface(RequiredInterface aRequired) { 
+       public AbstractComponent addRequiredInterface(RequiredInterface aRequired) { 
                _required.add(aRequired);
                return this;
        }
@@ -87,6 +87,7 @@ public abstract class AbstractComponent<Type> implements Component<Type> {
                }
        }
        
+       @Override
        public String getContext() {
                return _context;
        }
index d14eb08a2364891205aef8647b47446e8df66f0a..48293d4518b88a35be628e928b11c350c30805a8 100644 (file)
@@ -42,6 +42,12 @@ public interface Component<Type> {
         */
        void addContext(String aContext);
        
+       /**
+        * Getst the context.
+        * @return Context or null if not set. 
+        */
+       String getContext();
+       
        /**
         * Gets the fully qualified name of the component which includes
         * the context of the component.  
index a4b0620fccc589ab5fd48796aa3ca8ba34d17768..b9056971136787a59fd09fc6f5de558ee6ed2fd6 100644 (file)
@@ -18,363 +18,397 @@ package org.wamblee.system.core;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Collection;
+import java.util.HashSet;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Set;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
 
 /**
- * Container consisting of multiple components. 
+ * Container consisting of multiple components.
  * 
  * @author Erik Brakkee
  */
 public class Container extends AbstractComponent<Scope> {
 
-       private static final Log LOG = LogFactory.getLog(Container.class);
-
-       private List<Component> _components;
-       private boolean _sealed; 
-
-       public static RequiredInterface[] filterRequiredServices(
-                       ProvidedInterface aProvided,
-                       Collection<RequiredInterface> aDescriptors) {
-               List<RequiredInterface> required = new ArrayList<RequiredInterface>();
-               for (RequiredInterface descriptor : aDescriptors) {
-                       if (descriptor.implementedBy(aProvided)) {
-                               required.add(descriptor);
-                       }
-               }
-               return required.toArray(new RequiredInterface[0]);
-       }
-
-       public static ProvidedInterface[] filterProvidedServices(
-                       RequiredInterface aRequired, Collection<ProvidedInterface> aProvided) {
-               List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
-               for (ProvidedInterface descriptor : aProvided) {
-                       if (aRequired.implementedBy(descriptor)) {
-                               provided.add(descriptor);
-                       }
-               }
-               return provided.toArray(new ProvidedInterface[0]);
-       }
-
-       /**
-        * Constructs the container
-        * 
-        * @param aName
-        *            Name of the container
-        * @param aComponents
-        *            Components.
-        * @param aProvided
-        *            Provided services of the container
-        * @param aRequired
-        *            Required services by the container.
-        */
-       public Container(String aName, Component[] aComponents,
-                       ProvidedInterface[] aProvided, RequiredInterface[] aRequired) {
-               super(aName, aProvided, aRequired);
-               _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. 
-        */
-       public void validate() {
-               List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
-               for (Component component : _components) {
-                       provided.addAll(Arrays.asList(component.getProvidedInterfaces()));
-               }
-
-               List<RequiredInterface> required = new ArrayList<RequiredInterface>();
-               for (Component component : _components) {
-                       required.addAll(Arrays.asList(component.getRequiredInterfaces()));
-               }
-
-               validateProvidedInterfaces(provided);
-
-               validateRequiredInterfaces(required);
-
-               List<RequiredInterface> reallyRequired = validateRequiredProvidedMatch(
-                               provided, required);
-               
-               String missingRequired = "";
-               for (RequiredInterface service : reallyRequired) {
-                       missingRequired += service + "\n";
-               }
-               if (missingRequired.length() > 0) {
-                       throw new SystemAssemblyException(getName()
-                                       + ": missing required services\n" + missingRequired);
-               }
-       }
-
-       private List<RequiredInterface> validateRequiredProvidedMatch(
-                       List<ProvidedInterface> aProvided, List<RequiredInterface> aRequired) {
-               List<RequiredInterface> reallyRequired = new ArrayList<RequiredInterface>(
-                               aRequired);
-               // Compute all required interfaces that are not provided
-
-               for (ProvidedInterface service : aProvided) {
-                       List<RequiredInterface> fulfilled = Arrays
-                                       .asList(filterRequiredServices(service, reallyRequired));
-                       reallyRequired.removeAll(fulfilled);
-               }
-               // Now remove all optional interfaces from the list.
-               for (Iterator<RequiredInterface> i = 
-                       reallyRequired.iterator(); i.hasNext(); ) { 
-                       RequiredInterface req = i.next();
-                       if ( req.isOptional() ) {
-                               i.remove(); 
-                       }
-               }
-               // Now the remaining interfaces should be covered by the required
-               // list.
-               reallyRequired.removeAll(Arrays.asList(getRequiredInterfaces()));
-               return reallyRequired;
-       }
-
-       private void validateRequiredInterfaces(List<RequiredInterface> aRequired) {
-               for (RequiredInterface service : getRequiredInterfaces()) {
-                       // TODO required interfaces by the component could be 
-                       //      subclasses or implementations of the requirements
-                       //      of the contained components. The code below assumes
-                       //      an exact match. 
-                       if (!(aRequired.contains(service))) {
-                               info("Service '"
-                                               + service
-                                               + "' indicated as required is not actually required by any of the components");
-                       }
-                       // Check for the case that the externally required service
-                       // is optional whereas the internally required service is 
-                       // mandatory. 
-                       if ( service.isOptional()) { 
-                               for (RequiredInterface intf: aRequired) { 
-                                       if ( intf.equals(service) && !intf.isOptional()) {  
-                                               warn("Required service '" + service + "' indicated as optional is mandatory by one of its components (" + getClients(intf) + ", " + intf + "), this can lead to problems when the container is started and the interface is not provided to the container.");
-                                       }
-                               }
-                       }
-               }
-       }
-
-       private void validateProvidedInterfaces(List<ProvidedInterface> aProvided) {
-               for (ProvidedInterface service : getProvidedInterfaces()) {
-                       // TODO provided interfaces by components could be 
-                       //      provide subclasses or implementations of the 
-                       //      provided interfaces of the container.
-                       //      The code below assumes an exact match. 
-                       if (!(aProvided.contains(service))) {
-                               throw new SystemAssemblyException(getName() + ": Service '"
-                                               + service
-                                               + "' is not provided by any of its components");
-                       }
-               }
-       }
-       
-       /**
-        * 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
-       protected Scope doStart(Scope aExternalScope) {
-               LOG.info("Starting '" + getQualifiedName() + "'");
-               
-               Scope scope = new DefaultScope(getProvidedInterfaces(), 
-                               aExternalScope);
-               
-               List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
-
-               // all interfaces from the required list of this container are
-               // provided to the components inside it.
-               RequiredInterface[] required = getRequiredInterfaces();
-               for (RequiredInterface intf : required) {
-                       ProvidedInterface provider = intf.getProvider();
-                       if (provider != null ) { 
-                               allProvided.add(provider);
-                       } else { 
-                               if ( !intf.isOptional()) { 
-                                       throw new SystemAssemblyException(getQualifiedName()
-                                                       + ": required interface '" + intf + "' is not provided");               
-                               }
-                       }
-               }
-
-               List<Component> started = new ArrayList<Component>();
-               for (Component component : _components) {
-                       try {
-                               checkAllRequiredServicesAlreadyProvided(allProvided, component);
-
-                               // Start the service.
-                               Object runtime = component.start(scope);
-                               scope.addRuntime(component, runtime); 
-                               started.add(component);
-
-                               // add all provided services
-                               ProvidedInterface[] provided = component.getProvidedInterfaces();
-                               allProvided.addAll(Arrays.asList(provided));
-                       } catch (SystemAssemblyException e) { 
-                               throw e; 
-                       } catch (RuntimeException e) {
-                               LOG.error(getQualifiedName() + ": could not start '"
-                                               + component.getQualifiedName() + "'", e);
-                               stopAlreadyStartedComponents(started, scope);
-                               throw e; 
-                       }
-               }
-               return scope; 
-       }
-
-       private void stopAlreadyStartedComponents(List<Component> aStarted, Scope aScope) {
-               // an exception occurred, stop the successfully started
-               // components
-               for (int i = aStarted.size() - 1; i >= 0; i--) {
-                       try {
-                               aStarted.get(i).stop(aScope);
-                       } catch (Throwable t) {
-                               LOG.error(getQualifiedName() + ": error stopping "
-                                               + aStarted.get(i).getQualifiedName());
-                       }
-               }
-       }
-
-       private void checkAllRequiredServicesAlreadyProvided(
-                       List<ProvidedInterface> aAllProvided, Component aComponent) {
-               // Check if all required services are already provided by
-               // earlier
-               // systems.
-
-               for (RequiredInterface descriptor : aComponent.getRequiredInterfaces()) {
-                       ProvidedInterface[] filtered = filterProvidedServices(
-                                       descriptor, aAllProvided);
-                       if ( filtered.length == 1 ) { 
-                               descriptor.setProvider(filtered[0]);
-                       } else if ( filtered.length > 1 ) { 
-                               throw new SystemAssemblyException(
-                                               "Service '"
-                                                               + descriptor
-                                                               + "' required by system '"
-                                                               + aComponent
-                                                               + "' matches multiple services provided by other systems: "
-                                                               + getServers(filtered));
-                       } else { 
-                               // filtered.length == 0
-                               if ( !descriptor.isOptional()) { 
-                                       throw new SystemAssemblyException(
-                                                       "Service '"
-                                                                       + descriptor
-                                                                       + "' required by system '"
-                                                                       + aComponent
-                                                                       + "' is not provided by systems that are started earlier");     
-                               }
-                       }
-               }
-       }
-
-       @Override
-       protected void doStop(Scope aScope) {
-               for (int i = _components.size() - 1; i >= 0; i--) {
-                       Component component = _components.get(i);
-                       Object runtime = aScope.getRuntime(component);
-                       component.stop(runtime);
-               }
-       }
-
-       private void info(String aMsg) {
-               LOG.info(getQualifiedName() + ": " + aMsg);
-       }
-       
-       private void warn(String aMsg) {
-               LOG.warn(getQualifiedName() + ": " + aMsg);
-       }
-       
-       private String getServers(ProvidedInterface[] aProvidedList ) {
-               String result = "";
-               for (ProvidedInterface provided: aProvidedList) {
-                       result += "(components ";
-                       for (Component component: _components) { 
-                               for (ProvidedInterface provided2: component.getProvidedInterfaces()) { 
-                                       if ( provided.equals(provided2)) { 
-                                               result += component + " ";
-                                       }
-                               }
-                       }
-                       result += ", interface " + provided + ")";
-               }
-               return result;
-       }
-
-       private List<Component> getClients(RequiredInterface aRequirement) {
-               List<Component> clients = new ArrayList<Component>();
-               for (Component component: _components) { 
-                       for (RequiredInterface required: component.getRequiredInterfaces()) { 
-                               if ( required.equals(aRequirement) && required.isOptional() == aRequirement.isOptional()) { 
-                                       clients.add(component);
-                               }
-                       }
-               }
-               return clients; 
-       }
-       
-       private void checkSealed() { 
-               if ( _sealed ) { 
-                       throw new SystemAssemblyException("Container is sealed");
-               }
-       }
+    private static final Log LOG = LogFactory.getLog(Container.class);
+
+    private List<Component> _components;
+    private Set<String> _componentNames;
+    private boolean _sealed;
+
+    public static RequiredInterface[] filterRequiredServices(
+            ProvidedInterface aProvided,
+            Collection<RequiredInterface> aDescriptors) {
+        List<RequiredInterface> required = new ArrayList<RequiredInterface>();
+        for (RequiredInterface descriptor : aDescriptors) {
+            if (descriptor.implementedBy(aProvided)) {
+                required.add(descriptor);
+            }
+        }
+        return required.toArray(new RequiredInterface[0]);
+    }
+
+    public static ProvidedInterface[] filterProvidedServices(
+            RequiredInterface aRequired, Collection<ProvidedInterface> aProvided) {
+        List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
+        for (ProvidedInterface descriptor : aProvided) {
+            if (aRequired.implementedBy(descriptor)) {
+                provided.add(descriptor);
+            }
+        }
+        return provided.toArray(new ProvidedInterface[0]);
+    }
+
+    /**
+     * Constructs the container
+     * 
+     * @param aName
+     *            Name of the container
+     * @param aComponents
+     *            Components.
+     * @param aProvided
+     *            Provided services of the container
+     * @param aRequired
+     *            Required services by the container.
+     */
+    public Container(String aName, Component[] aComponents,
+            ProvidedInterface[] aProvided, RequiredInterface[] aRequired) {
+        super(aName, aProvided, aRequired);
+        _components = new ArrayList<Component>();
+       
+        _componentNames = new HashSet<String>();
+        _sealed = false;
+        for (Component component : aComponents) {
+            addComponent(component);
+        }
+        validate();
+    }
+
+    public Container(String aName) {
+        this(aName, new Component[0], new ProvidedInterface[0],
+                new RequiredInterface[0]);
+    }
+
+    public Container addComponent(Component aComponent) {
+        checkSealed();
+        if (aComponent.getContext() != null) {
+            throw new SystemAssemblyException(
+                    "Inconsistent hierarchy, component '"
+                            + aComponent.getName()
+                            + "' is already part of another hierarchy");
+        }
+        if (_componentNames.contains(aComponent.getName())) {
+            throw new SystemAssemblyException("Duplicate component '"
+                    + aComponent.getName() + "'");
+        }
+        _components.add(aComponent);
+        _componentNames.add(aComponent.getName());
+        aComponent.addContext(getQualifiedName());
+        return this;
+    }
+
+    @Override
+    public Container addProvidedInterface(ProvidedInterface aProvided) {
+        checkSealed();
+        super.addProvidedInterface(aProvided);
+        return this;
+    }
+
+    @Override
+    public 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.
+     */
+    public void validate() {
+        List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
+        for (Component component : _components) {
+            provided.addAll(Arrays.asList(component.getProvidedInterfaces()));
+        }
+
+        List<RequiredInterface> required = new ArrayList<RequiredInterface>();
+        for (Component component : _components) {
+            required.addAll(Arrays.asList(component.getRequiredInterfaces()));
+        }
+
+        validateProvidedInterfaces(provided);
+
+        validateRequiredInterfaces(required);
+
+        List<RequiredInterface> reallyRequired = validateRequiredProvidedMatch(
+                provided, required);
+
+        String missingRequired = "";
+        for (RequiredInterface service : reallyRequired) {
+            missingRequired += service + "\n";
+        }
+        if (missingRequired.length() > 0) {
+            throw new SystemAssemblyException(getName()
+                    + ": missing required services\n" + missingRequired);
+        }
+    }
+
+    private List<RequiredInterface> validateRequiredProvidedMatch(
+            List<ProvidedInterface> aProvided, List<RequiredInterface> aRequired) {
+        List<RequiredInterface> reallyRequired = new ArrayList<RequiredInterface>(
+                aRequired);
+        // Compute all required interfaces that are not provided
+
+        for (ProvidedInterface service : aProvided) {
+            List<RequiredInterface> fulfilled = Arrays
+                    .asList(filterRequiredServices(service, reallyRequired));
+            reallyRequired.removeAll(fulfilled);
+        }
+        // Now remove all optional interfaces from the list.
+        for (Iterator<RequiredInterface> i = reallyRequired.iterator(); i
+                .hasNext();) {
+            RequiredInterface req = i.next();
+            if (req.isOptional()) {
+                i.remove();
+            }
+        }
+        // Now the remaining interfaces should be covered by the required
+        // list.
+        reallyRequired.removeAll(Arrays.asList(getRequiredInterfaces()));
+        return reallyRequired;
+    }
+
+    private void validateRequiredInterfaces(List<RequiredInterface> aRequired) {
+        for (RequiredInterface service : getRequiredInterfaces()) {
+            // TODO required interfaces by the component could be
+            // subclasses or implementations of the requirements
+            // of the contained components. The code below assumes
+            // an exact match.
+            if (!(aRequired.contains(service))) {
+                info("Service '"
+                        + service
+                        + "' indicated as required is not actually required by any of the components");
+            }
+            // Check for the case that the externally required service
+            // is optional whereas the internally required service is
+            // mandatory.
+            if (service.isOptional()) {
+                for (RequiredInterface intf : aRequired) {
+                    if (intf.equals(service) && !intf.isOptional()) {
+                        warn("Required service '"
+                                + service
+                                + "' indicated as optional is mandatory by one of its components ("
+                                + getClients(intf)
+                                + ", "
+                                + intf
+                                + "), this can lead to problems when the container is started and the interface is not provided to the container.");
+                    }
+                }
+            }
+        }
+    }
+
+    private void validateProvidedInterfaces(List<ProvidedInterface> aProvided) {
+        for (ProvidedInterface service : getProvidedInterfaces()) {
+            // TODO provided interfaces by components could be
+            // provide subclasses or implementations of the
+            // provided interfaces of the container.
+            // The code below assumes an exact match.
+            if (!(aProvided.contains(service))) {
+                throw new SystemAssemblyException(getName() + ": Service '"
+                        + service
+                        + "' is not provided by any of its components");
+            }
+        }
+    }
+
+    /**
+     * 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
+    protected Scope doStart(Scope aExternalScope) {
+        LOG.info("Starting '" + getQualifiedName() + "'");
+
+        Scope scope = new DefaultScope(getProvidedInterfaces(), aExternalScope);
+
+        List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
+
+        // all interfaces from the required list of this container are
+        // provided to the components inside it.
+        RequiredInterface[] required = getRequiredInterfaces();
+        for (RequiredInterface intf : required) {
+            ProvidedInterface provider = intf.getProvider();
+            if (provider != null) {
+                allProvided.add(provider);
+            } else {
+                if (!intf.isOptional()) {
+                    throw new SystemAssemblyException(getQualifiedName()
+                            + ": required interface '" + intf
+                            + "' is not provided");
+                }
+            }
+        }
+
+        List<Component> started = new ArrayList<Component>();
+        for (Component component : _components) {
+            try {
+                checkAllRequiredServicesAlreadyProvided(allProvided, component);
+
+                // Start the service.
+                Object runtime = component.start(scope);
+                scope.addRuntime(component, runtime);
+                started.add(component);
+
+                // add all provided services
+                ProvidedInterface[] provided = component
+                        .getProvidedInterfaces();
+                allProvided.addAll(Arrays.asList(provided));
+            } catch (SystemAssemblyException e) {
+                throw e;
+            } catch (RuntimeException e) {
+                LOG.error(getQualifiedName() + ": could not start '"
+                        + component.getQualifiedName() + "'", e);
+                stopAlreadyStartedComponents(started, scope);
+                throw e;
+            }
+        }
+        return scope;
+    }
+
+    private void stopAlreadyStartedComponents(List<Component> aStarted,
+            Scope aScope) {
+        // an exception occurred, stop the successfully started
+        // components
+        for (int i = aStarted.size() - 1; i >= 0; i--) {
+            try {
+                aStarted.get(i).stop(aScope);
+            } catch (Throwable t) {
+                LOG.error(getQualifiedName() + ": error stopping "
+                        + aStarted.get(i).getQualifiedName());
+            }
+        }
+    }
+
+    private void checkAllRequiredServicesAlreadyProvided(
+            List<ProvidedInterface> aAllProvided, Component aComponent) {
+        // Check if all required services are already provided by
+        // earlier
+        // systems.
+
+        for (RequiredInterface descriptor : aComponent.getRequiredInterfaces()) {
+            ProvidedInterface[] filtered = filterProvidedServices(descriptor,
+                    aAllProvided);
+            if (filtered.length == 1) {
+                descriptor.setProvider(filtered[0]);
+            } else if (filtered.length > 1) {
+                throw new SystemAssemblyException(
+                        "Service '"
+                                + descriptor
+                                + "' required by system '"
+                                + aComponent
+                                + "' matches multiple services provided by other systems: "
+                                + getServers(filtered));
+            } else {
+                // filtered.length == 0
+                if (!descriptor.isOptional()) {
+                    throw new SystemAssemblyException(
+                            "Service '"
+                                    + descriptor
+                                    + "' required by system '"
+                                    + aComponent
+                                    + "' is not provided by systems that are started earlier");
+                }
+            }
+        }
+    }
+
+    @Override
+    protected void doStop(Scope aScope) {
+        for (int i = _components.size() - 1; i >= 0; i--) {
+            Component component = _components.get(i);
+            Object runtime = aScope.getRuntime(component);
+            component.stop(runtime);
+        }
+    }
+
+    private void info(String aMsg) {
+        LOG.info(getQualifiedName() + ": " + aMsg);
+    }
+
+    private void warn(String aMsg) {
+        LOG.warn(getQualifiedName() + ": " + aMsg);
+    }
+
+    private String getServers(ProvidedInterface[] aProvidedList) {
+        String result = "";
+        for (ProvidedInterface provided : aProvidedList) {
+            result += "(components ";
+            for (Component component : _components) {
+                for (ProvidedInterface provided2 : component
+                        .getProvidedInterfaces()) {
+                    if (provided.equals(provided2)) {
+                        result += component + " ";
+                    }
+                }
+            }
+            result += ", interface " + provided + ")";
+        }
+        return result;
+    }
+
+    private List<Component> getClients(RequiredInterface aRequirement) {
+        List<Component> clients = new ArrayList<Component>();
+        for (Component component : _components) {
+            for (RequiredInterface required : component.getRequiredInterfaces()) {
+                if (required.equals(aRequirement)
+                        && required.isOptional() == aRequirement.isOptional()) {
+                    clients.add(component);
+                }
+            }
+        }
+        return clients;
+    }
+
+    private void checkSealed() {
+        if (_sealed) {
+            throw new SystemAssemblyException("Container is sealed");
+        }
+    }
 }
index b57f51f64f01a04d743f626fc6e90fd00fae4c39..5ec26223b416e842da3239add38c5fb65f803e43 100644 (file)
@@ -65,12 +65,17 @@ public class DefaultScope implements Scope {
 
        @Override
        public void addRuntime(Component aComponent, Object aRuntime) {
-               _runtimes.put(aComponent.getQualifiedName(), aRuntime);
+               _runtimes.put(aComponent.getName(), aRuntime);
        }
 
        @Override
        public Object getRuntime(Component aComponent) {
-               return _runtimes.get(aComponent.getQualifiedName());
+               return _runtimes.get(aComponent.getName());
+       }
+       
+       @Override
+       public Object getRuntime(String aName) {
+           return _runtimes.get(aName);
        }
 
        @Override
index 9db462f5c4393a873aaf3713272d67ee9f10a8e3..ea814da377ee14938b7a0d0c02a77f5409a06459 100644 (file)
@@ -73,5 +73,13 @@ public interface Scope {
         * @param aComponent Component for which we want to get the runtime.  
         * @return Runtime.  
         */
-       Object getRuntime(Component aComponent); 
+       Object getRuntime(Component aComponent);
+       
+       /**
+        * Gets the runtime for a component based on the name of the component
+        * (excluding its context). 
+        * @param aName Component name. 
+        * @return Component name. 
+        */
+       Object getRuntime(String aName);
 }
diff --git a/system/general/src/test/java/org/wamblee/system/adapters/DefaultContainerTest.java b/system/general/src/test/java/org/wamblee/system/adapters/DefaultContainerTest.java
new file mode 100644 (file)
index 0000000..913e397
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2008 the original author or authors.
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ * 
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.wamblee.system.adapters;
+
+import org.wamblee.system.core.Scope;
+import org.wamblee.test.AssertionUtils;
+
+public class DefaultContainerTest extends AdapterTestCase {
+
+    public void testConstructorInjection() {
+        ClassConfiguration x1Config = new ClassConfiguration(X1.class);
+        x1Config.getConstructorConfig().getParameters().setValue(0, "hello");
+
+        DefaultContainer container = new DefaultContainer("top").addComponent(
+                "x1", x1Config).addComponent("x4", X4.class);
+
+        Scope scope = container.start();
+        AssertionUtils.assertEquals(new String[] { "x1(hello)", "x4(x1)" },
+                EVENT_TRACKER.getEvents(Thread.currentThread()).toArray());
+
+        Object obj = scope.getRuntime("x1");
+        assertTrue(obj instanceof X1);
+        obj = scope.getRuntime("x4");
+        assertTrue(obj instanceof X4);
+    }
+}
index d1302f5852177582763f1cf1e0f85bdc4eb3a684..c162bb04400523af6b1b4f5c88efb8ad3542d948 100644 (file)
@@ -42,6 +42,11 @@ public class Application extends AbstractComponent {
                _random = Math.random();
        }
        
+       public Application(String aName) {
+        super(aName, new ProvidedInterface[0], required(false));
+        _random = Math.random();
+    }
+       
        public Application(boolean aIsOptinal) { 
                super("application", new ProvidedInterface[0], required(true)); 
        }
index 7eb66757772fb31d1746e6348855c242d1eaf94e..8b6c61e85fbe873feb02034a55154ed87d2b6efe 100644 (file)
@@ -215,6 +215,30 @@ public class ContainerTest extends TestCase {
                fail();
 
        }
+       
+       public void testDuplicateComponent() { 
+           try { 
+               Component comp1 = new Application(); 
+               Component comp2 = new Application();
+               Container system = new Container("top");
+               system.addComponent(comp1).addComponent(comp2);
+           } catch (SystemAssemblyException e) { 
+               return; 
+           }
+           fail();
+       }
+       
+
+       public void testInconsistentHierarchy() { 
+           try {
+               Component comp = new Application(); 
+               Container system = new Container("top").addComponent(comp);
+               Container system2 = new Container("top2").addComponent(comp);
+           } catch (SystemAssemblyException e) {
+               return;
+           }
+           fail();
+       }
 
        public void testCompositeWithExternalDependencesProvided() {
 
@@ -309,7 +333,7 @@ public class ContainerTest extends TestCase {
                
                // application 2 will throw an exception while starting
                Application application2 = control.createMock(Application.class,
-                               new ConstructorArgs(Application.class.getConstructor()),
+                               new ConstructorArgs(Application.class.getConstructor(String.class), "application2"),
                                Application.class.getDeclaredMethod("doStart", Scope.class));
 
                application2.doStart(EasyMockMatchers.anyObject(Scope.class));