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