1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.springframework.batch.core.scope.util;
17
18 import java.beans.PropertyEditor;
19 import java.util.Date;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Set;
23
24 import org.springframework.aop.TargetSource;
25 import org.springframework.aop.target.SimpleBeanTargetSource;
26 import org.springframework.beans.BeanWrapper;
27 import org.springframework.beans.BeanWrapperImpl;
28 import org.springframework.beans.BeansException;
29 import org.springframework.beans.PropertyEditorRegistrySupport;
30 import org.springframework.beans.TypeConverter;
31 import org.springframework.beans.TypeMismatchException;
32 import org.springframework.beans.factory.InitializingBean;
33 import org.springframework.beans.factory.config.BeanDefinition;
34 import org.springframework.beans.factory.config.BeanDefinitionHolder;
35 import org.springframework.beans.factory.config.BeanDefinitionVisitor;
36 import org.springframework.beans.factory.config.TypedStringValue;
37 import org.springframework.beans.factory.support.DefaultListableBeanFactory;
38 import org.springframework.beans.factory.support.GenericBeanDefinition;
39 import org.springframework.beans.factory.support.ManagedList;
40 import org.springframework.beans.factory.support.ManagedMap;
41 import org.springframework.beans.factory.support.ManagedSet;
42 import org.springframework.core.AttributeAccessor;
43 import org.springframework.core.MethodParameter;
44 import org.springframework.util.Assert;
45 import org.springframework.util.StringValueResolver;
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63 public class PlaceholderTargetSource extends SimpleBeanTargetSource implements InitializingBean {
64
65
66
67
68 private static final String PLACEHOLDER_PREFIX = "%{";
69
70 private static final String PLACEHOLDER_SUFFIX = "}";
71
72 private ContextFactory contextFactory;
73
74 private String beanName;
75
76
77
78
79
80
81
82 public void setContextFactory(ContextFactory contextFactory) {
83 this.contextFactory = contextFactory;
84 }
85
86
87
88
89
90
91
92 public void afterPropertiesSet() {
93 Assert.notNull(contextFactory, "The ContextFactory must be set.");
94 beanName = getTargetBeanName() + "#" + contextFactory.getContextId();
95 }
96
97
98
99
100
101
102 @Override
103 public synchronized Object getTarget() throws BeansException {
104
105
106 Object target = getTargetFromContext();
107 if (target != null) {
108 return target;
109 }
110
111 DefaultListableBeanFactory listableBeanFactory = (DefaultListableBeanFactory) getBeanFactory();
112
113 final TypeConverter typeConverter = listableBeanFactory.getTypeConverter();
114
115 DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(listableBeanFactory);
116 beanFactory.copyConfigurationFrom(listableBeanFactory);
117
118 final TypeConverter contextTypeConverter = new TypeConverter() {
119 @SuppressWarnings({ "unchecked", "rawtypes" })
120 public Object convertIfNecessary(Object value, Class requiredType, MethodParameter methodParam)
121 throws TypeMismatchException {
122 Object result = null;
123 if (value instanceof String) {
124 String key = (String) value;
125 if (key.startsWith(PLACEHOLDER_PREFIX) && key.endsWith(PLACEHOLDER_SUFFIX)) {
126 key = extractKey(key);
127 result = convertFromContext(key, requiredType);
128 if (result == null) {
129 Object property = getPropertyFromContext(key);
130
131
132 if (property != null) {
133 property = convertToString(property, typeConverter);
134 if (property != null) {
135 value = property;
136 }
137 logger.debug(String.format("Bound %%{%s} to String value [%s]", key, result));
138 }
139 else {
140 throw new IllegalStateException("Cannot bind to placeholder: " + key);
141 }
142 }
143 else {
144 logger.debug(String.format("Bound %%{%s} to [%s]", key, result));
145 }
146 }
147 }
148 else if (requiredType.isAssignableFrom(value.getClass())) {
149 result = value;
150 }
151 else if (requiredType.isAssignableFrom(String.class)) {
152 result = convertToString(value, typeConverter);
153 if (result == null) {
154 logger.debug("Falling back on toString for conversion of : [" + value.getClass() + "]");
155 result = value.toString();
156 }
157 }
158 return result != null ? result : typeConverter.convertIfNecessary(value, requiredType, methodParam);
159 }
160
161 @SuppressWarnings("rawtypes")
162 public Object convertIfNecessary(Object value, Class requiredType) throws TypeMismatchException {
163 return convertIfNecessary(value, requiredType, null);
164 }
165 };
166 beanFactory.setTypeConverter(contextTypeConverter);
167
168 try {
169
170
171
172
173
174
175 String targetBeanName = getTargetBeanName();
176 GenericBeanDefinition beanDefinition = new GenericBeanDefinition(listableBeanFactory
177 .getMergedBeanDefinition(targetBeanName));
178 logger.debug("Rehydrating scoped target: [" + targetBeanName + "]");
179
180 BeanDefinitionVisitor visitor = new PlaceholderBeanDefinitionVisitor(contextTypeConverter);
181
182 beanFactory.registerBeanDefinition(beanName, beanDefinition);
183
184 visitor.visitBeanDefinition(beanDefinition);
185 target = beanFactory.getBean(beanName);
186 putTargetInContext(target);
187 return target;
188
189 }
190 finally {
191 beanFactory.removeBeanDefinition(beanName);
192 beanFactory = null;
193
194 }
195
196 }
197
198 private void putTargetInContext(Object target) {
199 Object context = contextFactory.getContext();
200 if (context instanceof AttributeAccessor) {
201 ((AttributeAccessor) context).setAttribute(beanName, target);
202 }
203 }
204
205 private Object getTargetFromContext() {
206 Object context = contextFactory.getContext();
207 if (context instanceof AttributeAccessor) {
208 return ((AttributeAccessor) context).getAttribute(beanName);
209 }
210 return null;
211 }
212
213
214
215
216
217
218 protected String convertToString(Object value, TypeConverter typeConverter) {
219 String result = null;
220 try {
221
222
223 result = (String) typeConverter.convertIfNecessary(value, String.class);
224 }
225 catch (TypeMismatchException e) {
226
227 }
228 if (result == null && typeConverter instanceof PropertyEditorRegistrySupport) {
229
230
231
232
233
234
235 PropertyEditorRegistrySupport registry = (PropertyEditorRegistrySupport) typeConverter;
236 PropertyEditor editor = registry.findCustomEditor(value.getClass(), null);
237 if (editor != null) {
238 if (registry.isSharedEditor(editor)) {
239
240
241 synchronized (editor) {
242 editor.setValue(value);
243 result = editor.getAsText();
244 }
245 }
246 else {
247 editor.setValue(value);
248 result = editor.getAsText();
249 }
250 }
251 }
252 return result;
253 }
254
255
256
257
258
259
260 private Object convertFromContext(String key, Class<?> requiredType) {
261 Object result = null;
262 Object property = getPropertyFromContext(key);
263 if (property == null || requiredType.isAssignableFrom(property.getClass())) {
264 result = property;
265 }
266 return result;
267 }
268
269 private Object getPropertyFromContext(String key) {
270 Object context = contextFactory.getContext();
271 if (context == null) {
272 throw new IllegalStateException("No context available while replacing placeholders.");
273 }
274 BeanWrapper wrapper = new BeanWrapperImpl(context);
275 if (wrapper.isReadableProperty(key)) {
276 return wrapper.getPropertyValue(key);
277 }
278 return null;
279 }
280
281 private String extractKey(String value) {
282 return value.substring(value.indexOf(PLACEHOLDER_PREFIX) + PLACEHOLDER_PREFIX.length(), value
283 .indexOf(PLACEHOLDER_SUFFIX));
284 }
285
286
287
288
289
290
291
292
293
294 private boolean isKey(String value) {
295 return value.indexOf(PLACEHOLDER_PREFIX) == value.lastIndexOf(PLACEHOLDER_PREFIX)
296 && value.startsWith(PLACEHOLDER_PREFIX) && value.endsWith(PLACEHOLDER_SUFFIX);
297 }
298
299
300
301
302
303
304
305
306 private final class PlaceholderBeanDefinitionVisitor extends BeanDefinitionVisitor {
307
308 public PlaceholderBeanDefinitionVisitor(final TypeConverter typeConverter) {
309 super(new PlaceholderStringValueResolver(typeConverter));
310 }
311
312 @SuppressWarnings({ "unchecked", "rawtypes" })
313 protected Object resolveValue(Object value) {
314
315 if (value instanceof TypedStringValue) {
316
317 TypedStringValue typedStringValue = (TypedStringValue) value;
318 String stringValue = typedStringValue.getValue();
319 if (stringValue != null) {
320
321
322
323 if (isKey(stringValue)) {
324 String key = extractKey(stringValue);
325 Object result = getPropertyFromContext(key);
326 if (result != null) {
327 value = result;
328 logger.debug(String.format("Resolved %%{%s} to obtain [%s]", key, result));
329 }
330 }
331 else {
332
333
334 String visitedString = resolveStringValue(stringValue);
335 value = new TypedStringValue(visitedString);
336 }
337 }
338
339 }
340 else if (value instanceof Map) {
341
342 Map map = (Map) value;
343 Map newValue = new ManagedMap(map.size());
344 newValue.putAll(map);
345 super.visitMap(newValue);
346 value = newValue;
347
348 }
349 else if (value instanceof List) {
350
351 List list = (List) value;
352 List newValue = new ManagedList(list.size());
353 newValue.addAll(list);
354 super.visitList(newValue);
355 value = newValue;
356
357 }
358 else if (value instanceof Set) {
359
360 Set list = (Set) value;
361 Set newValue = new ManagedSet(list.size());
362 newValue.addAll(list);
363 super.visitSet(newValue);
364 value = newValue;
365
366 }
367 else if (value instanceof BeanDefinition) {
368
369 BeanDefinition newValue = new GenericBeanDefinition((BeanDefinition) value);
370 visitBeanDefinition((BeanDefinition) newValue);
371 value = newValue;
372
373 }
374 else if (value instanceof BeanDefinitionHolder) {
375
376 BeanDefinition newValue = new GenericBeanDefinition(((BeanDefinitionHolder) value).getBeanDefinition());
377 visitBeanDefinition((BeanDefinition) newValue);
378 value = newValue;
379
380 }
381 else {
382
383 value = super.resolveValue(value);
384
385 }
386
387 return value;
388
389 }
390
391 }
392
393 private final class PlaceholderStringValueResolver implements StringValueResolver {
394
395 private final TypeConverter typeConverter;
396
397 private PlaceholderStringValueResolver(TypeConverter typeConverter) {
398 this.typeConverter = typeConverter;
399 }
400
401 public String resolveStringValue(String strVal) {
402 if (!strVal.contains(PLACEHOLDER_PREFIX)) {
403 return strVal;
404 }
405 return replacePlaceholders(strVal, typeConverter);
406 }
407
408
409
410
411
412
413
414
415
416 private String replacePlaceholders(String value, TypeConverter typeConverter) {
417
418 StringBuilder result = new StringBuilder(value);
419
420 int first = result.indexOf(PLACEHOLDER_PREFIX);
421 int next = result.indexOf(PLACEHOLDER_SUFFIX, first + 1);
422
423 while (first >= 0) {
424
425 Assert.state(next > 0, String.format("Placeholder key incorrectly specified: use %skey%s (in %s)",
426 PLACEHOLDER_PREFIX, PLACEHOLDER_SUFFIX, value));
427
428 String key = result.substring(first + PLACEHOLDER_PREFIX.length(), next);
429
430 boolean replaced = replaceIfTypeMatches(result, first, next, key, String.class, typeConverter);
431 replaced |= replaceIfTypeMatches(result, first, next, key, Long.class, typeConverter);
432 replaced |= replaceIfTypeMatches(result, first, next, key, Integer.class, typeConverter);
433 replaced |= replaceIfTypeMatches(result, first, next, key, Date.class, typeConverter);
434 if (!replaced) {
435 if (!value.startsWith(PLACEHOLDER_PREFIX) || !value.endsWith(PLACEHOLDER_SUFFIX)) {
436 throw new IllegalStateException(String.format("Cannot bind to partial key %%{%s} in %s", key,
437 value));
438 }
439 logger.debug(String.format("Deferring binding of placeholder: %%{%s}", key));
440 }
441 else {
442 logger.debug(String.format("Bound %%{%s} to obtain [%s]", key, result));
443 }
444 first = result.indexOf(PLACEHOLDER_PREFIX, first + 1);
445 next = result.indexOf(PLACEHOLDER_SUFFIX, first + 1);
446
447 }
448
449 return result.toString();
450
451 }
452
453 private boolean replaceIfTypeMatches(StringBuilder result, int first, int next, String key,
454 Class<?> requiredType, TypeConverter typeConverter) {
455 Object property = convertFromContext(key, requiredType);
456 if (property != null) {
457 result.replace(first, next + 1, (String) typeConverter.convertIfNecessary(property, String.class));
458 return true;
459 }
460 return false;
461 }
462
463 }
464
465 }