X-Git-Url: http://wamblee.org/gitweb/?a=blobdiff_plain;f=system%2Fgeneral%2Fsrc%2Fmain%2Fjava%2Forg%2Fwamblee%2Fsystem%2Fcore%2FContainer.java;h=a4b0620fccc589ab5fd48796aa3ca8ba34d17768;hb=ce71ffcb89bd0ed7f1fc73cebec1b0e7659e6028;hp=ec45283e2d0fff00b1bc711658d80f5310414994;hpb=f4bb0b383ded9c7303a5d81cc6ee87df530d6716;p=utils 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 ec45283e..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 @@ -12,29 +12,29 @@ * 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.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; /** - * Composite system consisting of multiple subsystems. - * + * Container consisting of multiple components. + * * @author Erik Brakkee */ -public class Container extends AbstractComponent { +public class Container extends AbstractComponent { private static final Log LOG = LogFactory.getLog(Container.class); - private Component[] _systems; + private List _components; + private boolean _sealed; public static RequiredInterface[] filterRequiredServices( ProvidedInterface aProvided, @@ -49,8 +49,7 @@ public class Container extends AbstractComponent { } public static ProvidedInterface[] filterProvidedServices( - RequiredInterface aRequired, - Collection aProvided) { + RequiredInterface aRequired, Collection aProvided) { List provided = new ArrayList(); for (ProvidedInterface descriptor : aProvided) { if (aRequired.implementedBy(descriptor)) { @@ -59,162 +58,323 @@ public class Container extends AbstractComponent { } return provided.toArray(new ProvidedInterface[0]); } - + /** - * Construcst the composite system. - * @param aName Name of the system. - * @param aRegistry Service registry. - * @param aSystems Subsystems. - * @param aProvided Provided services of the system. - * @param aRequired Required services by the system. + * 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[] aSystems, + public Container(String aName, Component[] aComponents, ProvidedInterface[] aProvided, RequiredInterface[] aRequired) { super(aName, aProvided, aRequired); - _systems = aSystems; - for (Component component: aSystems) { + _components = new ArrayList(Arrays.asList(aComponents)); + for (Component component : aComponents) { component.addContext(getQualifiedName()); } - validate(aRequired); + _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 subsystems 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. + * 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(RequiredInterface[] aRequired) { + public void validate() { List provided = new ArrayList(); - for (Component system : _systems) { - provided.addAll(Arrays.asList(system.getProvidedInterfaces())); + for (Component component : _components) { + provided.addAll(Arrays.asList(component.getProvidedInterfaces())); } List required = new ArrayList(); - for (Component system : _systems) { - required.addAll(Arrays.asList(system.getRequiredInterfaces())); + for (Component component : _components) { + required.addAll(Arrays.asList(component.getRequiredInterfaces())); } - for (ProvidedInterface service : getProvidedInterfaces()) { - if (!(provided.contains(service))) { - throw new SystemAssemblyException(getName() + ": Service '" + service - + "' is not provided by any of the subsystems"); + validateProvidedInterfaces(provided); + + validateRequiredInterfaces(required); + + List 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 validateRequiredProvidedMatch( + List aProvided, List aRequired) { + List reallyRequired = new ArrayList( + aRequired); + // Compute all required interfaces that are not provided + + for (ProvidedInterface service : aProvided) { + List fulfilled = Arrays + .asList(filterRequiredServices(service, reallyRequired)); + reallyRequired.removeAll(fulfilled); + } + // Now remove all optional interfaces from the list. + for (Iterator 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 aRequired) { for (RequiredInterface service : getRequiredInterfaces()) { - if (!(required.contains(service))) { + // 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 subsystems"); + + "' 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."); + } + } } } + } - List reallyRequired = new ArrayList( - required); - // Compute all required interfaces that are not provided - for (ProvidedInterface service : provided) { - List fulfilled = - Arrays.asList(filterRequiredServices(service, - reallyRequired)); - reallyRequired.removeAll(fulfilled); - } - // Now the remaining interfaces should be covered by the required - // list. - reallyRequired.removeAll(Arrays.asList(aRequired)); - - String missingRequired = ""; - for (RequiredInterface service: reallyRequired) { - missingRequired += service + "\n"; - } - if ( missingRequired.length() > 0 ) { - throw new SystemAssemblyException(getName() + ": missing required services\n" + missingRequired); + private void validateProvidedInterfaces(List 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 void doStart() { - List provided = new ArrayList(); + protected Scope doStart(Scope aExternalScope) { + LOG.info("Starting '" + getQualifiedName() + "'"); + Scope scope = new DefaultScope(getProvidedInterfaces(), + aExternalScope); + + List allProvided = new ArrayList(); + // 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 ) { - throw new SystemAssemblyException(getQualifiedName() + ": required interface '" + intf +"' is not provided"); - } - provided.add(intf.getProvider()); + 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"); + } + } } - - startImpl(); + + List started = new ArrayList(); + 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; } - - /** - * Starts the subsystems. - * - * @param aRequiredServices - * Services that are available from other systems that have been - * started before. - */ - private void startImpl() { - LOG.info("Starting '" + getQualifiedName() + "'"); - List allProvided = new ArrayList(); - - // Add the provides of all externally required interfaces to the list of available - // interfaces - for (RequiredInterface required: getRequiredInterfaces()) { - allProvided.add(required.getProvider()); + + private void stopAlreadyStartedComponents(List 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()); + } } - - for (Component system : _systems) { - // Check if all required services are already provided by earlier - // systems. - RequiredInterface[] required = system.getRequiredInterfaces(); + } - for (RequiredInterface descriptor : required) { - ProvidedInterface[] filtered = filterProvidedServices( - descriptor, allProvided); + private void checkAllRequiredServicesAlreadyProvided( + List aAllProvided, Component aComponent) { + // Check if all required services are already provided by + // earlier + // systems. - if (filtered.length == 0) { - throw new SystemAssemblyException( - "Service '" - + descriptor - + "' required by system '" - + system - + "' is not provided by systems that are started earlier"); - } - if (filtered.length > 1) { + 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 '" - + system - + "' matches multiple services provided by other systems: " + - Arrays.asList(filtered)); + + aComponent + + "' is not provided by systems that are started earlier"); } - descriptor.setProvider(filtered[0]); } - - // Start the service. - system.start(); - - // add all provided services - ProvidedInterface[] provided = system.getProvidedInterfaces(); - allProvided.addAll(Arrays.asList(provided)); } } - @Override - protected void doStop() { - for (int i = _systems.length-1; i >= 0; i--) { - _systems[i].stop(); + 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(getName() + ": " + 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 getClients(RequiredInterface aRequirement) { + List clients = new ArrayList(); + 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"); + } + } }