Announcement Announcement Module
Collapse
No announcement yet.
BeanWrapperImpl uses an Object's interface to determine required data types? Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • BeanWrapperImpl uses an Object's interface to determine required data types?

    I'm having a problem with SimpleFormController trying to bind data types incorrectly. The id of my command object will not bind because it cannot find a PropertyEditor. The implementation of my command class uses Integer as the required type but the Interface is generic and requires anything that extends Number (so we can use either Integer or Longs as the ID). Spring is trying to find a PropertyEditor of type Number (which obviously can't exist because Number is Abstract).

    I'm not sure why it would use the interface's method signature for the type when we told it about the implementation. It should even really know or care about it's interface. If someone could please shed some light on the situation, I'd be forever greatful. I'm not sure how to fix this situation. Forcing a custom mapping for Number to Integer breaks other data types that my form uses (doubles for instance).

    I've confirmed that it sees the required type as Number in BeanWrapperImpl (line 979). findCustomEditor returns null.

    command config for my SimpleFormController:
    Code:
    <property name = "commandClass">
             <value>com.mycompany.myapp.domain.model.Inventory</value>
    </property>
    Inventory is a implementation of GenericEntity. Generic entity is defined as such:

    Code:
    package com.mycompany.common.domain.model;
    
    import java.io.Serializable;
    
    public interface GenericEntity<ID extends Number> extends Serializable {
    
       public ID getId();
    
       public Integer getVersion();
    
       public void setId(ID id);
    
       public void setVersion(Integer version);
    }
    Inventory's implementation of getId uses an Integer for it's implementation (code snipped for brevity):

    Code:
    public class Inventory implements GenericEntity<Integer> {
    
    private Integer id;
    
    public Integer getId() {
      return this.id;
    }
    public void setId(Integer id) {
      this.id = id;
    }
    Binding error:

    Code:
    Failed to convert property value of type [java.lang.String] to required type [java.lang.Number] for property id

  • #2
    Hmm. Interesting side effect of generics.

    Not sure what the solution is, but have you tried mapping your propertyEditor to the various field names instead of the class? Alternatively what happens if you map the propertyEditor to the Inventory class?

    Comment


    • #3
      Binding by field corrects the symptom:

      Code:
      binder.registerCustomEditor(Number.class, "id", new CustomNumberEditor(Integer.class, false));
      I guess the bigger question is how this should behave. If the implementation's data type should be used, then this seems like a band-aid.

      Comment


      • #4
        Just to be clear, how were you binding before (as in code fragment please)

        Comment


        • #5
          We were not not registering any custom binder before.

          It also seems that bindng by field to fix the problem doesn't really address all the situations where this is occuring. I have collections of objects that need to be bound and it's not possible to register a binder for them in the above fashion.

          for instance, inventory has a collection of details objects:

          details[0].id;
          details[1].id;
          details[2].id;
          ...
          details[150].id

          It doesn't seem that registerCustomEditor will accept regular expressions for the field name.

          Comment


          • #6
            After reading through more of the Spring code, this doesn't really appear to be a Spring problem. It's probably a problem with the java bean framework in 1.5. The following code determines the requiredType:

            Code:
                private Class findPropertyType(Method readMethod, Method writeMethod)
            	throws IntrospectionException {
            	Class propertyType = null;
            	try {
            	    if (readMethod != null) {
            		Class[] params = readMethod.getParameterTypes();
            		if (params.length != 0) {
            		    throw new IntrospectionException("bad read method arg count: " 
            						     + readMethod);
            		}
            		propertyType = readMethod.getReturnType();
            		if (propertyType == Void.TYPE) {
            		    throw new IntrospectionException("read method " +
            					readMethod.getName() + " returns void");
            		}
            	    }
            	    if (writeMethod != null) {
            		Class params[] = writeMethod.getParameterTypes();
            		if (params.length != 1) {
            		    throw new IntrospectionException("bad write method arg count: "
            						     + writeMethod);
            		}
            		if (propertyType != null && propertyType != params[0]) {
            		    throw new IntrospectionException("type mismatch between read and write methods");
            		}
            		propertyType = params[0];
            	    }
            	} catch (IntrospectionException ex) {
            	    throw ex;
            	}
            	return propertyType;
                }
            Here we see the getter method's type as the value used. It seems that is returning the incorrect value. It's difficult to really tell what's going on in memory there because my debugger is flaking out on me in there for some reason.

            I'll probably do some more research and submit a bug to Sun.

            Comment

            Working...
            X