Browse Source

Handle non-void write methods deterministically

This change resolves a specific issue with processing
java.math.BigDecimal via ExtendedBeanInfo. BigDecimal has a particular
constellation of #setScale methods that, prior to this change, had the
potential to cause ExtendedBeanInfo to throw an IntrospectionException
depending on the order in which the methods were processed.

Because JDK 7 no longer returns deterministic results from
Class#getDeclaredMethods, it became a genuine possibility - indeed a
statistical certainty that the 'wrong' setScale method handling order
happens sooner or later. Typically one could observe this failure once
out of every four test runs.

This commit introduces deterministic method ordering of all discovered
non-void returning write methods in such a way that solves the problem
for BigDecimal as well as for any other class having a similar method
arrangement.

Also:

 - Remove unnecessary cast

 - Pass no method information to PropertyDescriptor superclasses when
   invoking super(...). This ensures that any 'type mismatch'
   IntrospectionExceptions are handled locally in ExtendedBeanInfo and
   its Simple* PropertyDescriptor variants where we have full control.

Issue: SPR-10111, SPR-9702
pull/83/merge
Chris Beams 12 years ago
parent
commit
1c306dffcd
  1. 15
      spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java
  2. 20
      spring-beans/src/test/java/org/springframework/beans/ExtendedBeanInfoTests.java

15
spring-beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java

@ -31,6 +31,7 @@ import java.lang.reflect.Method;
import java.lang.reflect.Modifier; import java.lang.reflect.Modifier;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Enumeration; import java.util.Enumeration;
import java.util.List; import java.util.List;
@ -116,6 +117,14 @@ class ExtendedBeanInfo implements BeanInfo {
matches.add(method); matches.add(method);
} }
} }
// sort non-void returning write methods to guard against the ill effects of
// non-deterministic sorting of methods returned from Class#getDeclaredMethods
// under JDK 7. See http://bugs.sun.com/view_bug.do?bug_id=7023180
Collections.sort(matches, new Comparator<Method>() {
public int compare(Method m1, Method m2) {
return m2.toString().compareTo(m1.toString());
}
});
return matches; return matches;
} }
@ -261,7 +270,7 @@ class SimpleNonIndexedPropertyDescriptor extends PropertyDescriptor {
public SimpleNonIndexedPropertyDescriptor(String propertyName, public SimpleNonIndexedPropertyDescriptor(String propertyName,
Method readMethod, Method writeMethod) throws IntrospectionException { Method readMethod, Method writeMethod) throws IntrospectionException {
super(propertyName, readMethod, writeMethod); super(propertyName, null, null);
this.setReadMethod(readMethod); this.setReadMethod(readMethod);
this.setWriteMethod(writeMethod); this.setWriteMethod(writeMethod);
this.propertyType = findPropertyType(readMethod, writeMethod); this.propertyType = findPropertyType(readMethod, writeMethod);
@ -350,7 +359,7 @@ class SimpleIndexedPropertyDescriptor extends IndexedPropertyDescriptor {
Method indexedReadMethod, Method indexedWriteMethod) Method indexedReadMethod, Method indexedWriteMethod)
throws IntrospectionException { throws IntrospectionException {
super(propertyName, readMethod, writeMethod, indexedReadMethod, indexedWriteMethod); super(propertyName, null, null, null, null);
this.setReadMethod(readMethod); this.setReadMethod(readMethod);
this.setWriteMethod(writeMethod); this.setWriteMethod(writeMethod);
this.propertyType = findPropertyType(readMethod, writeMethod); this.propertyType = findPropertyType(readMethod, writeMethod);
@ -495,7 +504,7 @@ class PropertyDescriptorUtils {
// copy all attributes (emulating behavior of private FeatureDescriptor#addTable) // copy all attributes (emulating behavior of private FeatureDescriptor#addTable)
Enumeration<String> keys = source.attributeNames(); Enumeration<String> keys = source.attributeNames();
while (keys.hasMoreElements()) { while (keys.hasMoreElements()) {
String key = (String)keys.nextElement(); String key = keys.nextElement();
target.setValue(key, source.getValue(key)); target.setValue(key, source.getValue(key));
} }

20
spring-beans/src/test/java/org/springframework/beans/ExtendedBeanInfoTests.java

@ -23,6 +23,7 @@ import java.beans.Introspector;
import java.beans.PropertyDescriptor; import java.beans.PropertyDescriptor;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.math.BigDecimal;
import org.junit.Test; import org.junit.Test;
@ -582,6 +583,20 @@ public class ExtendedBeanInfoTests {
} }
} }
/**
* Prior to SPR-10111 (a follow-up fix for SPR-9702), this method would throw an
* IntrospectionException regarding a "type mismatch between indexed and non-indexed
* methods" intermittently (approximately one out of every four times) under JDK 7
* due to non-deterministic results from {@link Class#getDeclaredMethods()}.
* See http://bugs.sun.com/view_bug.do?bug_id=7023180
* @see #cornerSpr9702()
*/
@Test
public void cornerSpr10111() throws Exception {
new ExtendedBeanInfo(Introspector.getBeanInfo(BigDecimal.class));
}
@Test @Test
public void subclassWriteMethodWithCovariantReturnType() throws IntrospectionException { public void subclassWriteMethodWithCovariantReturnType() throws IntrospectionException {
@SuppressWarnings("unused") class B { @SuppressWarnings("unused") class B {
@ -692,7 +707,7 @@ public class ExtendedBeanInfoTests {
for (PropertyDescriptor pd : ebi.getPropertyDescriptors()) { for (PropertyDescriptor pd : ebi.getPropertyDescriptors()) {
if (pd.getName().equals("foo")) { if (pd.getName().equals("foo")) {
assertThat(pd.getWriteMethod(), is(C.class.getMethod("setFoo", int.class))); assertThat(pd.getWriteMethod(), is(C.class.getMethod("setFoo", String.class)));
return; return;
} }
} }
@ -730,7 +745,7 @@ public class ExtendedBeanInfoTests {
assertThat(hasReadMethodForProperty(ebi, "dateFormat"), is(false)); assertThat(hasReadMethodForProperty(ebi, "dateFormat"), is(false));
assertThat(hasWriteMethodForProperty(ebi, "dateFormat"), is(true)); assertThat(hasWriteMethodForProperty(ebi, "dateFormat"), is(true));
assertThat(hasIndexedReadMethodForProperty(ebi, "dateFormat"), is(false)); assertThat(hasIndexedReadMethodForProperty(ebi, "dateFormat"), is(false));
assertThat(hasIndexedWriteMethodForProperty(ebi, "dateFormat"), is(true)); assertThat(hasIndexedWriteMethodForProperty(ebi, "dateFormat"), is(trueUntilJdk17()));
} }
@Test @Test
@ -941,4 +956,5 @@ public class ExtendedBeanInfoTests {
assertThat(hasIndexedWriteMethodForProperty(bi, "address"), is(true)); assertThat(hasIndexedWriteMethodForProperty(bi, "address"), is(true));
} }
} }
} }

Loading…
Cancel
Save