2 * Copyright (C) 2010 Mathieu Baudier <mbaudier@argeo.org>
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package org
.argeo
.slc
.core
.execution
;
19 import java
.beans
.PropertyDescriptor
;
20 import java
.util
.HashSet
;
21 import java
.util
.Iterator
;
22 import java
.util
.List
;
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
;
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.
50 public class ExecutionParameterPostProcessor
extends
51 InstantiationAwareBeanPostProcessorAdapter
{
53 private final static Log log
= LogFactory
54 .getLog(ExecutionParameterPostProcessor
.class);
56 private ExecutionContext executionContext
;
57 private InstantiationManager instantiationManager
;
59 private String placeholderPrefix
= "@{";
60 private String placeholderSuffix
= "}";
61 private String nullValue
;
64 public PropertyValues
postProcessPropertyValues(PropertyValues pvs
,
65 PropertyDescriptor
[] pds
, Object bean
, String beanName
)
66 throws BeansException
{
68 // TODO: resolve at execution only if scope is execution
69 // TODO: deal with placeholders in RuntimeBeanReference and
70 // RuntimeBeanNameReference
72 MutablePropertyValues newPvs
= new MutablePropertyValues();
74 boolean changesOccured
= false;
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;
84 return changesOccured ? newPvs
: pvs
;
88 public boolean postProcessAfterInstantiation(Object bean
, String beanName
)
89 throws BeansException
{
90 if (bean
instanceof ExecutionFlow
)
91 instantiationManager
.flowInitializationStarted(
92 (ExecutionFlow
) bean
, beanName
);
97 public Object
postProcessAfterInitialization(Object bean
, String beanName
)
98 throws BeansException
{
99 if (bean
instanceof ExecutionFlow
)
100 instantiationManager
.flowInitializationFinished(
101 (ExecutionFlow
) bean
, beanName
);
105 protected String
resolvePlaceholder(Object bean
, String placeholder
) {
106 if (instantiationManager
.isInFlowInitialization())
107 return instantiationManager
.getInitializingFlowParameter(
108 placeholder
).toString();
111 // next call fail if no execution context available
112 Object obj
= executionContext
.getVariable(placeholder
);
114 return obj
.toString();
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();
127 String convertedValue
= resolveString(beanName
, bean
, originalValue
);
128 if (convertedValue
== 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)
137 return convertedValue
.equals(originalValue
) ? value
139 } else if (value
instanceof ManagedMap
) {
140 Map
<?
, ?
> mapVal
= (Map
<?
, ?
>) value
;
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
);
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;
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;
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
);
185 return entriesModified ? newContent
: value
;
187 // log.debug(beanName + ": " + value.getClass() + " : " + value);
193 private String
resolveString(String beanName
, Object bean
, String strVal
) {
194 // in case <null/> is passed
198 String value
= parseStringValue(bean
, strVal
, new HashSet
<String
>());
201 throw new SlcException("Could not resolve placeholder '" + strVal
202 + "' in bean '" + beanName
+ "'");
204 return (value
.equals(nullValue
) ?
null : value
);
207 public void setPlaceholderPrefix(String placeholderPrefix
) {
208 this.placeholderPrefix
= placeholderPrefix
;
211 public void setPlaceholderSuffix(String placeholderSuffix
) {
212 this.placeholderSuffix
= placeholderSuffix
;
215 public void setNullValue(String nullValue
) {
216 this.nullValue
= nullValue
;
219 public void setInstantiationManager(
220 InstantiationManager instantiationManager
) {
221 this.instantiationManager
= instantiationManager
;
224 public void setExecutionContext(ExecutionContext executionContext
) {
225 this.executionContext
= executionContext
;
229 // Following methods hacked from the internals of
230 // PropertyPlaceholderConfigurer
233 protected String
parseStringValue(Object bean
, String strVal
,
234 Set
<String
> visitedPlaceholders
)
235 throws BeanDefinitionStoreException
{
237 // in case <null/> is passed
241 StringBuffer buf
= new StringBuffer(strVal
);
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");
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
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
+ "'");
271 startIndex
= buf
.indexOf(placeholderPrefix
, startIndex
274 throw new BeanDefinitionStoreException(
275 "Could not resolve placeholder '" + placeholder
278 visitedPlaceholders
.remove(placeholder
);
284 return buf
.toString();
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();
298 } else if (StringUtils
299 .substringMatch(buf
, index
, placeholderPrefix
)) {
300 withinNestedPlaceholder
++;
301 index
= index
+ placeholderPrefix
.length();