* 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 {
private static final Log LOG = LogFactory.getLog(Container.class);
- private Component[] _systems;
+ private Component[] _components;
public static RequiredInterface[] filterRequiredServices(
ProvidedInterface aProvided,
}
public static ProvidedInterface[] filterProvidedServices(
- RequiredInterface aRequired,
- Collection<ProvidedInterface> aProvided) {
+ RequiredInterface aRequired, Collection<ProvidedInterface> aProvided) {
List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
for (ProvidedInterface descriptor : aProvided) {
if (aRequired.implementedBy(descriptor)) {
}
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.
+ * Construcst 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 = aComponents;
+ for (Component component : aComponents) {
component.addContext(getQualifiedName());
}
- validate(aRequired);
+ validate();
}
/**
- * 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.
*/
- private void validate(RequiredInterface[] aRequired) {
+ private void validate() {
List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
- for (Component system : _systems) {
- provided.addAll(Arrays.asList(system.getProvidedServices()));
+ for (Component component : _components) {
+ provided.addAll(Arrays.asList(component.getProvidedInterfaces()));
}
List<RequiredInterface> required = new ArrayList<RequiredInterface>();
- for (Component system : _systems) {
- required.addAll(Arrays.asList(system.getRequiredServices()));
+ for (Component component : _components) {
+ required.addAll(Arrays.asList(component.getRequiredInterfaces()));
}
- for (ProvidedInterface service : getProvidedServices()) {
- if (!(provided.contains(service))) {
- throw new SystemAssemblyException(getName() + ": Service '" + service
- + "' is not provided by any of the subsystems");
- }
- }
+ validateProvidedInterfaces(provided);
- for (RequiredInterface service : getRequiredServices()) {
- if (!(required.contains(service))) {
- info("Service '"
- + service
- + "' indicated as required is not actually required by any of the subsystems");
- }
+ 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>(
- required);
+ aRequired);
// Compute all required interfaces that are not provided
- for (ProvidedInterface service : provided) {
- List<RequiredInterface> fulfilled =
- Arrays.asList(filterRequiredServices(service,
- reallyRequired));
- reallyRequired.removeAll(fulfilled);
+
+ 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(aRequired));
-
- String missingRequired = "";
- for (RequiredInterface service: reallyRequired) {
- missingRequired += service + "\n";
+ // 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.");
+ }
+ }
+ }
}
- if ( missingRequired.length() > 0 ) {
- throw new SystemAssemblyException(getName() + ": missing required services\n" + missingRequired);
+ }
+
+ 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");
+ }
}
}
@Override
protected void doStart() {
- List<ProvidedInterface> provided = new ArrayList<ProvidedInterface>();
-
+ LOG.info("Starting '" + getQualifiedName() + "'");
+ List<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
+
// all interfaces from the required list of this container are
// provided to the components inside it.
- RequiredInterface[] required = getRequiredServices();
- 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());
+ 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");
+ }
+ }
}
-
- startImpl();
+
+ List<Component> started = new ArrayList<Component>();
+ for (Component component : _components) {
+ try {
+ checkAllRequiredServicesAlreadyProvided(allProvided, component);
+
+ // Start the service.
+ component.start();
+ 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);
+ throw e;
+ }
+ }
+
}
-
- /**
- * 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<ProvidedInterface> allProvided = new ArrayList<ProvidedInterface>();
-
- // Add the provides of all externally required interfaces to the list of available
- // interfaces
- for (RequiredInterface required: getRequiredServices()) {
- allProvided.add(required.getProvider());
+
+ private void stopAlreadyStartedComponents(List<Component> aStarted) {
+ // an exception occurred, stop the successfully started
+ // components
+ for (int i = aStarted.size() - 1; i >= 0; i--) {
+ try {
+ aStarted.get(i).stop();
+ } 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.getRequiredServices();
+ }
- for (RequiredInterface descriptor : required) {
- ProvidedInterface[] filtered = filterProvidedServices(
- descriptor, allProvided);
+ private void checkAllRequiredServicesAlreadyProvided(
+ List<ProvidedInterface> 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.getProvidedServices();
- allProvided.addAll(Arrays.asList(provided));
}
}
-
@Override
protected void doStop() {
- for (int i = _systems.length-1; i >= 0; i--) {
- _systems[i].stop();
+ for (int i = _components.length - 1; i >= 0; i--) {
+ _components[i].stop();
}
}
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<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;
+ }
}