]> git.argeo.org Git - gpl/argeo-slc.git/blob - runtime/org.argeo.slc.core/src/main/java/org/argeo/slc/core/execution/ExecutionParameterPostProcessor.java
Document and improve execution model
[gpl/argeo-slc.git] / runtime / org.argeo.slc.core / src / main / java / org / argeo / slc / core / execution / ExecutionParameterPostProcessor.java
1 /*
2 * Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
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
17 package org.argeo.slc.core.execution;
18
19 import java.beans.PropertyDescriptor;
20 import java.util.HashSet;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24 import java.util.Set;
25
26 import org.apache.commons.logging.Log;
27 import org.apache.commons.logging.LogFactory;
28 import org.argeo.slc.SlcException;
29 import org.argeo.slc.execution.ExecutionContext;
30 import org.argeo.slc.execution.ExecutionFlow;
31 import org.springframework.beans.BeansException;
32 import org.springframework.beans.MutablePropertyValues;
33 import org.springframework.beans.PropertyValue;
34 import org.springframework.beans.PropertyValues;
35 import org.springframework.beans.factory.BeanDefinitionStoreException;
36 import org.springframework.beans.factory.config.InstantiationAwareBeanPostProcessorAdapter;
37 import org.springframework.beans.factory.config.TypedStringValue;
38 import org.springframework.beans.factory.support.ManagedList;
39 import org.springframework.beans.factory.support.ManagedMap;
40 import org.springframework.beans.factory.support.ManagedSet;
41 import org.springframework.util.ObjectUtils;
42 import org.springframework.util.StringUtils;
43
44 public class ExecutionParameterPostProcessor extends
45 InstantiationAwareBeanPostProcessorAdapter {
46
47 private final static Log log = LogFactory
48 .getLog(ExecutionParameterPostProcessor.class);
49
50 private ExecutionContext executionContext;
51 private InstantiationManager instantiationManager;
52
53 private String placeholderPrefix = "@{";
54 private String placeholderSuffix = "}";
55 private String nullValue;
56
57 @Override
58 public PropertyValues postProcessPropertyValues(PropertyValues pvs,
59 PropertyDescriptor[] pds, Object bean, String beanName)
60 throws BeansException {
61
62 // TODO: resolve at execution only if scope is execution
63 // TODO: deal with placeholders in RuntimeBeanReference and
64 // RuntimeBeanNameReference
65
66 MutablePropertyValues newPvs = new MutablePropertyValues();
67
68 boolean changesOccured = false;
69
70 for (PropertyValue pv : pvs.getPropertyValues()) {
71 Object convertedValue = resolveValue(beanName, bean, pv.getValue());
72 newPvs.addPropertyValue(new PropertyValue(pv, convertedValue));
73 if (convertedValue != pv.getValue()) {
74 changesOccured = true;
75 }
76 }
77
78 return changesOccured ? newPvs : pvs;
79 }
80
81 @Override
82 public boolean postProcessAfterInstantiation(Object bean, String beanName)
83 throws BeansException {
84 if (bean instanceof ExecutionFlow)
85 instantiationManager.flowInitializationStarted(
86 (ExecutionFlow) bean, beanName);
87 return true;
88 }
89
90 @Override
91 public Object postProcessAfterInitialization(Object bean, String beanName)
92 throws BeansException {
93 if (bean instanceof ExecutionFlow)
94 instantiationManager.flowInitializationFinished(
95 (ExecutionFlow) bean, beanName);
96 return bean;
97 }
98
99 protected String resolvePlaceholder(Object bean, String placeholder) {
100 if (instantiationManager.isInFlowInitialization())
101 return instantiationManager.getInitializingFlowParameter(
102 placeholder).toString();
103
104 else {// execution
105 // next call fail if no execution context available
106 Object obj = executionContext.getVariable(placeholder);
107 if (obj != null) {
108 return obj.toString();
109 }
110 }
111
112 return null;
113 }
114
115 @SuppressWarnings(value = { "unchecked" })
116 public Object resolveValue(String beanName, Object bean, Object value) {
117 if (value instanceof TypedStringValue) {
118 TypedStringValue tsv = (TypedStringValue) value;
119 String originalValue = tsv.getValue();
120
121 String convertedValue = resolveString(beanName, bean, originalValue);
122 if (convertedValue == null)
123 return null;
124 return convertedValue.equals(originalValue) ? value
125 : new TypedStringValue(convertedValue);
126 } else if (value instanceof String) {
127 String originalValue = value.toString();
128 String convertedValue = resolveString(beanName, bean, originalValue);
129 if (convertedValue == null)
130 return null;
131 return convertedValue.equals(originalValue) ? value
132 : convertedValue;
133 } else if (value instanceof ManagedMap) {
134 Map mapVal = (Map) value;
135
136 Map newContent = new ManagedMap();
137 boolean entriesModified = false;
138 for (Iterator it = mapVal.entrySet().iterator(); it.hasNext();) {
139 Map.Entry entry = (Map.Entry) it.next();
140 Object key = entry.getKey();
141 int keyHash = (key != null ? key.hashCode() : 0);
142 Object newKey = resolveValue(beanName, bean, key);
143 int newKeyHash = (newKey != null ? newKey.hashCode() : 0);
144 Object val = entry.getValue();
145 Object newVal = resolveValue(beanName, bean, val);
146 newContent.put(newKey, newVal);
147 entriesModified = entriesModified
148 || (newVal != val || newKey != key || newKeyHash != keyHash);
149 }
150
151 return entriesModified ? newContent : value;
152 } else if (value instanceof ManagedList) {
153 List listVal = (List) value;
154 List newContent = new ManagedList();
155 boolean valueModified = false;
156
157 for (int i = 0; i < listVal.size(); i++) {
158 Object elem = listVal.get(i);
159 Object newVal = resolveValue(beanName, bean, elem);
160 newContent.add(newVal);
161 if (!ObjectUtils.nullSafeEquals(newVal, elem)) {
162 valueModified = true;
163 }
164 }
165 return valueModified ? newContent : value;
166 } else if (value instanceof ManagedSet) {
167 Set setVal = (Set) value;
168 Set newContent = new ManagedSet();
169 boolean entriesModified = false;
170 for (Iterator it = setVal.iterator(); it.hasNext();) {
171 Object elem = it.next();
172 int elemHash = (elem != null ? elem.hashCode() : 0);
173 Object newVal = resolveValue(beanName, bean, elem);
174 int newValHash = (newVal != null ? newVal.hashCode() : 0);
175 newContent.add(newVal);
176 entriesModified = entriesModified
177 || (newVal != elem || newValHash != elemHash);
178 }
179 return entriesModified ? newContent : value;
180 } else {
181 // log.debug(beanName + ": " + value.getClass() + " : " + value);
182 return value;
183 }
184
185 }
186
187 private String resolveString(String beanName, Object bean, String strVal) {
188 // in case <null/> is passed
189 if (strVal == null)
190 return null;
191
192 String value = parseStringValue(bean, strVal, new HashSet<String>());
193
194 if (value == null)
195 throw new SlcException("Could not resolve placeholder '" + strVal
196 + "' in bean '" + beanName + "'");
197
198 return (value.equals(nullValue) ? null : value);
199 }
200
201 public void setPlaceholderPrefix(String placeholderPrefix) {
202 this.placeholderPrefix = placeholderPrefix;
203 }
204
205 public void setPlaceholderSuffix(String placeholderSuffix) {
206 this.placeholderSuffix = placeholderSuffix;
207 }
208
209 public void setNullValue(String nullValue) {
210 this.nullValue = nullValue;
211 }
212
213 public void setInstantiationManager(
214 InstantiationManager instantiationManager) {
215 this.instantiationManager = instantiationManager;
216 }
217
218 public void setExecutionContext(ExecutionContext executionContext) {
219 this.executionContext = executionContext;
220 }
221
222 //
223 // Following methods hacked from the internals of
224 // PropertyPlaceholderConfigurer
225 //
226
227 protected String parseStringValue(Object bean, String strVal,
228 Set<String> visitedPlaceholders)
229 throws BeanDefinitionStoreException {
230
231 // in case <null/> is passed
232 if (strVal == null)
233 return null;
234
235 StringBuffer buf = new StringBuffer(strVal);
236
237 int startIndex = strVal.indexOf(placeholderPrefix);
238 while (startIndex != -1) {
239 int endIndex = findPlaceholderEndIndex(buf, startIndex);
240 if (endIndex != -1) {
241 String placeholder = buf.substring(startIndex
242 + placeholderPrefix.length(), endIndex);
243 if (!visitedPlaceholders.add(placeholder)) {
244 throw new BeanDefinitionStoreException(
245 "Circular placeholder reference '" + placeholder
246 + "' in property definitions");
247 }
248 // Recursive invocation, parsing placeholders contained in
249 // the placeholder key.
250 placeholder = parseStringValue(bean, placeholder,
251 visitedPlaceholders);
252 // Now obtain the value for the fully resolved key...
253 String propVal = resolvePlaceholder(bean, placeholder);
254 if (propVal != null) {
255 // Recursive invocation, parsing placeholders contained
256 // in the
257 // previously resolved placeholder value.
258 propVal = parseStringValue(bean, propVal,
259 visitedPlaceholders);
260 buf.replace(startIndex, endIndex
261 + placeholderSuffix.length(), propVal);
262 if (log.isTraceEnabled()) {
263 log.trace("Resolved placeholder '" + placeholder + "'");
264 }
265 startIndex = buf.indexOf(placeholderPrefix, startIndex
266 + propVal.length());
267 } else {
268 throw new BeanDefinitionStoreException(
269 "Could not resolve placeholder '" + placeholder
270 + "'");
271 }
272 visitedPlaceholders.remove(placeholder);
273 } else {
274 startIndex = -1;
275 }
276 }
277
278 return buf.toString();
279 }
280
281 private int findPlaceholderEndIndex(CharSequence buf, int startIndex) {
282 int index = startIndex + placeholderPrefix.length();
283 int withinNestedPlaceholder = 0;
284 while (index < buf.length()) {
285 if (StringUtils.substringMatch(buf, index, placeholderSuffix)) {
286 if (withinNestedPlaceholder > 0) {
287 withinNestedPlaceholder--;
288 index = index + placeholderSuffix.length();
289 } else {
290 return index;
291 }
292 } else if (StringUtils
293 .substringMatch(buf, index, placeholderPrefix)) {
294 withinNestedPlaceholder++;
295 index = index + placeholderPrefix.length();
296 } else {
297 index++;
298 }
299 }
300 return -1;
301 }
302
303 }