View Javadoc
1   /*
2    * Copyright (C) 2007-2012 Argeo GmbH
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *         http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.argeo.slc.core.execution.generator;
17  
18  import java.util.HashMap;
19  
20  import org.apache.commons.logging.Log;
21  import org.apache.commons.logging.LogFactory;
22  import org.argeo.slc.SlcException;
23  import org.springframework.aop.scope.ScopedProxyUtils;
24  import org.springframework.beans.BeansException;
25  import org.springframework.beans.MutablePropertyValues;
26  import org.springframework.beans.factory.config.BeanDefinitionHolder;
27  import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
28  import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
29  import org.springframework.beans.factory.config.RuntimeBeanReference;
30  import org.springframework.beans.factory.support.BeanDefinitionRegistry;
31  import org.springframework.beans.factory.support.GenericBeanDefinition;
32  import org.springframework.core.Ordered;
33  
34  /**
35   * Generates <code>ExecutionFlows</code> and <code>Runnables</code> as
36   * beans in the Spring Application Context.
37   * Called by the Application Context as a <code>BeanFactoryPostProcessor</code>.
38   * Two kinds of beans are generated:
39   * <code>RunnableCallFlow</code>, calling a list of <code>Runnables</code> from the
40   * Application Context after configuring the <code>ExecutionContext</code>, 
41   * and outputs of a <code>RunnableFactory</code>.
42   */
43  public class ExecutionFlowGenerator implements BeanFactoryPostProcessor,
44  		Ordered {
45  	
46  	private final Log log = LogFactory.getLog(getClass());
47  
48  	/**
49  	 * Source providing a list of <code>RunnableCallFlowDescriptor</code> 
50  	 * used to create <code>RunnableCallFlow</code> and a list of 
51  	 * <code>RunnableDataNode</code> used to create any kind of flow via a factory
52  	 */
53  	protected ExecutionFlowGeneratorSource source;
54  	
55  	/**
56  	 * Factory used to create Runnables in the Application context from
57  	 * the <code>RunnableDataNode</code> provided from the source.
58  	 */
59  	protected RunnableFactory runnableFactory;
60  	
61  	/**
62  	 * Bean name of the <code>ExecutionContext</code>.
63  	 * Used to provide the created <code>RunnableCallFlow</code> beans 
64  	 * with a <code>RuntimeBeanReference</code> to
65  	 * the <code>ExecutionContext</code>
66  	 */
67  	private String executionContextBeanName = "executionContext";
68  	
69  	/**
70  	 * Bean name of the context values Map.
71  	 * A bean of class HashMap is created with this name, and a 
72  	 * <code>RuntimeBeanReference</code> is provided to the created
73  	 * <code>RunnableCallFlow</code> beans.
74  	 */
75  	private String contextValuesBeanName = "executionFlowGenerator.contextValues";
76  	
77  	/**
78  	 * Prefix added to the bean names defined in each 
79  	 * <code>RunnableCallFlowDescriptor</code>
80  	 */
81  	private String flowBeanNamesPrefix = "";
82  	
83  	private int order = Ordered.HIGHEST_PRECEDENCE;
84  		
85  	public void postProcessBeanFactory(
86  			ConfigurableListableBeanFactory beanFactory) throws BeansException {
87  
88  		// assert that the beanFactory is a BeanDefinitionRegistry
89  		if (!(beanFactory instanceof BeanDefinitionRegistry)) {
90  			throw new SlcException("Can only work on "
91  					+ BeanDefinitionRegistry.class);
92  		} 
93  		
94  		// add bean for the Context Values Map
95  		createAndRegisterContextValuesBean((BeanDefinitionRegistry) beanFactory);
96  		
97  		// add beans for each RunnableDataNode
98  		for(RunnableDataNode node : source.getRunnableDataNodes()) {
99  			runnableFactory.createAndRegisterRunnable(node, (BeanDefinitionRegistry) beanFactory);
100 		}
101 		
102 		// add beans for each RunnableCallFlowDescriptor of the source to the application context
103 		for (RunnableCallFlowDescriptor descriptor : source
104 				.getRunnableCallFlowDescriptors()) {
105 			createAndRegisterFlowFor(descriptor, (BeanDefinitionRegistry) beanFactory);
106 		}
107 	}
108 
109 	/**
110 	 * Creates a <code>RunnableCallFlow</code> bean
111 	 * for a <code>RunnableCallFlowDescriptor</code> and registers 
112 	 * it in the <code>BeanDefinitionRegistry</code>
113 	 * @param flowDescriptor
114 	 * @param registry
115 	 */
116 	private void createAndRegisterFlowFor(RunnableCallFlowDescriptor flowDescriptor, BeanDefinitionRegistry registry) {
117 		// create the flow bean
118 		GenericBeanDefinition flowBean = new GenericBeanDefinition();
119 		flowBean.setBeanClass(RunnableCallFlow.class);
120 		
121 		String beanName = flowBeanNamesPrefix + flowDescriptor.getBeanName();
122 		
123 		MutablePropertyValues mpv = new MutablePropertyValues();		
124 		mpv.addPropertyValue("runnableCalls", flowDescriptor.getRunnableCalls());
125 		mpv.addPropertyValue("sharedContextValuesMap", new RuntimeBeanReference(contextValuesBeanName));
126 		
127 		mpv.addPropertyValue("name", beanName);
128 		mpv.addPropertyValue("path", flowDescriptor.getPath());
129 
130 		mpv.addPropertyValue("executionContext", new RuntimeBeanReference(executionContextBeanName));
131 		
132 		flowBean.setPropertyValues(mpv);
133 		
134 		// register it
135 		if(log.isDebugEnabled()) {
136 			log.debug("Registering bean definition for RunnableCallFlow " + beanName);
137 		}
138 		registry.registerBeanDefinition(beanName, flowBean);
139 	}
140 	
141 	/**
142 	 * Creates the Context Values bean and register it in the
143 	 * <code>BeanDefinitionRegistry</code>
144 	 * @param registry
145 	 */
146 	private void createAndRegisterContextValuesBean(BeanDefinitionRegistry registry) {
147 		GenericBeanDefinition contextValuesBean = new GenericBeanDefinition();
148 		contextValuesBean.setBeanClass(HashMap.class);
149 		
150 		BeanDefinitionHolder bdh = ScopedProxyUtils.createScopedProxy(new BeanDefinitionHolder(contextValuesBean, contextValuesBeanName), registry, true);															
151 		registry.registerBeanDefinition(contextValuesBeanName, bdh.getBeanDefinition());		
152 	}
153 	
154 	public int getOrder() {
155 		return order;
156 	}
157 
158 	public void setOrder(int order) {
159 		this.order = order;
160 	}
161 
162 	public void setSource(ExecutionFlowGeneratorSource source) {
163 		this.source = source;
164 	}
165 
166 	public void setRunnableFactory(RunnableFactory runnableFactory) {
167 		this.runnableFactory = runnableFactory;
168 	}
169 
170 	public void setExecutionContextBeanName(String executionContextBeanName) {
171 		this.executionContextBeanName = executionContextBeanName;
172 	}
173 
174 	public void setContextValuesBeanName(String contextValuesBeanName) {
175 		this.contextValuesBeanName = contextValuesBeanName;
176 	}
177 
178 	public void setFlowBeanNamesPrefix(String flowBeanNamesPrefix) {
179 		this.flowBeanNamesPrefix = flowBeanNamesPrefix;
180 	}
181 }