We use DWR extensively in our AJAX enabled apps. Well the other day I found this weird case where some of our beans properties were not being properly set when being converted from Javascript to Java.
Looking at the source I saw how DWR uses the Introspector on its BeanConverter. This also true for other utils such as Apache commons BeanUtils and PropertyUtils.
I’m Probably missing something obvious here, but regardles of this beans conforming to the Javabeans spec or not the way the introspector finds setter and getters seems backwards to me.
If a bean implements an interface with different return types on its getter than the interface but compatible with the interface the Introspector is unable to see that there are valid accessors in the bean.
I wrote a very simple test case that shows this behavior and possible nasty workaround using reflection.
I wrote all classes and interfaces in the same file but you can use the main method to test with your own classes and you’ll see that the behavior is the same.
I’m not an expert in the javabeans spec and if I’m missing something obvious please let me know.
I also found this post at sun’s forums where other people think this is backwards .
http://forums.sun.com/thread.jspa?threadID=5404791
import java.beans.BeanInfo; import java.beans.IntrospectionException; import java.beans.Introspector; import java.beans.PropertyDescriptor; import java.io.Serializable; import java.lang.reflect.Method; /** * Shows odd behavior when using an introspector from the java.beans api */ public class IntrospectorPossibleBugTest { /** * Marker interface for all locations */ interface Location { } /** * A City is a location */ class City implements Location { } /** * Marker interface for people, a person must have a location assigned */ interface Person { Location getLocation(); } /** * A worker is a Person and should have a location that is actually a City */ static class Worker implements Person, Serializable { private City location; public City getLocation() { return location; } public void setLocation(City location) { this.location = location; } } /** * This small test cases shows the odd behavior. * Even though Worker conforms with the getters and setters for the javabean spec the * Introspector seems to not be able to find any setter for the property location. * Seems like instead of following a bottom up loockup for property accessors it starts and stops once it * finds a compatible property in one of the implemented interfaces. */ public static void main(String... args) throws IntrospectionException { BeanInfo info = Introspector.getBeanInfo(Worker.class); PropertyDescriptor[] descriptors = info.getPropertyDescriptors(); for (PropertyDescriptor descriptor : descriptors) { if (!descriptor.getName().equals("class")) { System.out.println("property: \t\t\t\t\t\t" + descriptor.getName()); System.out.println("getter: \t\t\t\t\t\t" + descriptor.getReadMethod()); System.out.println("getter returns: \t\t\t\t" + descriptor.getReadMethod().getReturnType()); System.out.println("setter: \t\t\t\t\t\t" + descriptor.getWriteMethod()); String propertyName = descriptor.getName(); String setterName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1); Method setter = findMethodImplFirst(Worker.class, setterName, City.class); System.out.println("setter with reflection: \t\t" + setter); } } } /** * workaround for introspector odd behavior with javabeans that implement interfaces with comaptible return types * but instrospection is unable to find the right accessors * * @param currentTargetClass the class being evaluated * @param methodName the method name we are looking for * @param argTypes the arg types for the method name * @return a method if found */ public static Method findMethodImplFirst(Class<?> currentTargetClass, String methodName, Class<?>... argTypes) { Method method = null; if (currentTargetClass != null && methodName != null) { try { method = currentTargetClass.getMethod(methodName, argTypes); } catch (Throwable t) { // nothing we can do but continue } //Is the method in one of our parent classes if (method == null) { Class<?> superclass = currentTargetClass.getSuperclass(); if (!superclass.equals(Object.class)) { method = findMethodImplFirst(superclass, methodName, argTypes); } } } return method; } }
output:
property: location
getter: public InstrospectorPossibleBugTest$Location InstrospectorPossibleBugTest$Worker.getLocation()
getter returns: interface InstrospectorPossibleBugTest$Location
setter: null
setter with reflection: public void InstrospectorPossibleBugTest$Worker.setLocation(InstrospectorPossibleBugTest$City)

A related sun Bug mentioned at the DWR forums:
http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4275879
and the DWR forum’s entry
http://www.nabble.com/DWR-BeanConverter-%2B-Instrospector-use-may-be-affected-by-a-bug-td25418854.html
Yes, just got an email and It is a bug!
Acknowledge by Sun and soon to be published at http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6921637