Results 1 to 5 of 5

Thread: BeanWrapper trampling generics?

  1. #1
    Join Date
    Jun 2007
    Posts
    3

    Default BeanWrapper trampling generics?

    I'm getting this problem in the web layer, but I think it's related to the container itself, which is why I'm posting here.

    All this is using Spring Framework 2.0.6.

    I'm writing a couple of SimpleFormControllers who need very similar command objects. Being a lazy man I figured I'd write a base class for all these classes using the spiffy Java5 generics.

    The generic superclass looks like this:

    Code:
    public class GenericCommand<T> {
        private T add;
        private boolean change;
    
        GenericCommand() {}
    
        public void setAdd(T a) { add = a; }
        public T    getAdd()    { return add; }
    
        public void    setChange(boolean c) { change = c; }
        public boolean  isChange()          { return change; }
    }
    One of the subclasses of GenericCommand is CourseCommand:

    Code:
    public class CourseCommand extends GenericCommand<Course> {
        private String changeSemester;
    
        CourseCommand() {
            super();
            setAdd(new Course());
        }
    
        public void   setChangeSemester(String s) { changeSemester = s; }
        public String getChangeSemester()         { return changeSemester; }
    }
    Now, this seems to be all nice and spiffy, until I try submitting the form. That gets me this error when the validator tries to get the attribute add from CourseCommand: java.lang.NoSuchMethodError: uio.ifi.joly.web.command.CourseCommand.getAdd()Lui o/ifi/joly/domain/Course

    From what I can tell it looks like Spring has reflected a new getAdd() method on top of my old one, with the return type Object, clobbering the type erasure generated by the compiler, and making the generics go up in smoke. Is this a known issue, and if so is there a way to stop Spring from breaking the generic functionality?

  2. #2
    Join Date
    Feb 2005
    Posts
    217

    Default

    That gets me this error when the validator tries to get the attribute add from CourseCommand: java.lang.NoSuchMethodError: uio.ifi.joly.web.command.CourseCommand.getAdd()Lui o/ifi/joly/domain/Course
    Can you post your validator code?

    clobbering the type erasure generated by the compiler, and making the generics go up in smoke
    I'm not sure how one clobbers type erasure

  3. #3
    Join Date
    Aug 2005
    Location
    London (the English one!)
    Posts
    378

    Default Similar issue

    Hi

    I believe that I has similar issue where the BeanWrapper.getPropertyType() always returns Object...

    Code:
    public class Super1<I> {
        I id;
    
        public Super1(I id) {
            this.id = id;
        }
    
        public I getId() {
            return id;
        }
    }
    and my test:
    Code:
    Super1<Integer> super1 = new Super1<Integer>(123);
    BeanWrapper bw2 = new BeanWrapperImpl(super1);
    System.out.println("id is of type:" + bw2.getPropertyType("id"));
    System.out.println("bw access to id is of type:" + bw2.getPropertyValue("id").getClass());
    System.out.println("direct access to id is of type:" + super1.getId().getClass());
    The first call returns java.lang.OBJECT, which is incorrect I'd say. the other 2 calls return java.lang.Integer.

    The issue is that the use of BeanWrapper.getPropertyType() is deeply embedded in many libraries, including the Spring Rich Client one that I am using and this causes havoc!

    Any suggestion / patch?

    many thanks

    Benoit

  4. #4
    Join Date
    Aug 2006
    Location
    Now Germany, previously Ukraine
    Posts
    1,546

    Default

    It is direct consequence of type erasure (see 4.6 in the Java langauge specification, "The erasure of a parameterized type (§4.5) G<T1, ... ,Tn> is |G|."). So, at runtime class Super1<Integer> is represented just as Super1 and any reflection calls can not see that it was parametrized by Integer,

    You have to blame Java language itself, not Spring in this case.

    Regards,
    Oleksandr

    Quote Originally Posted by benoitx View Post
    Hi

    I believe that I has similar issue where the BeanWrapper.getPropertyType() always returns Object...

    Code:
    public class Super1<I> {
        I id;
    
        public Super1(I id) {
            this.id = id;
        }
    
        public I getId() {
            return id;
        }
    }
    and my test:
    Code:
    Super1<Integer> super1 = new Super1<Integer>(123);
    BeanWrapper bw2 = new BeanWrapperImpl(super1);
    System.out.println("id is of type:" + bw2.getPropertyType("id"));
    System.out.println("bw access to id is of type:" + bw2.getPropertyValue("id").getClass());
    System.out.println("direct access to id is of type:" + super1.getId().getClass());
    The first call returns java.lang.OBJECT, which is incorrect I'd say. the other 2 calls return java.lang.Integer.

    The issue is that the use of BeanWrapper.getPropertyType() is deeply embedded in many libraries, including the Spring Rich Client one that I am using and this causes havoc!

    Any suggestion / patch?

    many thanks

    Benoit

  5. #5
    Join Date
    Aug 2005
    Location
    London (the English one!)
    Posts
    378

    Default Not entirely true.

    Hi Oleksandr

    Thanks for your reply. It is not entirely impossible as highlighted in 2 articles:
    http://www.ibm.com/developerworks/ja...-cwt11085.html
    http://www.artima.com/weblogs/viewpo...?thread=208860

    This require some Java 1.5 code (but it could be made backward compatible by some tools). In any case, I'd think that Spring could either come up with a spring-core-jdk15 using these technologies or define some kind of Annotation that would make the BeanWrapper able to find out more about the PropertyType (why not using getPropertyValue().getClass() as the first attempt).

    For instance in the enclosed code that shows it is possible to find out the generic type of a property.

    Class net.objectlab.springtest.GenericTest details:
    intSuper2 is of parameterized type
    net.objectlab.springtest.GenericTest$Super1
    using types (java.lang.Integer)
    Class net.objectlab.springtest.GenericTest$Super1 details:
    id is of type java.lang.Object

    Not a 'clean' solution of course, but it shows that it may not be impossible I'd think. Especially if the developers were to adhere to a convention of some sort...

    My £0.02 suggestion,

    Benoit

    Code:
    import java.lang.reflect.Field;
    import java.lang.reflect.GenericArrayType;
    import java.lang.reflect.Modifier;
    import java.lang.reflect.ParameterizedType;
    import java.lang.reflect.Type;
    import java.util.HashSet;
    
    import org.springframework.beans.BeanWrapper;
    import org.springframework.beans.BeanWrapperImpl;
    import org.springframework.binding.form.FormModel;
    import org.springframework.richclient.form.FormModelHelper;
    
    public class GenericTest {
    private Super1<Integer> intSuper2;
    
    public Super1<Integer> getIntSuper2() {
    return intSuper2;
    }
    
    public void setIntSuper2(Super1<Integer> intSuper2) {
    this.intSuper2 = intSuper2;
    }
    
    public static void main(String[] a) {
    try {
        analyze("", GenericTest.class);
    } catch (SecurityException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    }
    
    private static HashSet<String> s_processed = new HashSet<String>();
    
    private static void describe(String lead, Field field) {
    
    // get base and generic types, check kind
    Class<?> btype = field.getType();
    Type gtype = field.getGenericType();
    if (gtype instanceof ParameterizedType) {
    
        // list basic parameterized type information
        ParameterizedType ptype = (ParameterizedType) gtype;
        System.out.println(lead + field.getName() + " is of parameterized type");
        System.out.println(lead + ' ' + btype.getName());
    
        // print list of actual types for parameters
        System.out.print(lead + " using types (");
        Type[] actuals = ptype.getActualTypeArguments();
        for (int i = 0; i < actuals.length; i++) {
    	if (i > 0) {
    	    System.out.print(" ");
    	}
    	Type actual = actuals[i];
    	if (actual instanceof Class) {
    	    System.out.print(((Class) actual).getName());
    	} else {
    	    System.out.print(actuals[i]);
    	}
        }
        System.out.println(")");
    
        // analyze all parameter type classes
        for (int i = 0; i < actuals.length; i++) {
    	Type actual = actuals[i];
    	if (actual instanceof Class) {
    	    analyze(lead, (Class) actual);
    	}
        }
    
    } else if (gtype instanceof GenericArrayType) {
    
        // list array type and use component type
        System.out.println(lead + field.getName() + " is array type " + gtype);
        gtype = ((GenericArrayType) gtype).getGenericComponentType();
    
    } else {
    
        // just list basic information
        System.out.println(lead + field.getName() + " is of type " + btype.getName());
    }
    
    // analyze the base type of this field
    analyze(lead, btype);
    }
    
    private static void analyze(String lead, Class<?> clas) {
    
    // substitute component type in case of an array
    if (clas.isArray()) {
        clas = clas.getComponentType();
    }
    
    // make sure class should be expanded
    String name = clas.getName();
    if (!clas.isPrimitive() && !clas.isInterface() && !name.startsWith("java.lang.") && !s_processed.contains(name)) {
    
        // print introduction for class
        s_processed.add(name);
        System.out.println(lead + "Class " + clas.getName() + " details:");
    
        // process each field of class
        String indent = lead + ' ';
        Field[] fields = clas.getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
    	Field field = fields[i];
    	if (!Modifier.isStatic(field.getModifiers())) {
    	    describe(indent, field);
    	}
        }
    }
    }

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •