X-Git-Url: http://wamblee.org/gitweb/?a=blobdiff_plain;f=system%2Fgeneral%2Fsrc%2Fmain%2Fjava%2Forg%2Fwamblee%2Fsystem%2Fcore%2FContainer.java;h=f1b8b1ec0380fe4204f7f2021dd97c9bf8e53de9;hb=fc10e15e031cf409146b04cbb5e98b649c6ccddc;hp=b7ee4e60c6ddcb61f0fed6c52b911cfb37fb7998;hpb=323116ae80daecd6e064abe014a080c5cd03b123;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 b7ee4e60..f1b8b1ec 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 @@ -18,23 +18,22 @@ 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 Component[] _components; public static RequiredInterface[] filterRequiredServices( ProvidedInterface aProvided, @@ -60,190 +59,265 @@ public class Container extends AbstractComponent { } /** - * Construcst the composite system. + * Construcst the container * * @param aName - * Name of the system. - * @param aRegistry - * Service registry. - * @param aSystems - * Subsystems. + * Name of the container + * @param aComponents + * Components. * @param aProvided - * Provided services of the system. + * Provided services of the container * @param aRequired - * Required services by the system. + * 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 = aComponents; + for (Component component : aComponents) { component.addContext(getQualifiedName()); } - validate(aRequired); + validate(); } /** - * Validates the subsystems together to check that there are no required + * 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. */ - private void validate(RequiredInterface[] aRequired) { + private 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); - for (RequiredInterface service : getRequiredInterfaces()) { - if (!(required.contains(service))) { - info("Service '" - + service - + "' indicated as required is not actually required by any of the subsystems"); - } + 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( - required); + aRequired); // Compute all required interfaces that are not provided - for (ProvidedInterface service : 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(aRequired)); + reallyRequired.removeAll(Arrays.asList(getRequiredInterfaces())); + return reallyRequired; + } - String missingRequired = ""; - for (RequiredInterface service : reallyRequired) { - missingRequired += service + "\n"; + private void validateRequiredInterfaces(List 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."); + } + } + } } - 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"); + } } } + + public Scope start() { + return super.start(new DefaultScope(new ProvidedInterface[0])); + } @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"); + if (provider != null ) { + allProvided.add(provider); + } else { + if ( !intf.isOptional()) { + throw new SystemAssemblyException(getQualifiedName() + + ": required interface '" + intf + "' is not provided"); + } } - provided.add(intf.getProvider()); - } - - startImpl(); - } - - /** - * 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()); } List started = new ArrayList(); - for (Component system : _systems) { + for (Component component : _components) { try { - // Check if all required services are already provided by - // earlier - // systems. - RequiredInterface[] required = system.getRequiredInterfaces(); - - for (RequiredInterface descriptor : required) { - ProvidedInterface[] filtered = filterProvidedServices( - descriptor, allProvided); - - 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) { - throw new SystemAssemblyException( - "Service '" - + descriptor - + "' required by system '" - + system - + "' matches multiple services provided by other systems: " - + Arrays.asList(filtered)); - } - descriptor.setProvider(filtered[0]); - } + checkAllRequiredServicesAlreadyProvided(allProvided, component); // Start the service. - system.start(); - started.add(system); + Object runtime = component.start(scope); + scope.addRuntime(component, runtime); + started.add(component); // add all provided services - ProvidedInterface[] provided = system.getProvidedInterfaces(); + ProvidedInterface[] provided = component.getProvidedInterfaces(); allProvided.addAll(Arrays.asList(provided)); } catch (SystemAssemblyException e) { throw e; } catch (RuntimeException e) { LOG.error(getQualifiedName() + ": could not start '" - + system.getQualifiedName() + "'", e); - // an exception occurred, stop the successfully started - // systems - for (int i = started.size() - 1; i >= 0; i--) { - try { - started.get(i).stop(); - } catch (Throwable t) { - LOG.error(getQualifiedName() + ": error stopping " - + started.get(i).getQualifiedName()); - } - } + + component.getQualifiedName() + "'", e); + stopAlreadyStartedComponents(started, scope); throw e; } } + return scope; + } + + 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()); + } + } + } + + private void checkAllRequiredServicesAlreadyProvided( + List 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() { - for (int i = _systems.length - 1; i >= 0; i--) { - _systems[i].stop(); + protected void doStop(Scope aScope) { + for (int i = _components.length - 1; i >= 0; i--) { + Component component = _components[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; + } }