DataBinder uses a default limit of 256 for array/collection auto-growing (SPR-7842)

This commit is contained in:
Juergen Hoeller
2011-07-03 20:39:19 +00:00
parent 4faa5af3f8
commit eb50eec85b
6 changed files with 168 additions and 36 deletions

View File

@@ -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;
}

View File

@@ -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());
}

View File

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