BATCH-1378: copy map/list/set to avoid storing wrong value after placeholder binding

This commit is contained in:
dsyer
2009-08-22 11:19:43 +00:00
parent 1956122d57
commit 052f259cb8
3 changed files with 104 additions and 8 deletions

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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"