A menudo utilizo DWR para aplicaciones en JAVA que usan comportamiento AJAX. El otro dia me encontre con un caso extraño donde las propiedades de uno de mis objetos no estaban siendo asignados adecuadamente.
Mire en el código interno de DWR y me di cuenta de que usan la clase Introspector en el BeanConverter. Otras librerías como Apache commons BeanUtils and PropertyUtils tambien utilizan esta clase para asignación e información sobre propiedades de Javabeans.
Probablemente hay algo que no es obvio o se me ha pasado pero desde luego la forma en la que la clase Introspector descubre propiedades en los javabeans parece extraño.
Si una clase implementa un interfaz con retornos de clase diferente a los declarados en el interfaz aun así siendo compatible con este el Introspector no encuentra los métodos adecuados para cambiar las propiedades.
He escrito este corto ejemplo con todas las clases en un solo archivo pero también puedes probarlo tu mismo con tus clases y veras que el comportamiento no es normal. He escrito un pequeño método que utliza el Reflection API para encontrar adecuadamente el método de escritura de la propiedad aunque no es idea el encontrarlo de esta forma
No soy un experto en la especificación de los Java beans pero desde luego el hecho de que una clase implemente un interfaz correctamente haga que el Introspector no encuentre los métodos de escritura me parece incorrecto. Si tienes una opinión diferente me gustaría escucharla.
He encontrado también esta entrada en los foros de Sun donde otras personas se han encontrado con el mismo caso.
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