Skip to content

Commit 26d5ef9

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 Backport-Commit: aa3e0be (forward-ported via cherry-pick from 3.1.x)
1 parent 6888a6f commit 26d5ef9

File tree

2 files changed

+28
-11
lines changed

2 files changed

+28
-11
lines changed

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

Lines changed: 11 additions & 2 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> findCandidateWriteMethods(MethodDescriptor[] methodDescript
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

@@ -264,7 +273,7 @@ public SimpleNonIndexedPropertyDescriptor(PropertyDescriptor original)
264273
public SimpleNonIndexedPropertyDescriptor(String propertyName,
265274
Method readMethod, Method writeMethod) throws IntrospectionException {
266275

267-
super(propertyName, readMethod, writeMethod);
276+
super(propertyName, null, null);
268277
this.setReadMethod(readMethod);
269278
this.setWriteMethod(writeMethod);
270279
this.propertyType = findPropertyType(readMethod, writeMethod);
@@ -353,7 +362,7 @@ public SimpleIndexedPropertyDescriptor(String propertyName,
353362
Method indexedReadMethod, Method indexedWriteMethod)
354363
throws IntrospectionException {
355364

356-
super(propertyName, readMethod, writeMethod, indexedReadMethod, indexedWriteMethod);
365+
super(propertyName, null, null, null, null);
357366
this.setReadMethod(readMethod);
358367
this.setWriteMethod(writeMethod);
359368
this.propertyType = findPropertyType(readMethod, writeMethod);

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

Lines changed: 17 additions & 9 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

@@ -192,7 +193,6 @@ public Number getProperty1() {
192193
}
193194
}
194195
class Child extends Parent {
195-
@Override
196196
public Integer getProperty1() {
197197
return 2;
198198
}
@@ -214,7 +214,6 @@ interface Spr9453<T> {
214214
@Test
215215
public void cornerSpr9453() throws IntrospectionException {
216216
final class Bean implements Spr9453<Class<?>> {
217-
@Override
218217
public Class<?> getProp() {
219218
return null;
220219
}
@@ -583,16 +582,28 @@ class C {
583582
}
584583
}
585584

585+
/**
586+
* Prior to SPR-10111 (a follow-up fix for SPR-9702), this method would throw an
587+
* IntrospectionException regarding a "type mismatch between indexed and non-indexed
588+
* methods" intermittently (approximately one out of every four times) under JDK 7
589+
* due to non-deterministic results from {@link Class#getDeclaredMethods()}.
590+
* See http://bugs.sun.com/view_bug.do?bug_id=7023180
591+
* @see #cornerSpr9702()
592+
*/
593+
@Test
594+
public void cornerSpr10111() throws Exception {
595+
new ExtendedBeanInfo(Introspector.getBeanInfo(BigDecimal.class));
596+
}
597+
598+
586599
@Test
587600
public void subclassWriteMethodWithCovariantReturnType() throws IntrospectionException {
588601
@SuppressWarnings("unused") class B {
589602
public String getFoo() { return null; }
590603
public Number setFoo(String foo) { return null; }
591604
}
592605
class C extends B {
593-
@Override
594606
public String getFoo() { return null; }
595-
@Override
596607
public Integer setFoo(String foo) { return null; }
597608
}
598609

@@ -695,7 +706,7 @@ public void overloadedNonStandardWriteMethodsOnly_orderB() throws IntrospectionE
695706

696707
for (PropertyDescriptor pd : ebi.getPropertyDescriptors()) {
697708
if (pd.getName().equals("foo")) {
698-
assertThat(pd.getWriteMethod(), is(C.class.getMethod("setFoo", int.class)));
709+
assertThat(pd.getWriteMethod(), is(C.class.getMethod("setFoo", String.class)));
699710
return;
700711
}
701712
}
@@ -733,7 +744,7 @@ public void reproSpr8522() throws IntrospectionException {
733744
assertThat(hasReadMethodForProperty(ebi, "dateFormat"), is(false));
734745
assertThat(hasWriteMethodForProperty(ebi, "dateFormat"), is(true));
735746
assertThat(hasIndexedReadMethodForProperty(ebi, "dateFormat"), is(false));
736-
assertThat(hasIndexedWriteMethodForProperty(ebi, "dateFormat"), is(true));
747+
assertThat(hasIndexedWriteMethodForProperty(ebi, "dateFormat"), is(trueUntilJdk17()));
737748
}
738749

739750
@Test
@@ -864,7 +875,6 @@ interface BookOperations {
864875
}
865876

866877
interface TextBookOperations extends BookOperations {
867-
@Override
868878
TextBook getBook();
869879
}
870880

@@ -874,7 +884,6 @@ public void setBook(Book book) { }
874884
}
875885

876886
class LawLibrary extends Library implements TextBookOperations {
877-
@Override
878887
public LawBook getBook() { return null; }
879888
}
880889

@@ -889,7 +898,6 @@ public boolean isTargetMethod() {
889898
}
890899

891900
class B extends A {
892-
@Override
893901
public boolean isTargetMethod() {
894902
return false;
895903
}

0 commit comments

Comments
 (0)