DataBinder uses a default limit of 256 for array/collection auto-growing (SPR-7842)
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -46,6 +46,8 @@ public class BeanPropertyBindingResult extends AbstractPropertyBindingResult imp
|
||||
|
||||
private final boolean autoGrowNestedPaths;
|
||||
|
||||
private final int autoGrowCollectionLimit;
|
||||
|
||||
private transient BeanWrapper beanWrapper;
|
||||
|
||||
|
||||
@@ -55,7 +57,7 @@ public class BeanPropertyBindingResult extends AbstractPropertyBindingResult imp
|
||||
* @param objectName the name of the target object
|
||||
*/
|
||||
public BeanPropertyBindingResult(Object target, String objectName) {
|
||||
this(target, objectName, true);
|
||||
this(target, objectName, true, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -63,11 +65,13 @@ public class BeanPropertyBindingResult extends AbstractPropertyBindingResult imp
|
||||
* @param target the target bean to bind onto
|
||||
* @param objectName the name of the target object
|
||||
* @param autoGrowNestedPaths whether to "auto-grow" a nested path that contains a null value
|
||||
* @param autoGrowCollectionLimit the limit for array and collection auto-growing
|
||||
*/
|
||||
public BeanPropertyBindingResult(Object target, String objectName, boolean autoGrowNestedPaths) {
|
||||
public BeanPropertyBindingResult(Object target, String objectName, boolean autoGrowNestedPaths, int autoGrowCollectionLimit) {
|
||||
super(objectName);
|
||||
this.target = target;
|
||||
this.autoGrowNestedPaths = autoGrowNestedPaths;
|
||||
this.autoGrowCollectionLimit = autoGrowCollectionLimit;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +91,7 @@ public class BeanPropertyBindingResult extends AbstractPropertyBindingResult imp
|
||||
this.beanWrapper = createBeanWrapper();
|
||||
this.beanWrapper.setExtractOldValueForEditor(true);
|
||||
this.beanWrapper.setAutoGrowNestedPaths(this.autoGrowNestedPaths);
|
||||
this.beanWrapper.setAutoGrowCollectionLimit(this.autoGrowCollectionLimit);
|
||||
}
|
||||
return this.beanWrapper;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -105,6 +105,9 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
/** Default object name used for binding: "target" */
|
||||
public static final String DEFAULT_OBJECT_NAME = "target";
|
||||
|
||||
/** Default limit for array and collection growing: 256 */
|
||||
public static final int DEFAULT_AUTO_GROW_COLLECTION_LIMIT = 256;
|
||||
|
||||
|
||||
/**
|
||||
* We'll create a lot of DataBinder instances: Let's use a static logger.
|
||||
@@ -127,6 +130,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
|
||||
private boolean autoGrowNestedPaths = true;
|
||||
|
||||
private int autoGrowCollectionLimit = DEFAULT_AUTO_GROW_COLLECTION_LIMIT;
|
||||
|
||||
private String[] allowedFields;
|
||||
|
||||
private String[] disallowedFields;
|
||||
@@ -199,6 +204,22 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
return this.autoGrowNestedPaths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify the limit for array and collection auto-growing.
|
||||
* <p>Default is 256, preventing OutOfMemoryErrors in case of large indexes.
|
||||
* Raise this limit if your auto-growing needs are unusually high.
|
||||
*/
|
||||
public void setAutoGrowCollectionLimit(int autoGrowCollectionLimit) {
|
||||
this.autoGrowCollectionLimit = autoGrowCollectionLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the current limit for array and collection auto-growing.
|
||||
*/
|
||||
public int getAutoGrowCollectionLimit() {
|
||||
return this.autoGrowCollectionLimit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize standard JavaBean property access for this DataBinder.
|
||||
* <p>This is the default; an explicit call just leads to eager initialization.
|
||||
@@ -207,7 +228,8 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
public void initBeanPropertyAccess() {
|
||||
Assert.state(this.bindingResult == null,
|
||||
"DataBinder is already initialized - call initBeanPropertyAccess before other configuration methods");
|
||||
this.bindingResult = new BeanPropertyBindingResult(getTarget(), getObjectName(), isAutoGrowNestedPaths());
|
||||
this.bindingResult = new BeanPropertyBindingResult(
|
||||
getTarget(), getObjectName(), isAutoGrowNestedPaths(), getAutoGrowCollectionLimit());
|
||||
if (this.conversionService != null) {
|
||||
this.bindingResult.initConversion(this.conversionService);
|
||||
}
|
||||
@@ -509,17 +531,14 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
return this.conversionService;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void registerCustomEditor(Class requiredType, PropertyEditor propertyEditor) {
|
||||
getPropertyEditorRegistry().registerCustomEditor(requiredType, propertyEditor);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void registerCustomEditor(Class requiredType, String field, PropertyEditor propertyEditor) {
|
||||
getPropertyEditorRegistry().registerCustomEditor(requiredType, field, propertyEditor);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public PropertyEditor findCustomEditor(Class requiredType, String propertyPath) {
|
||||
return getPropertyEditorRegistry().findCustomEditor(requiredType, propertyPath);
|
||||
}
|
||||
@@ -700,8 +719,7 @@ public class DataBinder implements PropertyEditorRegistry, TypeConverter {
|
||||
* @throws BindException if there were any errors in the bind operation
|
||||
* @see BindingResult#getModel()
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public Map close() throws BindException {
|
||||
public Map<?, ?> close() throws BindException {
|
||||
if (getBindingResult().hasErrors()) {
|
||||
throw new BindException(getBindingResult());
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -34,6 +34,7 @@ import org.springframework.beans.BeanWithObjectProperty;
|
||||
import org.springframework.beans.DerivedTestBean;
|
||||
import org.springframework.beans.ITestBean;
|
||||
import org.springframework.beans.IndexedTestBean;
|
||||
import org.springframework.beans.InvalidPropertyException;
|
||||
import org.springframework.beans.MutablePropertyValues;
|
||||
import org.springframework.beans.NotWritablePropertyException;
|
||||
import org.springframework.beans.NullValueInNestedPathException;
|
||||
@@ -1486,7 +1487,6 @@ public class DataBinderTests extends TestCase {
|
||||
MutablePropertyValues mpvs = new MutablePropertyValues();
|
||||
mpvs.add("name", name);
|
||||
mpvs.add("beanName", beanName);
|
||||
|
||||
binder.bind(mpvs);
|
||||
|
||||
assertEquals(name, testBean.getName());
|
||||
@@ -1495,6 +1495,62 @@ public class DataBinderTests extends TestCase {
|
||||
assertEquals("beanName", disallowedFields[0]);
|
||||
}
|
||||
|
||||
public void testAutoGrowWithinDefaultLimit() throws Exception {
|
||||
TestBean testBean = new TestBean();
|
||||
DataBinder binder = new DataBinder(testBean, "testBean");
|
||||
|
||||
MutablePropertyValues mpvs = new MutablePropertyValues();
|
||||
mpvs.add("friends[4]", "");
|
||||
binder.bind(mpvs);
|
||||
|
||||
assertEquals(5, testBean.getFriends().size());
|
||||
}
|
||||
|
||||
public void testAutoGrowBeyondDefaultLimit() throws Exception {
|
||||
TestBean testBean = new TestBean();
|
||||
DataBinder binder = new DataBinder(testBean, "testBean");
|
||||
|
||||
MutablePropertyValues mpvs = new MutablePropertyValues();
|
||||
mpvs.add("friends[256]", "");
|
||||
try {
|
||||
binder.bind(mpvs);
|
||||
fail("Should have thrown InvalidPropertyException");
|
||||
}
|
||||
catch (InvalidPropertyException ex) {
|
||||
// expected
|
||||
assertTrue(ex.getRootCause() instanceof IndexOutOfBoundsException);
|
||||
}
|
||||
}
|
||||
|
||||
public void testAutoGrowWithinCustomLimit() throws Exception {
|
||||
TestBean testBean = new TestBean();
|
||||
DataBinder binder = new DataBinder(testBean, "testBean");
|
||||
binder.setAutoGrowCollectionLimit(10);
|
||||
|
||||
MutablePropertyValues mpvs = new MutablePropertyValues();
|
||||
mpvs.add("friends[4]", "");
|
||||
binder.bind(mpvs);
|
||||
|
||||
assertEquals(5, testBean.getFriends().size());
|
||||
}
|
||||
|
||||
public void testAutoGrowBeyondCustomLimit() throws Exception {
|
||||
TestBean testBean = new TestBean();
|
||||
DataBinder binder = new DataBinder(testBean, "testBean");
|
||||
binder.setAutoGrowCollectionLimit(10);
|
||||
|
||||
MutablePropertyValues mpvs = new MutablePropertyValues();
|
||||
mpvs.add("friends[16]", "");
|
||||
try {
|
||||
binder.bind(mpvs);
|
||||
fail("Should have thrown InvalidPropertyException");
|
||||
}
|
||||
catch (InvalidPropertyException ex) {
|
||||
// expected
|
||||
assertTrue(ex.getRootCause() instanceof IndexOutOfBoundsException);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class BeanWithIntegerList {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user