FULL PRODUCT VERSION :
java version "1.6.0_13"
Java(TM) SE Runtime Environment (build 1.6.0_13-b03)
Java HotSpot(TM) Client VM (build 11.3-b02, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows [Version 6.0.6001]
A DESCRIPTION OF THE PROBLEM :
It's probably easier just to read the code sample, but here's an attempt at a coherent explanation.
My bean class has a property with both getter and setter, but Introspector.getBeanInfo describes it as a read-only property. The bean class implements an interface that declares the getter but not the setter. The getter in the interface returns another interface, but the getter in the bean class returns an implementation of that interface. The setter in the bean class takes the implementation as its argument.
This confuses Introspector.processPropertyDescriptors, which attempts to reconcile the two versions of the getter with the one version of the setter. It simply chooses the "latest" version of the getter, which happens to be the one that returns the interface. The setter, which takes the implementation, does not match this, so it gets discarded.
It looks like Introspector.processPropertyDescriptors needs to be rather more sophisticated. It should somehow try to match all versions of the getter against all versions of the setter, rather than just ignoring all but the last one that Class.getDeclaredMethods happened to return.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the attached code.
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
No exception.
ACTUAL -
Exception in thread "main" java.lang.Exception: Where's my setter?
at IntrospectorBug.main(IntrospectorBug.java:32)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
interface Foo {
}
class FooImpl implements Foo {
}
interface Bar {
Foo getFoo();
}
class BarImpl implements Bar {
private FooImpl foo;
public FooImpl getFoo() {
return foo;
}
public void setFoo(FooImpl val) {
foo = val;
}
}
public class IntrospectorBug {
public static void main(String[] args) throws Exception {
BeanInfo info = Introspector.getBeanInfo(BarImpl.class);
for (PropertyDescriptor desc : info.getPropertyDescriptors())
if (desc.getName().equals("foo") && (desc.getWriteMethod() == null))
throw new Exception("Where's my setter?");
}
}
---------- END SOURCE ----------