JDK-6723447 : Introspector doesn't check return type for indexed property setters
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.beans
  • Affected Version: 1.4.2
  • Priority: P4
  • Status: Closed
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2008-07-08
  • Updated: 2011-03-08
  • Resolved: 2011-03-08
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7
7 b66Fixed
Description
FULL PRODUCT VERSION :
java version "1.4.2_10"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_10-b03)
Java HotSpot(TM) Client VM (build 1.4.2_10-b03, mixed mode)

ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
Introspector.getTargetPropertyInfo will create an IndexedPropertyDescriptor for methods that return a non-void type. According to the JavaBeans spec, this is the signature for an indexed property setter:

void setter(int index, PropertyType value);

However, Introspector will report that the following method is an indexed property setter:

public java.math.BigDecimal java.math.BigDecimal.setScale(int,int)

This is because the getTargetPropertyInfo method fails to ensure that the return type is void for the method it is inspecting. The following test is performed on line 564 of Introspector.java:

} else if (argCount == 2) {
  if (argTypes[0] == int.class && name.startsWith(SET_PREFIX)) {

There should be an additional check for:

resultType == void.class

before an instance of IndexedPropertyDescriptor is created.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run the provided code for java.math.BigDecimal:

java SimpleBeanDumper java.math.BigDecimal

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Dumping Properties for: java.math.BigDecimal

class
  propertyType:        class java.lang.Class
  readMethod:          public final native java.lang.Class java.lang.Object.getClass()
  writeMethod:         null
ACTUAL -
Dumping Properties for: java.math.BigDecimal

class
  propertyType:        class java.lang.Class
  readMethod:          public final native java.lang.Class java.lang.Object.getClass()
  writeMethod:         null
scale
  propertyType:        null
  readMethod:          null
  writeMethod:         null
  indexedPropertyType: int
  indexedReadMethod:   null
  indexedWriteMethod:  public java.math.BigDecimal java.math.BigDecimal.setScale(int,int)

ERROR MESSAGES/STACK TRACES THAT OCCUR :
No errors.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.beans.*;
import java.io.*;
import java.util.*;

public class SimpleBeanDumper {

    public static void dump(Class beanClass) throws IntrospectionException {
        dump(beanClass, new PrintWriter(System.out, true));
    }

    public static void dump(Class beanClass, PrintWriter out) throws IntrospectionException {
        if (beanClass == null) {
            throw new NullPointerException("No bean class specified");
        }
        if (out == null) {
            out = new PrintWriter(System.out, true);
        }

        out.println("Dumping Properties for: " + beanClass.getName());
        out.println();

        BeanInfo beanInfo = Introspector.getBeanInfo(beanClass);
        PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors();
        Arrays.sort(descriptors,
                    new Comparator() {
                        public int compare(Object o1, Object o2) {
                            String o1Name = ((PropertyDescriptor)o1).getName();
                            String o2Name = ((PropertyDescriptor)o2).getName();
                            return o1Name.compareTo(o2Name);
                        }
                    });
        for (int i = 0, n = descriptors.length; i < n; i++) {
            PropertyDescriptor descriptor = descriptors[i];
            String propName = descriptor.getName();
            out.println(propName);
            out.println("  propertyType:        " + descriptor.getPropertyType());
            out.println("  readMethod:          " + descriptor.getReadMethod());
            out.println("  writeMethod:         " + descriptor.getWriteMethod());
            if (descriptor instanceof IndexedPropertyDescriptor) {
                IndexedPropertyDescriptor indexedDescriptor = (IndexedPropertyDescriptor)descriptor;
                out.println("  indexedPropertyType: " + indexedDescriptor.getIndexedPropertyType());
                out.println("  indexedReadMethod:   " + indexedDescriptor.getIndexedReadMethod());
                out.println("  indexedWriteMethod:  " + indexedDescriptor.getIndexedWriteMethod());
            }
        }
    }

    public static void main(String[] args) throws Exception {
        Class clazz = MyBean.class;
        if (args.length > 0) {
            clazz = Class.forName(args[0]);
        }
        dump(clazz);
    }

    public static class MyBean implements Serializable {
        public int getID() { return 0; }
        public void setID(int id) {}

        public String getId() { return null; }
        public void setId(String id) {}

        public String[] getFriends() { return null; }
        public String getFriends(int i) { return null; }
        public void setFriends(String[] friends) {}
        public void setFriends(int i, String friend) {}

        public Map getMap() { return null; }
        public void setMap(Map map) {}
    }
}

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

CUSTOMER SUBMITTED WORKAROUND :
None that I'm aware of.

Comments
EVALUATION JavaBeans should use an utility method for detection.
25-12-2008