-
Bug
-
Resolution: Not an Issue
-
P4
-
7u51
-
x86_64
-
linux
FULL PRODUCT VERSION :
java version "1.7.0_51"
Java(TM) SE Runtime Environment (build 1.7.0_51-b13)
Java HotSpot(TM) 64-Bit Server VM (build 24.51-b03, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Linux hel 3.2.0-58-virtual #88-Ubuntu SMP Tue Dec 3 17:58:13 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
A DESCRIPTION OF THE PROBLEM :
When using java.beans.Introspector to inspect regular beans that have bridge methods, the result is different before and after GC clears SoftReferences in PropertyDescriptor.
This is similar to bug 6422403, but that one is already resolved in Java 7.
Also similar to bug bug 7172854, but that one involves non-void setters, and this one does not.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
The attached BridgeMethodsTest reproduces the problem. It uses java.beans.Introspector to obtain bean info about "Derived" class.
Next, BridgeMethodsTest simulates GC by clearing (using reflection) some SoftReferences in PropertyDescriptor obtained from java.beans.Introspector.
Next, BridgeMethodsTest checks the property types once again. They should be same as before simulated GC but are different.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
PropertyDescriptor.getPropertyType() should always return the same Class, if no other methods on PropertyDescriptor are called.
ACTUAL -
PropertyDescriptor.getPropertyType() returns different types before and after GC.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.AssertionError: wrong type after GC: x3, got class java.lang.Number, expected class java.lang.Long
at BridgeMethodsTest.check(BridgeMethodsTest.java:78)
at BridgeMethodsTest.checkPropertyType(BridgeMethodsTest.java:55)
at BridgeMethodsTest.main(BridgeMethodsTest.java:48)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class BridgeMethodsTest {
/* @formatter:off */
public interface Propertiful {
Number getX1();
Number getX2();
Number getX3();
Number getX4();
Number getX5();
}
public static class Derived implements Propertiful {
public void setX1(Long newValue) {} @Override public Long getX1() { return null; }
public void setX2(Long newValue) {} @Override public Long getX2() { return null; }
public void setX3(Long newValue) {} @Override public Long getX3() { return null; }
public void setX4(Long newValue) {} @Override public Long getX4() { return null; }
public void setX5(Long newValue) {} @Override public Long getX5() { return null; }
}
/* @formatter:on */
public static void main(String[] args) throws Exception {
Map<String, Class<?>> propertyNameToExpectedType = new HashMap<>();
propertyNameToExpectedType.put("class", Class.class);
propertyNameToExpectedType.put("x1", Long.class);
propertyNameToExpectedType.put("x2", Long.class);
propertyNameToExpectedType.put("x3", Long.class);
propertyNameToExpectedType.put("x4", Long.class);
propertyNameToExpectedType.put("x5", Long.class);
PropertyDescriptor[] pds = Introspector.getBeanInfo(Derived.class).getPropertyDescriptors();
check(pds.length == 6, "Should find 6 PropertyDescriptors, got: " + pds.length);
for (PropertyDescriptor pd : pds) {
check(propertyNameToExpectedType.containsKey(pd.getName()), "unexepcted: " + pd.getName());
Class<?> expectedType = propertyNameToExpectedType.get(pd.getName());
checkPropertyType("before GC", pd, expectedType);
doWhatGCWouldDoAnywaySoonerOrLater(pd);
checkPropertyType("after GC", pd, expectedType);
}
}
private static void checkPropertyType(String when, PropertyDescriptor pd, Class<?> expectedType) {
Class<?> propertyType;
propertyType = pd.getPropertyType();
check(propertyType == expectedType, "wrong type " + when + ": " + pd.getName() + ", got " + propertyType
+ ", expected " + expectedType);
}
private static void doWhatGCWouldDoAnywaySoonerOrLater(PropertyDescriptor pd) throws NoSuchFieldException,
IllegalAccessException {
/*
* Be merciful. If I (or GC) cleared propertyTypeRef and readMethodRef but not writeMethodRef, we would just get
* null (yes, null!) from pd.getPropertyType()
*/
for (String fieldName : Arrays.asList("propertyTypeRef", "readMethodRef", "writeMethodRef")) {
Field fd = PropertyDescriptor.class.getDeclaredField(fieldName);
fd.setAccessible(true);
Reference<?> ref = (Reference<?>) fd.get(pd);
if (ref instanceof WeakReference || ref instanceof SoftReference) {
ref.clear();
}
}
}
private static void check(boolean test, String message) {
if (!test) {
throw new AssertionError(message);
}
}
}
---------- END SOURCE ----------
java version "1.7.0_51"
Java(TM) SE Runtime Environment (build 1.7.0_51-b13)
Java HotSpot(TM) 64-Bit Server VM (build 24.51-b03, mixed mode)
ADDITIONAL OS VERSION INFORMATION :
Linux hel 3.2.0-58-virtual #88-Ubuntu SMP Tue Dec 3 17:58:13 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
A DESCRIPTION OF THE PROBLEM :
When using java.beans.Introspector to inspect regular beans that have bridge methods, the result is different before and after GC clears SoftReferences in PropertyDescriptor.
This is similar to bug 6422403, but that one is already resolved in Java 7.
Also similar to bug bug 7172854, but that one involves non-void setters, and this one does not.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
The attached BridgeMethodsTest reproduces the problem. It uses java.beans.Introspector to obtain bean info about "Derived" class.
Next, BridgeMethodsTest simulates GC by clearing (using reflection) some SoftReferences in PropertyDescriptor obtained from java.beans.Introspector.
Next, BridgeMethodsTest checks the property types once again. They should be same as before simulated GC but are different.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
PropertyDescriptor.getPropertyType() should always return the same Class, if no other methods on PropertyDescriptor are called.
ACTUAL -
PropertyDescriptor.getPropertyType() returns different types before and after GC.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.AssertionError: wrong type after GC: x3, got class java.lang.Number, expected class java.lang.Long
at BridgeMethodsTest.check(BridgeMethodsTest.java:78)
at BridgeMethodsTest.checkPropertyType(BridgeMethodsTest.java:55)
at BridgeMethodsTest.main(BridgeMethodsTest.java:48)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.ref.Reference;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class BridgeMethodsTest {
/* @formatter:off */
public interface Propertiful {
Number getX1();
Number getX2();
Number getX3();
Number getX4();
Number getX5();
}
public static class Derived implements Propertiful {
public void setX1(Long newValue) {} @Override public Long getX1() { return null; }
public void setX2(Long newValue) {} @Override public Long getX2() { return null; }
public void setX3(Long newValue) {} @Override public Long getX3() { return null; }
public void setX4(Long newValue) {} @Override public Long getX4() { return null; }
public void setX5(Long newValue) {} @Override public Long getX5() { return null; }
}
/* @formatter:on */
public static void main(String[] args) throws Exception {
Map<String, Class<?>> propertyNameToExpectedType = new HashMap<>();
propertyNameToExpectedType.put("class", Class.class);
propertyNameToExpectedType.put("x1", Long.class);
propertyNameToExpectedType.put("x2", Long.class);
propertyNameToExpectedType.put("x3", Long.class);
propertyNameToExpectedType.put("x4", Long.class);
propertyNameToExpectedType.put("x5", Long.class);
PropertyDescriptor[] pds = Introspector.getBeanInfo(Derived.class).getPropertyDescriptors();
check(pds.length == 6, "Should find 6 PropertyDescriptors, got: " + pds.length);
for (PropertyDescriptor pd : pds) {
check(propertyNameToExpectedType.containsKey(pd.getName()), "unexepcted: " + pd.getName());
Class<?> expectedType = propertyNameToExpectedType.get(pd.getName());
checkPropertyType("before GC", pd, expectedType);
doWhatGCWouldDoAnywaySoonerOrLater(pd);
checkPropertyType("after GC", pd, expectedType);
}
}
private static void checkPropertyType(String when, PropertyDescriptor pd, Class<?> expectedType) {
Class<?> propertyType;
propertyType = pd.getPropertyType();
check(propertyType == expectedType, "wrong type " + when + ": " + pd.getName() + ", got " + propertyType
+ ", expected " + expectedType);
}
private static void doWhatGCWouldDoAnywaySoonerOrLater(PropertyDescriptor pd) throws NoSuchFieldException,
IllegalAccessException {
/*
* Be merciful. If I (or GC) cleared propertyTypeRef and readMethodRef but not writeMethodRef, we would just get
* null (yes, null!) from pd.getPropertyType()
*/
for (String fieldName : Arrays.asList("propertyTypeRef", "readMethodRef", "writeMethodRef")) {
Field fd = PropertyDescriptor.class.getDeclaredField(fieldName);
fd.setAccessible(true);
Reference<?> ref = (Reference<?>) fd.get(pd);
if (ref instanceof WeakReference || ref instanceof SoftReference) {
ref.clear();
}
}
}
private static void check(boolean test, String message) {
if (!test) {
throw new AssertionError(message);
}
}
}
---------- END SOURCE ----------
- relates to
-
JDK-8043316 PropertyDescriptor loses information when bean class uses generics
-
- Closed
-