/* * Copyright 2007 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.core; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Composite system consisting of multiple subsystems. * * @author Erik Brakkee */ public class Container extends AbstractComponent { private static final Log LOG = LogFactory.getLog(Container.class); private Component[] _systems; public static RequiredInterface[] filterRequiredServices( ProvidedInterface aProvided, Collection aDescriptors) { List required = new ArrayList(); for (RequiredInterface descriptor : aDescriptors) { if (descriptor.implementedBy(aProvided)) { required.add(descriptor); } } return required.toArray(new RequiredInterface[0]); } public static ProvidedInterface[] filterProvidedServices( RequiredInterface aRequired, Collection aProvided) { List provided = new ArrayList(); for (ProvidedInterface descriptor : aProvided) { if (aRequired.implementedBy(descriptor)) { provided.add(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. */ public Container(String aName, Component[] aSystems, ProvidedInterface[] aProvided, RequiredInterface[] aRequired) { super(aName, aProvided, aRequired); _systems = aSystems; for (Component component : aSystems) { component.addContext(getQualifiedName()); } validate(aRequired); } /** * 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. */ private void validate(RequiredInterface[] aRequired) { List provided = new ArrayList(); for (Component system : _systems) { provided.addAll(Arrays.asList(system.getProvidedInterfaces())); } List required = new ArrayList(); for (Component system : _systems) { required.addAll(Arrays.asList(system.getRequiredInterfaces())); } for (ProvidedInterface service : getProvidedInterfaces()) { if (!(provided.contains(service))) { throw new SystemAssemblyException(getName() + ": Service '" + service + "' is not provided by any of the subsystems"); } } for (RequiredInterface service : getRequiredInterfaces()) { if (!(required.contains(service))) { info("Service '" + service + "' indicated as required is not actually required by any of the subsystems"); } } 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); } } @Override protected void doStart() { List provided = 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()); } 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) { 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]); } // Start the service. system.start(); started.add(system); // add all provided services ProvidedInterface[] provided = system.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()); } } throw e; } } } @Override protected void doStop() { for (int i = _systems.length - 1; i >= 0; i--) { _systems[i].stop(); } } private void info(String aMsg) { LOG.info(getName() + ": " + aMsg); } }