package org.wamblee.system; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * Assembler to control multiple subsystems. It makes sure that all dependencies * are met and controls the order in which systems are initialized. * * @author Erik Brakkee */ public class SystemAssembler { private static final Log LOG = LogFactory.getLog(SystemAssembler.class); private static final String ROOT_CONTEXT_NAME = "root"; private String _context; private Component[] _systems; private ProvidedInterface[] _required; 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]); } /** * Constructs the assembler. * * @param aSystems * Systems that must be assembled. * @param aAvailableServices * Available services from other systems outside of the systems * that this assembler manages. */ public SystemAssembler(Component[] aSystems, ProvidedInterface[] aAvailableServices) { this(ROOT_CONTEXT_NAME, aSystems, aAvailableServices); } /** * Constructs the assembler. * * @param aContext * Context (unique name) of the assembler. * @param aSystems * Systems that must be assembled. * @param aAvailableServices * Available services from other systems outside of the systems * that this assembler manages. */ public SystemAssembler(String aContext, Component[] aSystems, ProvidedInterface[] aAvailableServices) { _context = aContext; _systems = aSystems; _required = aAvailableServices; validate(); } /** * Determines if the systems are ordered appropriately so that all * dependencies are met. */ private void validate() throws SystemAssemblyException { List allProvided = new ArrayList(); for (ProvidedInterface descriptor : _required) { allProvided.add(descriptor); } 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); 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)); } } // add all provided services ProvidedInterface[] provided = system.getProvidedServices(); allProvided.addAll(Arrays.asList(provided)); } } /** * Starts the subsystems. * * @param aRequiredServices * Services that are available from other systems that have been * started before. */ public void start() { LOG.info("Starting '" + _context + "'"); Set allProvided = new HashSet(); allProvided.addAll(Arrays.asList(_required)); for (Component system : _systems) { // Compose a list of the required services required for the subsystem. RequiredInterface[] descriptors = system .getRequiredServices(); List services = new ArrayList(); for (RequiredInterface required : descriptors) { ProvidedInterface[] provided = filterProvidedServices( required, allProvided); assert provided.length == 1; services.add(provided[0]); required.setProvider(provided[0]); } // Start the service. system.start(_context); // Add started services to the set of started services. for (ProvidedInterface service : system.getProvidedServices()) { allProvided.add(service); } } } }