XFire

Binding to parameterized List of Maps throws ClassCastException

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Duplicate
  • Affects Version/s: 1.1
  • Fix Version/s: 1.2.1
  • Component/s: Aegis Module
  • Labels:
    None
  • Number of attachments :
    0

Description

This is similar to bug XFIRE-161 but it's a second level of parameterization.

public List<Map<String, Object>> getAll();

Exception in thread "main" java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
at org.codehaus.xfire.aegis.type.DefaultTypeCreator.createCollectionType(DefaultTypeCreator.java:48)
at org.codehaus.xfire.aegis.type.java5.Java5TypeCreator.createCollectionType(Java5TypeCreator.java:159)
at org.codehaus.xfire.aegis.type.XMLTypeCreator.createCollectionType(XMLTypeCreator.java:181)
at org.codehaus.xfire.aegis.type.AbstractTypeCreator.createTypeForClass(AbstractTypeCreator.java:90)
at org.codehaus.xfire.aegis.type.AbstractTypeCreator.createType(AbstractTypeCreator.java:347)
at org.codehaus.xfire.aegis.AegisBindingProvider.getSuggestedName(AegisBindingProvider.java:122)
at org.codehaus.xfire.service.binding.DefaultServiceConfiguration.getOutParameterName(DefaultServiceConfiguration.java:174)
at org.codehaus.xfire.service.binding.ObjectServiceFactory.getOutParameterName(ObjectServiceFactory.java:958)
at org.codehaus.xfire.service.binding.ObjectServiceFactory.addOperation(ObjectServiceFactory.java:719)
at org.codehaus.xfire.service.binding.ObjectServiceFactory.initializeOperations(ObjectServiceFactory.java:645)

Activity

Hide
Andrzej Doyle added a comment -

It seems the second level of parameterization isn't necessary - I get a similar issue with serialization for

public BlockingQueue<Runnable> getQueue(Connection conn);

  • Context initialization failed
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'handlerMapping' defined in ServletContext resource [/WEB-INF/xfire-servlet.xml]: Initialization of bean failed; nested exception is java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
    java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl
    at org.codehaus.xfire.aegis.type.DefaultTypeCreator.createCollectionType(DefaultTypeCreator.java:48)
    at org.codehaus.xfire.aegis.type.java5.Java5TypeCreator.createCollectionType(Java5TypeCreator.java:159)
    at org.codehaus.xfire.aegis.type.XMLTypeCreator.createCollectionType(XMLTypeCreator.java:181)
    at org.codehaus.xfire.aegis.type.AbstractTypeCreator.createTypeForClass(AbstractTypeCreator.java:90)
    at org.codehaus.xfire.aegis.type.AbstractTypeCreator.createType(AbstractTypeCreator.java:347)
    at org.codehaus.xfire.aegis.AegisBindingProvider.getSuggestedName(AegisBindingProvider.java:125)
    at org.codehaus.xfire.service.binding.DefaultServiceConfiguration.getInParameterName(DefaultServiceConfiguration.java:160)
    at org.codehaus.xfire.annotations.AnnotationServiceConfiguration.getInParameterName(AnnotationServiceConfiguration.java:138)
    at org.codehaus.xfire.service.binding.ObjectServiceFactory.getInParameterName(ObjectServiceFactory.java:943)
    at org.codehaus.xfire.service.binding.ObjectServiceFactory.addOperation(ObjectServiceFactory.java:701)
    at org.codehaus.xfire.service.binding.ObjectServiceFactory.initializeOperations(ObjectServiceFactory.java:645)
Show
Andrzej Doyle added a comment - It seems the second level of parameterization isn't necessary - I get a similar issue with serialization for public BlockingQueue<Runnable> getQueue(Connection conn);
  • Context initialization failed org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'handlerMapping' defined in ServletContext resource [/WEB-INF/xfire-servlet.xml]: Initialization of bean failed; nested exception is java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl at org.codehaus.xfire.aegis.type.DefaultTypeCreator.createCollectionType(DefaultTypeCreator.java:48) at org.codehaus.xfire.aegis.type.java5.Java5TypeCreator.createCollectionType(Java5TypeCreator.java:159) at org.codehaus.xfire.aegis.type.XMLTypeCreator.createCollectionType(XMLTypeCreator.java:181) at org.codehaus.xfire.aegis.type.AbstractTypeCreator.createTypeForClass(AbstractTypeCreator.java:90) at org.codehaus.xfire.aegis.type.AbstractTypeCreator.createType(AbstractTypeCreator.java:347) at org.codehaus.xfire.aegis.AegisBindingProvider.getSuggestedName(AegisBindingProvider.java:125) at org.codehaus.xfire.service.binding.DefaultServiceConfiguration.getInParameterName(DefaultServiceConfiguration.java:160) at org.codehaus.xfire.annotations.AnnotationServiceConfiguration.getInParameterName(AnnotationServiceConfiguration.java:138) at org.codehaus.xfire.service.binding.ObjectServiceFactory.getInParameterName(ObjectServiceFactory.java:943) at org.codehaus.xfire.service.binding.ObjectServiceFactory.addOperation(ObjectServiceFactory.java:701) at org.codehaus.xfire.service.binding.ObjectServiceFactory.initializeOperations(ObjectServiceFactory.java:645)
Hide
Andrzej Doyle added a comment -

Looking through the sources reveals some interesting symptoms...

Clearly, the attempts to find the parameterized type of my collection filter through both XMLTypeCreater and Java5TypeCreator down to DefaultTypeCreator. We can tell from the lack of NPEs that the TypeClassInfo passed around does have a generic type associated with it - and the exception indicates that the concrete class of this generic type is sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl. So far, so good - in this case it reasonable threw a ClassCastException since it isn't an instance of Class.

However, before we fell through to the DefaultTypeCreator, this should ideally have been picked up by Java5TypeCreator's createCollectionType() method. We can see that info.getGenericType() (i.e. out ParameterizedTypeImpl) gets passed into that class' getComponentType() method. We can deduce that getComponentType returned null (since we fell through to DefaultTypeCreator); and due to the instanceof checks, we can say that the 'if' test on line 167 failed.

Essentially, sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl is NOT an instance of java.lang.reflect.ParameterizedType. Which it really really should be.

I'm not sure what the remedy for this is... of course a hack could be constructed that would also explicitly accept ParameterizedTypeImpl classes, but that's horrible. I'm a little puzzled... ParameterizedTypeImpl should clearly be an implementation of ParameterizedType, and I'm beginning to wonder whether I have a suspect JDK and/or libraries. I'll see if Google can turn up any info on this - it doesn't help that the sun.reflect stuff is incredibly sparsely documented...

Show
Andrzej Doyle added a comment - Looking through the sources reveals some interesting symptoms... Clearly, the attempts to find the parameterized type of my collection filter through both XMLTypeCreater and Java5TypeCreator down to DefaultTypeCreator. We can tell from the lack of NPEs that the TypeClassInfo passed around does have a generic type associated with it - and the exception indicates that the concrete class of this generic type is sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl. So far, so good - in this case it reasonable threw a ClassCastException since it isn't an instance of Class. However, before we fell through to the DefaultTypeCreator, this should ideally have been picked up by Java5TypeCreator's createCollectionType() method. We can see that info.getGenericType() (i.e. out ParameterizedTypeImpl) gets passed into that class' getComponentType() method. We can deduce that getComponentType returned null (since we fell through to DefaultTypeCreator); and due to the instanceof checks, we can say that the 'if' test on line 167 failed. Essentially, sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl is NOT an instance of java.lang.reflect.ParameterizedType. Which it really really should be. I'm not sure what the remedy for this is... of course a hack could be constructed that would also explicitly accept ParameterizedTypeImpl classes, but that's horrible. I'm a little puzzled... ParameterizedTypeImpl should clearly be an implementation of ParameterizedType, and I'm beginning to wonder whether I have a suspect JDK and/or libraries. I'll see if Google can turn up any info on this - it doesn't help that the sun.reflect stuff is incredibly sparsely documented...
Hide
Andrzej Doyle added a comment -

Ah - I overlooked something. A quick test reveals that ParameterizedTypeImpl really is an implementation of ParameterizedType, so that's good.

The reason getComponentType is returning null, then, must be because type.getActualTypeArguments()[0] is neither a Class nor a WildcardType.

I can immediately see the cause of the originally reported bug - with nested generic types, this value would be an instance of ParameterizedType (again), and should be handled recursively somehow (perhaps you need only inspect type.getRawType()). Perhaps for some reason this is the same thing that's affecting me - I'll try and debug in Tomcat and see what the actual type argument is.

Show
Andrzej Doyle added a comment - Ah - I overlooked something. A quick test reveals that ParameterizedTypeImpl really is an implementation of ParameterizedType, so that's good. The reason getComponentType is returning null, then, must be because type.getActualTypeArguments()[0] is neither a Class nor a WildcardType. I can immediately see the cause of the originally reported bug - with nested generic types, this value would be an instance of ParameterizedType (again), and should be handled recursively somehow (perhaps you need only inspect type.getRawType()). Perhaps for some reason this is the same thing that's affecting me - I'll try and debug in Tomcat and see what the actual type argument is.
Hide
Andrzej Doyle added a comment -

I retract my first comment - the initialization works fine with one level of parameterization but breaks with two.

It turns out that it was happy with the BlockingQueue<Runnable> - what it takes exception to is a List<FilterResultsColumn<IDataColumn>>, so this is the same bug as originally reported. It's definitely due to Java5TypeCreator.getComponentType not having enough cases - though what those extra cases should be is unclear to me. For 'simple' cases (probably anything that isn't a special case in AbstractTypeCreator.createTypeForClass) I think you'd be OK just taking type.getRawType(). I doubt this would work for a List<List<String>>, though...

Show
Andrzej Doyle added a comment - I retract my first comment - the initialization works fine with one level of parameterization but breaks with two. It turns out that it was happy with the BlockingQueue<Runnable> - what it takes exception to is a List<FilterResultsColumn<IDataColumn>>, so this is the same bug as originally reported. It's definitely due to Java5TypeCreator.getComponentType not having enough cases - though what those extra cases should be is unclear to me. For 'simple' cases (probably anything that isn't a special case in AbstractTypeCreator.createTypeForClass) I think you'd be OK just taking type.getRawType(). I doubt this would work for a List<List<String>>, though...
Hide
Kenny MacLeod added a comment -

This remains a problem with 1.2-RC. Would be great to get it fixed for 1.2 release.

Show
Kenny MacLeod added a comment - This remains a problem with 1.2-RC. Would be great to get it fixed for 1.2 release.
Hide
Andrzej Doyle added a comment -

I've been spending a bit of time getting this to work. I'll say now though, it's more of a hack than a bona fide development of the sources, and I've only been working on the Java 5 side as that's what I'm using. Additionally I'm not convinced that I've understood the philosophy of the class structure, and as I've been moving methods around that could be quite important!

So as such, I'm not really in a position to submit a patch. However, my changes do seem to work to the extent that nested Lists are serialised correctly, and you can view the (correct) WSDL without getting that ClassCastException.

Essentially the direction of my changes was to change CollectionType so that its componentType field held an org.codehaus.xfire.aegis.type.Type rather than a Class. This got passed into the constructor, and made the getComponentType() method much simpler! The other major change was to the getComponentType() method in the Java5TypeCreator. Firstly it now returns a Type rather than a Class, so I added a lookupType() method that converts a Class into its corresponding Type (via the associated TypeMapping, creating and registering a new one if needed) and passed the original cases' Classes through this. Secondly, it now supports the type argument being another ParameterizedType - in this case it creates a TypeClassInfo for this inner type (setting the typeClass, genericType and if it's a map, keyType) then recursively calls createTypeForClass on this new TypeClassInfo. (Additionally, I found that sometimes the type argument would be of instance TypeVariable, so I added a case that fetched the genericDeclaration and passed it through lookupClass()).

These were the meat of my changes - you get serialisation of nested Collections for free since they already call down to their inner type to serialise the actual elements of the Collection. With the changes to the CollectionType constructor, I ended up having to move getComponentType() and thus lookupType() up into the AbstractTypeCreator, so that other concrete TypeCreators could use them as required. There's possibly a cleaner way to do it, but as I say I've merely been focussed on getting this to work as quickly as possible.

I still have outstanding issues with concrete implementations of intervaces vs. using proxies so I can't do a complete end-to-end test yet - still, this seems to be working for me and will hopefully provide a roadmap of sorts for sorting this out properly. If anyone does want me to provide my code as a more concrete guide I'd be happy to do so.

Show
Andrzej Doyle added a comment - I've been spending a bit of time getting this to work. I'll say now though, it's more of a hack than a bona fide development of the sources, and I've only been working on the Java 5 side as that's what I'm using. Additionally I'm not convinced that I've understood the philosophy of the class structure, and as I've been moving methods around that could be quite important! So as such, I'm not really in a position to submit a patch. However, my changes do seem to work to the extent that nested Lists are serialised correctly, and you can view the (correct) WSDL without getting that ClassCastException. Essentially the direction of my changes was to change CollectionType so that its componentType field held an org.codehaus.xfire.aegis.type.Type rather than a Class. This got passed into the constructor, and made the getComponentType() method much simpler! The other major change was to the getComponentType() method in the Java5TypeCreator. Firstly it now returns a Type rather than a Class, so I added a lookupType() method that converts a Class into its corresponding Type (via the associated TypeMapping, creating and registering a new one if needed) and passed the original cases' Classes through this. Secondly, it now supports the type argument being another ParameterizedType - in this case it creates a TypeClassInfo for this inner type (setting the typeClass, genericType and if it's a map, keyType) then recursively calls createTypeForClass on this new TypeClassInfo. (Additionally, I found that sometimes the type argument would be of instance TypeVariable, so I added a case that fetched the genericDeclaration and passed it through lookupClass()). These were the meat of my changes - you get serialisation of nested Collections for free since they already call down to their inner type to serialise the actual elements of the Collection. With the changes to the CollectionType constructor, I ended up having to move getComponentType() and thus lookupType() up into the AbstractTypeCreator, so that other concrete TypeCreators could use them as required. There's possibly a cleaner way to do it, but as I say I've merely been focussed on getting this to work as quickly as possible. I still have outstanding issues with concrete implementations of intervaces vs. using proxies so I can't do a complete end-to-end test yet - still, this seems to be working for me and will hopefully provide a roadmap of sorts for sorting this out properly. If anyone does want me to provide my code as a more concrete guide I'd be happy to do so.
Hide
Dan Diephouse added a comment -

Hiya - First, I'm really sorry I haven't had time to dig into this yet. It is definitely a priority. I'm scheduling it for 1.2.1 though as I absolutely need to get 1.2 out ASAP.

Show
Dan Diephouse added a comment - Hiya - First, I'm really sorry I haven't had time to dig into this yet. It is definitely a priority. I'm scheduling it for 1.2.1 though as I absolutely need to get 1.2 out ASAP.
Hide
Dan Diephouse added a comment -

Also, a patch would really help expedite things.

Show
Dan Diephouse added a comment - Also, a patch would really help expedite things.
Hide
fabrizio giustina added a comment -

the problem is in protected Class getComponentType(Object genericType) which doesn't handle more than one level for generic types:

a quick fix (don't crash but handle raw types on deeper levels) could be checking if type.getActualTypeArguments()[0] is again an instance of ParameterizedType, and use the class returned by getRawType().

protected Class getComponentType(Object genericType)
{
Class paramClass = null;

if (genericType instanceof ParameterizedType)
{
ParameterizedType type = (ParameterizedType) genericType;

if (type.getActualTypeArguments()[0] instanceof Class)

{ paramClass = (Class) type.getActualTypeArguments()[0]; }

else if (type.getActualTypeArguments()[0] instanceof WildcardType)
{
WildcardType wildcardType = (WildcardType) type.getActualTypeArguments()[0];

if (wildcardType.getUpperBounds()[0] instanceof Class)

{ paramClass = (Class) wildcardType.getUpperBounds()[0]; }

}
else if (type.getActualTypeArguments()[0] instanceof ParameterizedType) // FIXME, this is only a quick fix for XFIRE-424

{ ParameterizedType ptype = (ParameterizedType) type.getActualTypeArguments()[0]; paramClass = (Class) ptype.getRawType(); }

}
return paramClass;
}

With this fix, I am able to obtain a WSDL with parameterized collections, but the 2nd level of parameterized types are handled just as:
<xsd:element minOccurs="0" name="value" nillable="true" type="xsd:anyType"/>
...
(so List<List<Double>>() will be mapped simply to List<List>(); )

Probably a real fix (handle multiple levels of parametrized types) could be harder, it probably will require a reworking of Java5TypeCreator, which can't simply call the DefaultTypeCreator in this situation (passing a class back will mean forgetting about deeper levels of parameterization).

I hope that at least this quick fix could be released with xfire 1.2 (better than a ClassCast, isn't it?)

Show
fabrizio giustina added a comment - the problem is in protected Class getComponentType(Object genericType) which doesn't handle more than one level for generic types: a quick fix (don't crash but handle raw types on deeper levels) could be checking if type.getActualTypeArguments()[0] is again an instance of ParameterizedType, and use the class returned by getRawType(). protected Class getComponentType(Object genericType) { Class paramClass = null; if (genericType instanceof ParameterizedType) { ParameterizedType type = (ParameterizedType) genericType; if (type.getActualTypeArguments()[0] instanceof Class) { paramClass = (Class) type.getActualTypeArguments()[0]; } else if (type.getActualTypeArguments()[0] instanceof WildcardType) { WildcardType wildcardType = (WildcardType) type.getActualTypeArguments()[0]; if (wildcardType.getUpperBounds()[0] instanceof Class) { paramClass = (Class) wildcardType.getUpperBounds()[0]; } } else if (type.getActualTypeArguments()[0] instanceof ParameterizedType) // FIXME, this is only a quick fix for XFIRE-424 { ParameterizedType ptype = (ParameterizedType) type.getActualTypeArguments()[0]; paramClass = (Class) ptype.getRawType(); } } return paramClass; } With this fix, I am able to obtain a WSDL with parameterized collections, but the 2nd level of parameterized types are handled just as: <xsd:element minOccurs="0" name="value" nillable="true" type="xsd:anyType"/> ... (so List<List<Double>>() will be mapped simply to List<List>(); ) Probably a real fix (handle multiple levels of parametrized types) could be harder, it probably will require a reworking of Java5TypeCreator, which can't simply call the DefaultTypeCreator in this situation (passing a class back will mean forgetting about deeper levels of parameterization). I hope that at least this quick fix could be released with xfire 1.2 (better than a ClassCast, isn't it?)
Hide
Dan Diephouse added a comment -

I don't think it can be included at 1.2 at this point. HOWEVER, I am up for doing a 1.2.1 release in 1-2 weeks with this being the primary thing scheduled for it.

Show
Dan Diephouse added a comment - I don't think it can be included at 1.2 at this point. HOWEVER, I am up for doing a 1.2.1 release in 1-2 weeks with this being the primary thing scheduled for it.
Hide
fabrizio giustina added a comment -

> I don't think it can be included at 1.2 at this point

sorry for bothering, but why do you want to postpone this for 1.2.1 if 1.2 is not released yet? Looks like a critical bug for java 1.5 users and you have a fix available.

if you look at the getComponentType method, the addition of a last "else if" looks safe: at the moment if type.getActualTypeArguments()[0] is a ParameterizedType instance it will return null and the object will be casted to a Class, causing always a ClassCastException... it's just a matter of handling something that it's not handled at all at the moment:

}
// without this the method will return null and ParameterizedType will be casted to class
// FIXME, this is only a quick fix for XFIRE-424
else if (type.getActualTypeArguments()[0] instanceof ParameterizedType)

{ ParameterizedType ptype = (ParameterizedType) type.getActualTypeArguments()[0]; paramClass = (Class) ptype.getRawType(); }

Please still consider including this in 1.2, unless there are other reasons I am not aware of against this patch....

Show
fabrizio giustina added a comment - > I don't think it can be included at 1.2 at this point sorry for bothering, but why do you want to postpone this for 1.2.1 if 1.2 is not released yet? Looks like a critical bug for java 1.5 users and you have a fix available. if you look at the getComponentType method, the addition of a last "else if" looks safe: at the moment if type.getActualTypeArguments()[0] is a ParameterizedType instance it will return null and the object will be casted to a Class, causing always a ClassCastException... it's just a matter of handling something that it's not handled at all at the moment: } // without this the method will return null and ParameterizedType will be casted to class // FIXME, this is only a quick fix for XFIRE-424 else if (type.getActualTypeArguments()[0] instanceof ParameterizedType) { ParameterizedType ptype = (ParameterizedType) type.getActualTypeArguments()[0]; paramClass = (Class) ptype.getRawType(); } Please still consider including this in 1.2, unless there are other reasons I am not aware of against this patch....
Hide
Dan Diephouse added a comment -

I've now implemented this for Java 5 in SVN, and it works quite well! It'll be in the 1.2.1 release.

Also, I'm marking this as a duplicate of XFIRE-89, because that issue was here first, so don't freak out when it doesn't say closed

Show
Dan Diephouse added a comment - I've now implemented this for Java 5 in SVN, and it works quite well! It'll be in the 1.2.1 release. Also, I'm marking this as a duplicate of XFIRE-89, because that issue was here first, so don't freak out when it doesn't say closed

People

Vote (4)
Watch (4)

Dates

  • Created:
    Updated:
    Resolved: