XFire

Arrays of Collections do not map correctly [Patch Included]

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: 1.1.2
  • Fix Version/s: 1.2.1
  • Component/s: Aegis Module
  • Labels:
    None
  • Environment:
    Xfire 1.1.2
  • Number of attachments :
    1

Description

If I have a Service that returns an array of Collections / Map XFire fails saying that the keytype and componentType are not defined.

The problem seems to be that when an array is returned, XFire creates a new TypeInfo for the nested List / Map which does not inherit the keyType and componentType defined in the aegis mapping.

Here is an example

interface ArrayService{
    	public Map[] getMapArray();

	public List[] getArrayOfListsOfDoubles();
}
<mappings>
    <mapping>
                 <method name="getMapArray">
			<return-type keyType="java.lang.Double" componentType="java.lang.Double" />
		</method>

		<method name="getArrayOfListsOfDoubles">
			<return-type componentType="java.lang.Double" />
		</method>
    </mapping>
</mappings>

The problem was found at AbstractTypeCreator line 310. This was modified so now it looks like the following:

TypeClassInfo nestedInfo = createBasicClassInfo(componentType);
            nestedInfo.setKeyType(info.getKeyType());
            nestedInfo.setGenericType(info.getGenericType());

        	type = createTypeForClass(nestedInfo);
            getTypeMapping().register(type);

We will try to add the testcase for these scenarios.

Activity

Hide
Andres Bernasconi added a comment -

Patch to fix the current Issue.

Show
Andres Bernasconi added a comment - Patch to fix the current Issue.
Hide
Andres Bernasconi added a comment -

Notice that this fix does not allow for nesting collections (i.e. a List of Lists of Doubles), because we would need more information from the mapping (aegis.xml file).

We are suggesting allowing "property" references (a property-ref attribute of the property tag) that links properties together. We would also need to specify the type of "collection" we are nesting (i.e. an Array, a List, a Map...).

An example would be something like:

...
    <property name="listOfListsOfDoubles" componentType="java.util.List" property-ref="listOfDoubles" />
    <property name="listOfDoubles" componentType="java.lang.Double" />
...

Now in the AbstractTypeCreator we would see if we have a property-ref, if not we are in the final object. If we are in a list then we would need to recurse setting the property-ref somewhere so it knows the mapping to use next.

Show
Andres Bernasconi added a comment - Notice that this fix does not allow for nesting collections (i.e. a List of Lists of Doubles), because we would need more information from the mapping (aegis.xml file). We are suggesting allowing "property" references (a property-ref attribute of the property tag) that links properties together. We would also need to specify the type of "collection" we are nesting (i.e. an Array, a List, a Map...). An example would be something like:
...
    <property name="listOfListsOfDoubles" componentType="java.util.List" property-ref="listOfDoubles" />
    <property name="listOfDoubles" componentType="java.lang.Double" />
...
Now in the AbstractTypeCreator we would see if we have a property-ref, if not we are in the final object. If we are in a list then we would need to recurse setting the property-ref somewhere so it knows the mapping to use next.
Hide
Andres Bernasconi added a comment -

I'm having the same componentType and keyType when I define two services (using two different XFireExporters). One of my service uses an array of Maps ("map array service"), and the other one ("simple service") just returns a very simple POJO (even no mapping required there).

Both services work fine alone (when the other service is commented from the configuration), but when put together the application behaves strangely.

When the Simple Service is configure before the Map Array Service in the SimpleUrlHandlerMapping config bean, the stack trace for creating the map type is the following:

XMLTypeCreator.createMapType(AbstractTypeCreator$TypeClassInfo) line: 136
XMLTypeCreator(AbstractTypeCreator).createTypeForClass(AbstractTypeCreator$TypeClassInfo) line: 86
XMLTypeCreator(AbstractTypeCreator).createType(Method, int) line: 358
AegisBindingProvider.getSuggestedName(Service, OperationInfo, int) line: 125
DefaultServiceConfiguration.getInParameterName(Service, OperationInfo, Method, int, boolean) line: 160
ObjectServiceFactory.getInParameterName(Service, OperationInfo, Method, int, boolean) line: 943
ObjectServiceFactory.addOperation(Service, Method, String) line: 701
ObjectServiceFactory.initializeOperations(Service, String) line: 645
ObjectServiceFactory.create(Class, String, String, Map) line: 350
XFireExporter(ServiceBean).afterPropertiesSet() line: 158
XFireExporter.afterPropertiesSet() line: 27
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).invokeInitMethods(String, Object, RootBeanDefinition) line: 1059
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 363
DefaultListableBeanFactory(AbstractBeanFactory).getBean(String, Class, Object[]) line: 226
DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 147
BeanDefinitionValueResolver.resolveReference(String, RuntimeBeanReference) line: 176
BeanDefinitionValueResolver.resolveValueIfNecessary(String, Object) line: 105
BeanDefinitionValueResolver.resolveManagedMap(String, Map) line: 225
BeanDefinitionValueResolver.resolveValueIfNecessary(String, Object) line: 117
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).applyPropertyValues(String, RootBeanDefinition, BeanWrapper, PropertyValues) line: 1013
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).populateBean(String, RootBeanDefinition, BeanWrapper) line: 824
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 345
DefaultListableBeanFactory(AbstractBeanFactory).getBean(String, Class, Object[]) line: 226
DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 147
DefaultListableBeanFactory.preInstantiateSingletons() line: 275

If I change the SimpleUrlHandlerMapping and put the Map Array Service first, the stack trace for creating the map is the following:

XMLTypeCreator.createMapType(AbstractTypeCreator$TypeClassInfo) line: 136
XMLTypeCreator(AbstractTypeCreator).createTypeForClass(AbstractTypeCreator$TypeClassInfo) line: 86
XMLTypeCreator(AbstractTypeCreator).createType(Class) line: 398
ArrayType.getComponentType() line: 406
ArrayType.writeSchema(Element) line: 316
WSDLBuilder(AbstractWSDL).addDependency(SchemaType) line: 248
WSDLBuilder(AbstractWSDL).addDependency(SchemaType) line: 257
WSDLBuilder.writeParametersSchema(Collection, Element) line: 473
WSDLBuilder.createDocumentType(MessageInfo, Part, String) line: 446
WSDLBuilder.createWrappedOutputParts(Message, OperationInfo) line: 423
WSDLBuilder.createOutputMessage(OperationInfo) line: 192
WSDLBuilder.createAbstractInterface() line: 117
WSDLBuilder.write(OutputStream) line: 76
WSDLBuilderAdapter.write(OutputStream) line: 40
DefaultXFire.generateWSDL(String, OutputStream) line: 104
XFireServletControllerAdapter(XFireServletController).generateWSDL(HttpServletResponse, String) line: 317
XFireServletControllerAdapter(XFireServletController).doService(HttpServletRequest, HttpServletResponse) line: 116
XFireServletControllerAdapter.handleRequest(HttpServletRequest, HttpServletResponse) line: 63
XFireExporter.handleRequest(HttpServletRequest, HttpServletResponse) line: 44
SimpleControllerHandlerAdapter.handle(HttpServletRequest, HttpServletResponse, Object) line: 44

In this second case, I get the "genericType" / "keyType" not defined error. This is puzzling, and since I don't really understand the XFire architecture I don't know what is going on (it may even have something to do with Spring?? ). I guess the problem could be in ArrayType (look at the stack trace) since it is not giving it's InfoType to the defined Collection, but I dont have such object already available by then (ArrayType.getComponentType).

Dan, do you have any idea why is this happening?

Cheers
Andres Bernasconi.

Show
Andres Bernasconi added a comment - I'm having the same componentType and keyType when I define two services (using two different XFireExporters). One of my service uses an array of Maps ("map array service"), and the other one ("simple service") just returns a very simple POJO (even no mapping required there). Both services work fine alone (when the other service is commented from the configuration), but when put together the application behaves strangely. When the Simple Service is configure before the Map Array Service in the SimpleUrlHandlerMapping config bean, the stack trace for creating the map type is the following:
XMLTypeCreator.createMapType(AbstractTypeCreator$TypeClassInfo) line: 136
XMLTypeCreator(AbstractTypeCreator).createTypeForClass(AbstractTypeCreator$TypeClassInfo) line: 86
XMLTypeCreator(AbstractTypeCreator).createType(Method, int) line: 358
AegisBindingProvider.getSuggestedName(Service, OperationInfo, int) line: 125
DefaultServiceConfiguration.getInParameterName(Service, OperationInfo, Method, int, boolean) line: 160
ObjectServiceFactory.getInParameterName(Service, OperationInfo, Method, int, boolean) line: 943
ObjectServiceFactory.addOperation(Service, Method, String) line: 701
ObjectServiceFactory.initializeOperations(Service, String) line: 645
ObjectServiceFactory.create(Class, String, String, Map) line: 350
XFireExporter(ServiceBean).afterPropertiesSet() line: 158
XFireExporter.afterPropertiesSet() line: 27
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).invokeInitMethods(String, Object, RootBeanDefinition) line: 1059
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 363
DefaultListableBeanFactory(AbstractBeanFactory).getBean(String, Class, Object[]) line: 226
DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 147
BeanDefinitionValueResolver.resolveReference(String, RuntimeBeanReference) line: 176
BeanDefinitionValueResolver.resolveValueIfNecessary(String, Object) line: 105
BeanDefinitionValueResolver.resolveManagedMap(String, Map) line: 225
BeanDefinitionValueResolver.resolveValueIfNecessary(String, Object) line: 117
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).applyPropertyValues(String, RootBeanDefinition, BeanWrapper, PropertyValues) line: 1013
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).populateBean(String, RootBeanDefinition, BeanWrapper) line: 824
DefaultListableBeanFactory(AbstractAutowireCapableBeanFactory).createBean(String, RootBeanDefinition, Object[]) line: 345
DefaultListableBeanFactory(AbstractBeanFactory).getBean(String, Class, Object[]) line: 226
DefaultListableBeanFactory(AbstractBeanFactory).getBean(String) line: 147
DefaultListableBeanFactory.preInstantiateSingletons() line: 275
If I change the SimpleUrlHandlerMapping and put the Map Array Service first, the stack trace for creating the map is the following:
XMLTypeCreator.createMapType(AbstractTypeCreator$TypeClassInfo) line: 136
XMLTypeCreator(AbstractTypeCreator).createTypeForClass(AbstractTypeCreator$TypeClassInfo) line: 86
XMLTypeCreator(AbstractTypeCreator).createType(Class) line: 398
ArrayType.getComponentType() line: 406
ArrayType.writeSchema(Element) line: 316
WSDLBuilder(AbstractWSDL).addDependency(SchemaType) line: 248
WSDLBuilder(AbstractWSDL).addDependency(SchemaType) line: 257
WSDLBuilder.writeParametersSchema(Collection, Element) line: 473
WSDLBuilder.createDocumentType(MessageInfo, Part, String) line: 446
WSDLBuilder.createWrappedOutputParts(Message, OperationInfo) line: 423
WSDLBuilder.createOutputMessage(OperationInfo) line: 192
WSDLBuilder.createAbstractInterface() line: 117
WSDLBuilder.write(OutputStream) line: 76
WSDLBuilderAdapter.write(OutputStream) line: 40
DefaultXFire.generateWSDL(String, OutputStream) line: 104
XFireServletControllerAdapter(XFireServletController).generateWSDL(HttpServletResponse, String) line: 317
XFireServletControllerAdapter(XFireServletController).doService(HttpServletRequest, HttpServletResponse) line: 116
XFireServletControllerAdapter.handleRequest(HttpServletRequest, HttpServletResponse) line: 63
XFireExporter.handleRequest(HttpServletRequest, HttpServletResponse) line: 44
SimpleControllerHandlerAdapter.handle(HttpServletRequest, HttpServletResponse, Object) line: 44
In this second case, I get the "genericType" / "keyType" not defined error. This is puzzling, and since I don't really understand the XFire architecture I don't know what is going on (it may even have something to do with Spring?? ). I guess the problem could be in ArrayType (look at the stack trace) since it is not giving it's InfoType to the defined Collection, but I dont have such object already available by then (ArrayType.getComponentType). Dan, do you have any idea why is this happening? Cheers Andres Bernasconi.
Hide
Dan Diephouse added a comment -

Looking into this for 1.2... Need to see if this applies to XFIRE-424 as well.

Show
Dan Diephouse added a comment - Looking into this for 1.2... Need to see if this applies to XFIRE-424 as well.
Hide
Dan Diephouse added a comment -

Hi Andres,
sorry this has taken a while, but you had a really good idea about how to do this and I finally got around to finishing it today. Now with today's SVN you can do this:

<mappings>
<method name="echoMapOfCollections">
<parameter index="0" keyType="#collection1" componentType="#collection1"/>
<return-type keyType="#collection1" componentType="#collection1"/>
</method>
<component name="collection1" class="java.util.List" componentType="java.lang.String"/>
</mapping>
</mappings>

Instead of a ref attribute, "#" signals that we're referencing a component type. This also opens up a way to control the naming of arrays/collections/etc! Just need to do:

<method name="getListofListofDoubles">
<return-type componentType="#someDoubles" typeName="LotsOfDoubles"/>
</method>
<component name="someDoubles" class="java.util.List" typeName="SomeDoubles" componentType="java.lang.Double" />

Show
Dan Diephouse added a comment - Hi Andres, sorry this has taken a while, but you had a really good idea about how to do this and I finally got around to finishing it today. Now with today's SVN you can do this: <mappings> <method name="echoMapOfCollections"> <parameter index="0" keyType="#collection1" componentType="#collection1"/> <return-type keyType="#collection1" componentType="#collection1"/> </method> <component name="collection1" class="java.util.List" componentType="java.lang.String"/> </mapping> </mappings> Instead of a ref attribute, "#" signals that we're referencing a component type. This also opens up a way to control the naming of arrays/collections/etc! Just need to do: <method name="getListofListofDoubles"> <return-type componentType="#someDoubles" typeName="LotsOfDoubles"/> </method> <component name="someDoubles" class="java.util.List" typeName="SomeDoubles" componentType="java.lang.Double" />
Hide
Andres Bernasconi added a comment -

Dan,

sorry for this lack of knowledge, but what is typeName and what does it do? (is it the "WSDL" typeName?)

BTW: Glad to see this implemented. Sorry we didn't provide much more code at the time.

Show
Andres Bernasconi added a comment - Dan, sorry for this lack of knowledge, but what is typeName and what does it do? (is it the "WSDL" typeName?) BTW: Glad to see this implemented. Sorry we didn't provide much more code at the time.
Hide
Dan Diephouse added a comment -

Yes, typeName controls the name of the type in the wsdl.

Show
Dan Diephouse added a comment - Yes, typeName controls the name of the type in the wsdl.

People

Vote (0)
Watch (1)

Dates

  • Created:
    Updated:
    Resolved: