package org.argeo.slc.core.execution.generator; import java.util.HashMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.argeo.slc.SlcException; import org.springframework.aop.scope.ScopedProxyUtils; import org.springframework.beans.BeansException; import org.springframework.beans.MutablePropertyValues; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.config.BeanFactoryPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; import org.springframework.beans.factory.config.RuntimeBeanReference; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.GenericBeanDefinition; import org.springframework.core.Ordered; /** * Generates ExecutionFlows and Runnables as * beans in the Spring Application Context. * Called by the Application Context as a BeanFactoryPostProcessor. * Two kinds of beans are generated: * RunnableCallFlow, calling a list of Runnables from the * Application Context after configuring the ExecutionContext, * and outputs of a RunnableFactory. */ public class ExecutionFlowGenerator implements BeanFactoryPostProcessor, Ordered { private final Log log = LogFactory.getLog(getClass()); /** * Source providing a list of RunnableCallFlowDescriptor * used to create RunnableCallFlow and a list of * RunnableDataNode used to create any kind of flow via a factory */ protected ExecutionFlowGeneratorSource source; /** * Factory used to create Runnables in the Application context from * the RunnableDataNode provided from the source. */ protected RunnableFactory runnableFactory; /** * Bean name of the ExecutionContext. * Used to provide the created RunnableCallFlow beans * with a RuntimeBeanReference to * the ExecutionContext */ private String executionContextBeanName = "executionContext"; /** * Bean name of the context values Map. * A bean of class HashMap is created with this name, and a * RuntimeBeanReference is provided to the created * RunnableCallFlow beans. */ private String contextValuesBeanName = "executionFlowGenerator.contextValues"; /** * Prefix added to the bean names defined in each * RunnableCallFlowDescriptor */ private String flowBeanNamesPrefix = ""; private int order = Ordered.HIGHEST_PRECEDENCE; public void postProcessBeanFactory( ConfigurableListableBeanFactory beanFactory) throws BeansException { // assert that the beanFactory is a BeanDefinitionRegistry if (!(beanFactory instanceof BeanDefinitionRegistry)) { throw new SlcException("Can only work on " + BeanDefinitionRegistry.class); } // add bean for the Context Values Map createAndRegisterContextValuesBean((BeanDefinitionRegistry) beanFactory); // add beans for each RunnableDataNode for(RunnableDataNode node : source.getRunnableDataNodes()) { runnableFactory.createAndRegisterRunnable(node, (BeanDefinitionRegistry) beanFactory); } // add beans for each RunnableCallFlowDescriptor of the source to the application context for (RunnableCallFlowDescriptor descriptor : source .getRunnableCallFlowDescriptors()) { createAndRegisterFlowFor(descriptor, (BeanDefinitionRegistry) beanFactory); } } /** * Creates a RunnableCallFlow bean * for a RunnableCallFlowDescriptor and registers * it in the BeanDefinitionRegistry * @param flowDescriptor * @param registry */ private void createAndRegisterFlowFor(RunnableCallFlowDescriptor flowDescriptor, BeanDefinitionRegistry registry) { // create the flow bean GenericBeanDefinition flowBean = new GenericBeanDefinition(); flowBean.setBeanClass(RunnableCallFlow.class); String beanName = flowBeanNamesPrefix + flowDescriptor.getBeanName(); MutablePropertyValues mpv = new MutablePropertyValues(); mpv.addPropertyValue("runnableCalls", flowDescriptor.getRunnableCalls()); mpv.addPropertyValue("sharedContextValuesMap", new RuntimeBeanReference(contextValuesBeanName)); mpv.addPropertyValue("name", beanName); mpv.addPropertyValue("path", flowDescriptor.getPath()); mpv.addPropertyValue("executionContext", new RuntimeBeanReference(executionContextBeanName)); flowBean.setPropertyValues(mpv); // register it if(log.isDebugEnabled()) { log.debug("Registering bean definition for RunnableCallFlow " + beanName); } registry.registerBeanDefinition(beanName, flowBean); } /** * Creates the Context Values bean and register it in the * BeanDefinitionRegistry * @param registry */ private void createAndRegisterContextValuesBean(BeanDefinitionRegistry registry) { GenericBeanDefinition contextValuesBean = new GenericBeanDefinition(); contextValuesBean.setBeanClass(HashMap.class); BeanDefinitionHolder bdh = ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(contextValuesBean, contextValuesBeanName), registry, true); registry.registerBeanDefinition(contextValuesBeanName, bdh.getBeanDefinition()); } public int getOrder() { return order; } public void setOrder(int order) { this.order = order; } public void setSource(ExecutionFlowGeneratorSource source) { this.source = source; } public void setRunnableFactory(RunnableFactory runnableFactory) { this.runnableFactory = runnableFactory; } public void setExecutionContextBeanName(String executionContextBeanName) { this.executionContextBeanName = executionContextBeanName; } public void setContextValuesBeanName(String contextValuesBeanName) { this.contextValuesBeanName = contextValuesBeanName; } public void setFlowBeanNamesPrefix(String flowBeanNamesPrefix) { this.flowBeanNamesPrefix = flowBeanNamesPrefix; } }