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