package org.wamblee.system.spring; import java.util.Map; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.ConstructorArgumentValues; import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.ApplicationContext; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.context.support.GenericApplicationContext; import org.wamblee.system.AbstractComponent; import org.wamblee.system.CompositeComponent; import org.wamblee.system.ProvidedInterfaceDescriptor; import org.wamblee.system.RequiredInterfaceDescriptor; import org.wamblee.system.Service; import org.wamblee.system.InterfaceDescriptor; import org.wamblee.system.ServiceRegistry; import org.wamblee.system.SystemAssembler; import org.wamblee.system.SystemAssemblyException; /** * Represents a system configured based on spring. * * @author Erik Brakkee */ public class SpringSystem extends AbstractComponent { /** * Singleton access to the service registry. Required while starting up. */ static ThreadLocal REGISTRY = new ThreadLocal(); private String[] _configFiles; private Map _provided; private Map _required; /** * Parent application context containing required services. */ private GenericApplicationContext _parentContext; /** * Application context containing parsed objects. */ private AbstractApplicationContext _context; /** * Construcst a spring system. * * @param aName * Name of the system. * @param aConfigFiles * Spring config files to read. * @param aProvided * Map of bean name to service descriptor describing the bean * names that the spring config files use for each required * service. * @param aRequired * Map of bean name to service descriptor describing the bean * names that the spring config files use for each required * service. */ public SpringSystem(String aName, ServiceRegistry aRegistry, String[] aConfigFiles, Map aProvided, Map aRequired) { super(aName, aRegistry, aProvided.values().toArray(new InterfaceDescriptor[0]), aRequired.keySet().toArray(new InterfaceDescriptor[0])); _configFiles = aConfigFiles; _provided = aProvided; _required = aRequired; } @Override protected void doStart(String aContext, Service[] aRequiredServices) { ServiceRegistry old = REGISTRY.get(); try { REGISTRY.set(getRegistry()); try { registerRequiredServices(aRequiredServices); parseConfigFiles(); exposeProvidedServices(aContext); } catch (Exception e) { throw new SystemAssemblyException( "Failed to assemble spring system", e); } } finally { REGISTRY.set(old); } } private void exposeProvidedServices(String aContext) { // Call addService for each provided service. for (String name : _provided.keySet()) { Object svc = _context.getBean(name); if (svc == null) { throw new IllegalArgumentException(aContext + ": service '" + name + "' is null"); } addService(aContext, _provided.get(name), svc); } } private void parseConfigFiles() { // Parse spring config files _context = new ClassPathXmlApplicationContext((String[]) _configFiles, _parentContext); } private void registerRequiredServices(Service[] aRequiredServices) { // Register required services in a parent context // Register the Hibernate mapping files as a bean. _parentContext = new GenericApplicationContext(); for (Service svc: aRequiredServices) { String id = svc.getId(); ProvidedInterfaceDescriptor descriptor = svc.getDescriptor(); RequiredInterfaceDescriptor[] requiredServices = SystemAssembler.filterRequiredServices(descriptor, _required.keySet()); for (RequiredInterfaceDescriptor required: requiredServices) { String beanName = _required.get(required); ConstructorArgumentValues cargs = new ConstructorArgumentValues(); cargs.addGenericArgumentValue(id); BeanDefinition definition = new RootBeanDefinition(ProvidedServiceBean.class, cargs, new MutablePropertyValues()); _parentContext.registerBeanDefinition(beanName, definition); } } _parentContext.refresh(); } @Override protected void doStop() { _context.close(); } }