|
|
|
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... 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. 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... This remains a problem with 1.2-RC. Would be great to get it fixed for 1.2 release.
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. 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.
Also, a patch would really help expedite things.
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) if (genericType instanceof ParameterizedType) if (type.getActualTypeArguments()[0] instanceof Class) { paramClass = (Class) type.getActualTypeArguments()[0]; } else if (type.getActualTypeArguments()[0] instanceof WildcardType) if (wildcardType.getUpperBounds()[0] instanceof Class) { paramClass = (Class) wildcardType.getUpperBounds()[0]; } } } With this fix, I am able to obtain a WSDL with parameterized collections, but the 2nd level of parameterized types are handled just as: 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?) 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.
> 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: } Please still consider including this in 1.2, unless there are other reasons I am not aware of against this patch.... 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 |
||||||||||||||||||||||||||||||||||||||||||||
public BlockingQueue<Runnable> getQueue(Connection conn);
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)