Skip to content

Commit aa3e0be

Browse files
committed
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
1 parent 28c7760 commit aa3e0be

File tree

2 files changed

+30
-5
lines changed

2 files changed

+30
-5
lines changed

org.springframework.beans/src/main/java/org/springframework/beans/ExtendedBeanInfo.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.lang.reflect.Modifier;
3232

3333
import java.util.ArrayList;
34+
import java.util.Collections;
3435
import java.util.Comparator;
3536
import java.util.Enumeration;
3637
import java.util.List;
@@ -115,6 +116,14 @@ private List<Method> findNonVoidWriteMethods(MethodDescriptor[] methodDescriptor
115116
matches.add(method);
116117
}
117118
}
119+
// sort non-void returning write methods to guard against the ill effects of
120+
// non-deterministic sorting of methods returned from Class#getDeclaredMethods
121+
// under JDK 7. See http://bugs.sun.com/view_bug.do?bug_id=7023180
122+
Collections.sort(matches, new Comparator<Method>() {
123+
public int compare(Method m1, Method m2) {
124+
return m2.toString().compareTo(m1.toString());
125+
}
126+
});
118127
return matches;
119128
}
120129

@@ -260,7 +269,7 @@ public SimpleNonIndexedPropertyDescriptor(PropertyDescriptor original)
260269
public SimpleNonIndexedPropertyDescriptor(String propertyName,
261270
Method readMethod, Method writeMethod) throws IntrospectionException {
262271

263-
super(propertyName, readMethod, writeMethod);
272+
super(propertyName, null, null);
264273
this.setReadMethod(readMethod);
265274
this.setWriteMethod(writeMethod);
266275
this.propertyType = findPropertyType(readMethod, writeMethod);
@@ -349,7 +358,7 @@ public SimpleIndexedPropertyDescriptor(String propertyName,
349358
Method indexedReadMethod, Method indexedWriteMethod)
350359
throws IntrospectionException {
351360

352-
super(propertyName, readMethod, writeMethod, indexedReadMethod, indexedWriteMethod);
361+
super(propertyName, null, null, null, null);
353362
this.setReadMethod(readMethod);
354363
this.setWriteMethod(writeMethod);
355364
this.propertyType = findPropertyType(readMethod, writeMethod);
@@ -494,7 +503,7 @@ public static void copyNonMethodProperties(PropertyDescriptor source, PropertyDe
494503
// copy all attributes (emulating behavior of private FeatureDescriptor#addTable)
495504
Enumeration<String> keys = source.attributeNames();
496505
while (keys.hasMoreElements()) {
497-
String key = (String)keys.nextElement();
506+
String key = keys.nextElement();
498507
target.setValue(key, source.getValue(key));
499508
}
500509

org.springframework.beans/src/test/java/org/springframework/beans/ExtendedBeanInfoTests.java

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import java.beans.PropertyDescriptor;
2424

2525
import java.lang.reflect.Method;
26+
import java.math.BigDecimal;
2627

2728
import org.junit.Test;
2829

@@ -582,6 +583,20 @@ class C {
582583
}
583584
}
584585

586+
/**
587+
* Prior to SPR-10111 (a follow-up fix for SPR-9702), this method would throw an
588+
* IntrospectionException regarding a "type mismatch between indexed and non-indexed
589+
* methods" intermittently (approximately one out of every four times) under JDK 7
590+
* due to non-deterministic results from {@link Class#getDeclaredMethods()}.
591+
* See http://bugs.sun.com/view_bug.do?bug_id=7023180
592+
* @see #cornerSpr9702()
593+
*/
594+
@Test
595+
public void cornerSpr10111() throws Exception {
596+
new ExtendedBeanInfo(Introspector.getBeanInfo(BigDecimal.class));
597+
}
598+
599+
585600
@Test
586601
public void subclassWriteMethodWithCovariantReturnType() throws IntrospectionException {
587602
@SuppressWarnings("unused") class B {
@@ -694,7 +709,7 @@ public void overloadedNonStandardWriteMethodsOnly_orderB() throws IntrospectionE
694709

695710
for (PropertyDescriptor pd : ebi.getPropertyDescriptors()) {
696711
if (pd.getName().equals("foo")) {
697-
assertThat(pd.getWriteMethod(), is(C.class.getMethod("setFoo", int.class)));
712+
assertThat(pd.getWriteMethod(), is(C.class.getMethod("setFoo", String.class)));
698713
return;
699714
}
700715
}
@@ -732,7 +747,7 @@ public void reproSpr8522() throws IntrospectionException {
732747
assertThat(hasReadMethodForProperty(ebi, "dateFormat"), is(false));
733748
assertThat(hasWriteMethodForProperty(ebi, "dateFormat"), is(true));
734749
assertThat(hasIndexedReadMethodForProperty(ebi, "dateFormat"), is(false));
735-
assertThat(hasIndexedWriteMethodForProperty(ebi, "dateFormat"), is(true));
750+
assertThat(hasIndexedWriteMethodForProperty(ebi, "dateFormat"), is(trueUntilJdk17()));
736751
}
737752

738753
@Test
@@ -943,4 +958,5 @@ public void setAddress(int index, String addr) { }
943958
assertThat(hasIndexedWriteMethodForProperty(bi, "address"), is(true));
944959
}
945960
}
961+
946962
}

0 commit comments

Comments
 (0)