package org.wamblee.system; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; 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 SubSystem[] _systems; public static RequiredServiceDescriptor[] filterRequiredServices( ProvidedServiceDescriptor aProvided, Collection aDescriptors) { List required = new ArrayList(); for (RequiredServiceDescriptor descriptor : aDescriptors) { if (descriptor.implementedBy(aProvided)) { required.add(descriptor); } } return required.toArray(new RequiredServiceDescriptor[0]); } public static ProvidedServiceDescriptor[] filterProvidedServices( RequiredServiceDescriptor aRequired, Collection aProvided) { List provided = new ArrayList(); for (ProvidedServiceDescriptor descriptor : aProvided) { if (aRequired.implementedBy(descriptor)) { provided.add(descriptor); } } return provided.toArray(new ProvidedServiceDescriptor[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(SubSystem[] aSystems, ProvidedServiceDescriptor[] 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, SubSystem[] aSystems, ProvidedServiceDescriptor[] aAvailableServices) { _context = aContext; _systems = aSystems; validate(aAvailableServices); } /** * Determines if the systems are ordered appropriately so that all * dependencies are met. */ private void validate(ProvidedServiceDescriptor[] aDescriptors) throws SystemAssemblyException { List allProvided = new ArrayList(); for (ProvidedServiceDescriptor descriptor : aDescriptors) { allProvided.add(descriptor); } for (SubSystem system : _systems) { // Check if all required services are already provided by earlier // systems. RequiredServiceDescriptor[] required = system.getRequiredServices(); for (RequiredServiceDescriptor descriptor : required) { ProvidedServiceDescriptor[] 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 ProvidedServiceDescriptor[] provided = system.getProvidedServices(); allProvided.addAll(Arrays.asList(provided)); } } /** * Starts the subsystems. * * @param aRegistry * Service registry to which created services must be registered. * @param aRequiredServices * Services that are available from other systems that have been * started before. */ public void start(ServiceRegistry aRegistry, Service[] aRequiredServices) { LOG.info("Starting '" + _context + "'"); Map allProvided = new HashMap(); for (Service service : aRequiredServices) { allProvided.put(service.getDescriptor(), service); } for (SubSystem system : _systems) { // Compose a list of the required services required for the subsystem. RequiredServiceDescriptor[] descriptors = system .getRequiredServices(); List services = new ArrayList(); for (RequiredServiceDescriptor descriptor : descriptors) { ProvidedServiceDescriptor[] provided = filterProvidedServices( descriptor, allProvided.keySet()); services.add(allProvided.get(provided[0])); } // Start the service. Service[] provided = system.start(_context, services .toArray(new Service[0])); // Add started services to the map of started services. for (Service service : provided) { allProvided.put(service.getDescriptor(), service); } } } }