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");
+ }
+ }
}