JDK-6921637 : java.beans.Introspector fails to find setter
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.beans
  • Affected Version: 6u15
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: linux
  • CPU: x86
  • Submitted: 2010-02-01
  • Updated: 2011-01-19
  • Resolved: 2010-02-03
Related Reports
Duplicate :  
Description
FULL PRODUCT VERSION :
java version "1.6.0_15"
Java(TM) SE Runtime Environment (build 1.6.0_15-b03-219)
Java HotSpot(TM) 64-Bit Server VM (build 14.1-b02-90, mixed mode)

A DESCRIPTION OF THE PROBLEM :
If a bean implements an interface with different return types on its getter than the interface but compatible with the interface the Instrospector is unable to see that there are valid accessors in the bean.

This is true not just for interfaces but also for overridden properties using base classes.

It appears that java.beans.Introspector can see that there are valid getters and setters in the class Worker and insist on exposing the read only property in the base class.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. Create a class with a property that exposes only a getter.
2. Create a subclass that extends the foist class and override the property providing getters and setter with a compatible return type but not the same one.
See code below for examples


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The instrospector should find valid write and read method for the bean by naming conventions as stated in the Javabeans spec:

8.3.1
Simple properties
By default, we use design patterns to locate properties by looking for methods of the form:
public <PropertyType> get<PropertyName>(); public void set<PropertyName>(<PropertyType> a);
If we discover a matching pair of ���get<PropertyName>��� and ���set<PropertyName>��� methods that take and return the same type, then we regard these methods as defining a read-write prop- erty whose name will be ���<propertyName>���. We will use the ���get<PropertyName>��� method to get the property value and the ���set<PropertyName>��� method to set the property value. The pair of methods may be located either in the same class or one may be in a base class and the other may be in a derived class.
ACTUAL -
Output:

property: 					name
getter: 						public java.lang.Object Worker.getName()
getter returns: 				class java.lang.Object
setter: 						null

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

/**
 * Shows odd behavior when using an instrospector from the java.beans api
 */
public class InstrospectorPossibleBugTest {

	/**
	 * super class for people
	 */
	public class Person {

		public Object getName() {
			return null;
		}

	}

	/**
	 * A worker is a Person
	 */
	public class Worker extends Person {

		private String name;

		public String getName() {
			return name;
		}

		public void setName(String name) {
			this.name = name;
		}
	}


	/**
	 * This small test cases shows the odd behavior.
	 * Even though Worker conforms with the getters and setters for the javabean spec the
	 * Instrospector seems to not be able to find any setter for the property name.
	 * Seems like instead of following a bottom up lookup for property accessors it starts and stops once it
	 * finds a compatible property in one of the implemented interfaces or parent classes.
	 */
	public static void main(String... args) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
		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, String.class);
				System.out.println("setter with reflection: \t\t" + setter);
			}
		}

	}


}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
/**
	 * workaround for instrospector odd behavior with javabeans that implement interfaces or extend base classes
	 * with compatible 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;
	}