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