BATCH-1378: copy map/list/set to avoid storing wrong value after placeholder binding
This commit is contained in:
@@ -17,6 +17,9 @@ package org.springframework.batch.core.scope.util;
|
||||
|
||||
import java.beans.PropertyEditor;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.springframework.aop.TargetSource;
|
||||
import org.springframework.aop.target.SimpleBeanTargetSource;
|
||||
@@ -31,6 +34,9 @@ import org.springframework.beans.factory.config.BeanDefinitionVisitor;
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
|
||||
import org.springframework.beans.factory.support.GenericBeanDefinition;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.ManagedSet;
|
||||
import org.springframework.core.AttributeAccessor;
|
||||
import org.springframework.core.MethodParameter;
|
||||
import org.springframework.util.Assert;
|
||||
@@ -259,7 +265,11 @@ public class PlaceholderTargetSource extends SimpleBeanTargetSource implements I
|
||||
}
|
||||
|
||||
private Object getPropertyFromContext(String key) {
|
||||
BeanWrapper wrapper = new BeanWrapperImpl(contextFactory.getContext());
|
||||
Object context = contextFactory.getContext();
|
||||
if (context==null) {
|
||||
throw new IllegalStateException("No context available while replacing placeholders.");
|
||||
}
|
||||
BeanWrapper wrapper = new BeanWrapperImpl(context);
|
||||
if (wrapper.isReadableProperty(key)) {
|
||||
return wrapper.getPropertyValue(key);
|
||||
}
|
||||
@@ -297,6 +307,7 @@ public class PlaceholderTargetSource extends SimpleBeanTargetSource implements I
|
||||
super(new PlaceholderStringValueResolver(typeConverter));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Object resolveValue(Object value) {
|
||||
|
||||
if (value instanceof TypedStringValue) {
|
||||
@@ -304,21 +315,49 @@ public class PlaceholderTargetSource extends SimpleBeanTargetSource implements I
|
||||
TypedStringValue typedStringValue = (TypedStringValue) value;
|
||||
String stringValue = typedStringValue.getValue();
|
||||
if (stringValue != null) {
|
||||
|
||||
// If the value is a whole key, try to simply replace it from context.
|
||||
|
||||
// If the value is a whole key, try to simply replace it
|
||||
// from context.
|
||||
if (isKey(stringValue)) {
|
||||
Object result = getPropertyFromContext(extractKey(stringValue));
|
||||
String key = extractKey(stringValue);
|
||||
Object result = getPropertyFromContext(key);
|
||||
if (result != null) {
|
||||
value = result;
|
||||
logger.debug(String.format("Resolved %%{%s} to obtain [%s]", key, result));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Otherwise it might contain embedded keys so we try to replace those
|
||||
// Otherwise it might contain embedded keys so we try to
|
||||
// replace those
|
||||
String visitedString = resolveStringValue(stringValue);
|
||||
typedStringValue.setValue(visitedString);
|
||||
}
|
||||
}
|
||||
|
||||
} else if (value instanceof Map) {
|
||||
|
||||
Map map = (Map) value;
|
||||
Map newValue = new ManagedMap(map.size());
|
||||
newValue.putAll(map);
|
||||
super.visitMap(newValue);
|
||||
value = newValue;
|
||||
|
||||
} else if (value instanceof List) {
|
||||
|
||||
List list = (List) value;
|
||||
List newValue = new ManagedList(list.size());
|
||||
newValue.addAll(list);
|
||||
super.visitList(newValue);
|
||||
value = newValue;
|
||||
|
||||
} else if (value instanceof Set) {
|
||||
|
||||
Set list = (Set) value;
|
||||
Set newValue = new ManagedSet(list.size());
|
||||
newValue.addAll(list);
|
||||
super.visitSet(newValue);
|
||||
value = newValue;
|
||||
|
||||
}
|
||||
else {
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -56,6 +57,10 @@ public class MultipleContextPlaceholderTargetSourceTests {
|
||||
@Qualifier("list")
|
||||
private TestBean list;
|
||||
|
||||
@Autowired
|
||||
@Qualifier("map")
|
||||
private TestBean map;
|
||||
|
||||
@After
|
||||
public void removeContext() {
|
||||
contextFactory.clearContext();
|
||||
@@ -100,6 +105,23 @@ public class MultipleContextPlaceholderTargetSourceTests {
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleValueInMap() throws Exception {
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
final String value = "foo" + i;
|
||||
contextFactory.setContext(this);
|
||||
attributes = Collections.singletonMap("foo", value);
|
||||
try {
|
||||
assertEquals("foo" + i, map.getMap().get("foo"));
|
||||
}
|
||||
finally {
|
||||
contextFactory.clearContext();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Test context: attributes=" + attributes;
|
||||
@@ -110,6 +132,8 @@ public class MultipleContextPlaceholderTargetSourceTests {
|
||||
|
||||
private List<String> names = new ArrayList<String>();
|
||||
|
||||
private Map<String, String> map = new HashMap<String, String>();
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
@@ -125,6 +149,15 @@ public class MultipleContextPlaceholderTargetSourceTests {
|
||||
public void setNames(List<String> names) {
|
||||
this.names.addAll(names);
|
||||
}
|
||||
|
||||
public Map<String, String> getMap() {
|
||||
return new HashMap<String, String>(map);
|
||||
}
|
||||
|
||||
public void setMap(Map<String, String> map) {
|
||||
this.map.putAll(map);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class SimpleContextFactory extends ContextFactorySupport {
|
||||
|
||||
@@ -26,12 +26,36 @@
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="simpleTarget" class="org.springframework.batch.core.scope.util.MultipleContextPlaceholderTargetSourceTests$TestBean">
|
||||
<bean id="map" class="org.springframework.aop.framework.ProxyFactoryBean">
|
||||
<property name="targetSource">
|
||||
<bean
|
||||
class="org.springframework.batch.core.scope.util.PlaceholderTargetSource">
|
||||
<property name="contextFactory" ref="context" />
|
||||
<property name="targetBeanName" value="mapTarget" />
|
||||
</bean>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="simpleTarget"
|
||||
class="org.springframework.batch.core.scope.util.MultipleContextPlaceholderTargetSourceTests$TestBean">
|
||||
<property name="name" value="%{attributes[foo]}" />
|
||||
</bean>
|
||||
|
||||
<bean id="listTarget" class="org.springframework.batch.core.scope.util.MultipleContextPlaceholderTargetSourceTests$TestBean">
|
||||
<property name="names" value="%{attributes[foo]}" />
|
||||
<bean id="listTarget"
|
||||
class="org.springframework.batch.core.scope.util.MultipleContextPlaceholderTargetSourceTests$TestBean">
|
||||
<property name="names">
|
||||
<list>
|
||||
<value>%{attributes[foo]}</value></list>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="mapTarget"
|
||||
class="org.springframework.batch.core.scope.util.MultipleContextPlaceholderTargetSourceTests$TestBean">
|
||||
<property name="map">
|
||||
<map>
|
||||
<entry key="foo" value="%{attributes[foo]}" />
|
||||
</map>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="context"
|
||||
|
||||
Reference in New Issue
Block a user