Skip to content

Commit 1c306df

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 bf8a339 commit 1c306df

File tree

2 files changed

+30
-5
lines changed

2 files changed

+30
-5
lines changed

spring-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;
@@ -116,6 +117,14 @@ private List<Method> findNonVoidWriteMethods(MethodDescriptor[] methodDescriptor
116117
matches.add(method);
117118
}
118119
}
120+
// sort non-void returning write methods to guard against the ill effects of
121+
// non-deterministic sorting of methods returned from Class#getDeclaredMethods
122+
// under JDK 7. See http://bugs.sun.com/view_bug.do?bug_id=7023180
123+
Collections.sort(matches, new Comparator<Method>() {
124+
public int compare(Method m1, Method m2) {
125+
return m2.toString().compareTo(m1.toString());
126+
}
127+
});
119128
return matches;
120129
}
121130

@@ -261,7 +270,7 @@ public SimpleNonIndexedPropertyDescriptor(PropertyDescriptor original)
261270
public SimpleNonIndexedPropertyDescriptor(String propertyName,
262271
Method readMethod, Method writeMethod) throws IntrospectionException {
263272

264-
super(propertyName, readMethod, writeMethod);
273+
super(propertyName, null, null);
265274
this.setReadMethod(readMethod);
266275
this.setWriteMethod(writeMethod);
267276
this.propertyType = findPropertyType(readMethod, writeMethod);
@@ -350,7 +359,7 @@ public SimpleIndexedPropertyDescriptor(String propertyName,
350359
Method indexedReadMethod, Method indexedWriteMethod)
351360
throws IntrospectionException {
352361

353-
super(propertyName, readMethod, writeMethod, indexedReadMethod, indexedWriteMethod);
362+
super(propertyName, null, null, null, null);
354363
this.setReadMethod(readMethod);
355364
this.setWriteMethod(writeMethod);
356365
this.propertyType = findPropertyType(readMethod, writeMethod);
@@ -495,7 +504,7 @@ public static void copyNonMethodProperties(PropertyDescriptor source, PropertyDe
495504
// copy all attributes (emulating behavior of private FeatureDescriptor#addTable)
496505
Enumeration<String> keys = source.attributeNames();
497506
while (keys.hasMoreElements()) {
498-
String key = (String)keys.nextElement();
507+
String key = keys.nextElement();
499508
target.setValue(key, source.getValue(key));
500509
}
501510

spring-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 {
@@ -692,7 +707,7 @@ public void overloadedNonStandardWriteMethodsOnly_orderB() throws IntrospectionE
692707

693708
for (PropertyDescriptor pd : ebi.getPropertyDescriptors()) {
694709
if (pd.getName().equals("foo")) {
695-
assertThat(pd.getWriteMethod(), is(C.class.getMethod("setFoo", int.class)));
710+
assertThat(pd.getWriteMethod(), is(C.class.getMethod("setFoo", String.class)));
696711
return;
697712
}
698713
}
@@ -730,7 +745,7 @@ public void reproSpr8522() throws IntrospectionException {
730745
assertThat(hasReadMethodForProperty(ebi, "dateFormat"), is(false));
731746
assertThat(hasWriteMethodForProperty(ebi, "dateFormat"), is(true));
732747
assertThat(hasIndexedReadMethodForProperty(ebi, "dateFormat"), is(false));
733-
assertThat(hasIndexedWriteMethodForProperty(ebi, "dateFormat"), is(true));
748+
assertThat(hasIndexedWriteMethodForProperty(ebi, "dateFormat"), is(trueUntilJdk17()));
734749
}
735750

736751
@Test
@@ -941,4 +956,5 @@ public void setAddress(int index, String addr) { }
941956
assertThat(hasIndexedWriteMethodForProperty(bi, "address"), is(true));
942957
}
943958
}
959+
944960
}

0 commit comments

Comments
 (0)