XFire

Generated collection structures don't appear on WSDL

Details

  • Type: Bug Bug
  • Status: Closed Closed
  • Priority: Major Major
  • Resolution: Fixed
  • Affects Version/s: 1.1, 1.1.1
  • Fix Version/s: 1.2.5
  • Component/s: JAX-WS, JAXB 2.0
  • Labels:
    None
  • Environment:
    Java 5
  • Number of attachments :
    4

Description

When trying to expose a service that returns a collection of type T : Collection<T> doStuff()
The generated WSDL makes a reference to ArrayOfT but has no corresponding schema.

This occurs when using the org.codehaus.xfire.jaxws.JAXWSServiceFactory and the org.codehaus.xfire.jaxb2.JaxbServiceFactory.

  1. FakeService.java
    16/Feb/07 1:04 PM
    0.4 kB
    Frédéric Desmoulins
  2. FakeService.wsdl
    16/Feb/07 1:04 PM
    2 kB
    Frédéric Desmoulins
  3. spring-xfire.xml
    16/Feb/07 1:05 PM
    2 kB
    Frédéric Desmoulins
  4. web.xml
    16/Feb/07 1:05 PM
    2 kB
    Frédéric Desmoulins

Activity

Hide
Rick Sears added a comment -

I'm trying to become more familiar with the code, but I think the issue may stem from this area of the JaxbTypeCreator class:

public Type createType(Method m, int index)
{
Class clazz = null;
if (index > -1)

{ clazz = m.getParameterTypes()[index]; }

else

{ clazz = m.getReturnType(); }

if (isJaxbType(clazz))

{ return createJaxbType(clazz); }

else

{ return nextCreator.createType(m, index); }

}

Calling m.getReturnType on a method that returns a List<Bar> would only return List as the class, and then the subsequent call to isJaxbType would return false for the java.util.List class. I've seen this mentioned before in JIRA issue XFIRE-85, but that did not seem to deal with the JAXB version of the code.

Hope this helps.

Show
Rick Sears added a comment - I'm trying to become more familiar with the code, but I think the issue may stem from this area of the JaxbTypeCreator class: public Type createType(Method m, int index) { Class clazz = null; if (index > -1) { clazz = m.getParameterTypes()[index]; } else { clazz = m.getReturnType(); } if (isJaxbType(clazz)) { return createJaxbType(clazz); } else { return nextCreator.createType(m, index); } } Calling m.getReturnType on a method that returns a List<Bar> would only return List as the class, and then the subsequent call to isJaxbType would return false for the java.util.List class. I've seen this mentioned before in JIRA issue XFIRE-85, but that did not seem to deal with the JAXB version of the code. Hope this helps.
Hide
Rick Sears added a comment -

Another note. It seems that the Java5TypeCreator handles all the generic support for the Aegis binding, but the JaxbTypeCreator does not extend from this class or have a reference to this creator to help it resolve return types that use generics. Don't know if there is a way to configure the JaxbTypeCreator externally to utilize some of the functionality of the Java5TypeCreator, but if there is i'd love to try it out.

Show
Rick Sears added a comment - Another note. It seems that the Java5TypeCreator handles all the generic support for the Aegis binding, but the JaxbTypeCreator does not extend from this class or have a reference to this creator to help it resolve return types that use generics. Don't know if there is a way to configure the JaxbTypeCreator externally to utilize some of the functionality of the Java5TypeCreator, but if there is i'd love to try it out.
Hide
Mark Larman added a comment -

This could be a different manifestation of the JIRA issue XFIRE-526 where an ArrayOfString is referenced but not declared.

Show
Mark Larman added a comment - This could be a different manifestation of the JIRA issue XFIRE-526 where an ArrayOfString is referenced but not declared.
Hide
Dan Diephouse added a comment -

OK, looking into this.

Show
Dan Diephouse added a comment - OK, looking into this.
Hide
Dan Diephouse added a comment -

I can't reproduce this in the latest release. Have you tried using a 1.2.x build?

Here is my test service:

@WebService
public class CollectionService {
public Collection<Foo> getFoos() { return null; }
}

Show
Dan Diephouse added a comment - I can't reproduce this in the latest release. Have you tried using a 1.2.x build? Here is my test service: @WebService public class CollectionService { public Collection<Foo> getFoos() { return null; } }
Hide
Frédéric Desmoulins added a comment -

Same problem for me on:

Java 6 (build 1.6.0-b105)
jaxws-2.1fcs
XFire 1.2.4

With XFire Settings:
JaxbServiceFactory
JaxbTypeRegistry
Jsr181WebAnnotations

Java sample:
WebMethod:
List<Person> getUsers() {...}

Generated Schema:
<xsd:element name="getUsersResponse">
<xsd:complexType>
<xsd:sequence>
<xsd:element maxOccurs="1" minOccurs="1" name="out" nillable="true" type="ns1:ArrayOfPerson"/>
</xsd:sequence>
</xsd:complexType>
</xsd:element>

But "ns1:ArrayOfPerson" is not declared in the WSDL.

Show
Frédéric Desmoulins added a comment - Same problem for me on: Java 6 (build 1.6.0-b105) jaxws-2.1fcs XFire 1.2.4 With XFire Settings: JaxbServiceFactory JaxbTypeRegistry Jsr181WebAnnotations Java sample: WebMethod: List<Person> getUsers() {...} Generated Schema: <xsd:element name="getUsersResponse"> <xsd:complexType> <xsd:sequence> <xsd:element maxOccurs="1" minOccurs="1" name="out" nillable="true" type="ns1:ArrayOfPerson"/> </xsd:sequence> </xsd:complexType> </xsd:element> But "ns1:ArrayOfPerson" is not declared in the WSDL.
Hide
Dan Diephouse added a comment -

Hi Frederic - any chance you could attach your WSDL to this issue with your configuration? Or you could also send it to me privately dan AT envoisolutions dot com. Thanks

Show
Dan Diephouse added a comment - Hi Frederic - any chance you could attach your WSDL to this issue with your configuration? Or you could also send it to me privately dan AT envoisolutions dot com. Thanks
Hide
Frédéric Desmoulins added a comment -

The service

Show
Frédéric Desmoulins added a comment - The service
Hide
Frédéric Desmoulins added a comment -

WSDL with ArrayOfString

Show
Frédéric Desmoulins added a comment - WSDL with ArrayOfString
Hide
Frédéric Desmoulins added a comment -

Simplified Spring configuration.

Show
Frédéric Desmoulins added a comment - Simplified Spring configuration.
Hide
Frédéric Desmoulins added a comment -

web.xml

Show
Frédéric Desmoulins added a comment - web.xml
Hide
Frédéric Desmoulins added a comment -

Hi Dan,

Thanks for your quick reply!
You'll find 4 attached files generating the ArrayOf* problem.

Regards

Show
Frédéric Desmoulins added a comment - Hi Dan, Thanks for your quick reply! You'll find 4 attached files generating the ArrayOf* problem. Regards
Hide
Tom Lambrechts added a comment -

This issue also accoures with array type return parameters.

Show
Tom Lambrechts added a comment - This issue also accoures with array type return parameters.
Hide
Dan Diephouse added a comment -

I think I see the issue, Jsr181HandlerMapping does not set up the correct wsdlbuilder for you when you use the JaxbTypeRegistry. I committed changes in SVN so that you can set an appropriate service factory that will do this though:

<bean id="handlerMapping" class="....Jsr181HandlerMapping">
<property name="serviceFactory"><bean class=".org.codehaus.xfire.jaxb2.JaxbServiceFactory"></property>
...
</bean>

Show
Dan Diephouse added a comment - I think I see the issue, Jsr181HandlerMapping does not set up the correct wsdlbuilder for you when you use the JaxbTypeRegistry. I committed changes in SVN so that you can set an appropriate service factory that will do this though: <bean id="handlerMapping" class="....Jsr181HandlerMapping"> <property name="serviceFactory"><bean class=".org.codehaus.xfire.jaxb2.JaxbServiceFactory"></property> ... </bean>
Hide
Tom Lambrechts added a comment -

Dan,
I don't use the Jsr181HandlerMapping but the XFireHttpServer.
I found an other issue in the JaxbWSDLBuilder that causes that Arraytypes and other basic types are not included in the wsdl
The fix below fixes the fact that the ArrayType is not included in the WSDL

public void addDependency(org.codehaus.xfire.wsdl.SchemaType type)
{
if (!hasDependency(type))
{
if (type instanceof JaxbType)

{ JaxbType jaxbType = (JaxbType) type; if (types.contains(jaxbType)) return; classes.add(jaxbType.getActualTypeClass()); namespaces.add(jaxbType.getSchemaType().getNamespaceURI()); types.add(jaxbType); }

// FIX!!!!! when Unable to render embedded object: File (hasDependency(type) and not instanceof JaxbType (for example all aegis basic types as arrayType) we still want to add it to the WSDL) not found.
else
super.addDependency(type);
// END FIX!!!!!
}
else

{ super.addDependency(type); }

}

Show
Tom Lambrechts added a comment - Dan, I don't use the Jsr181HandlerMapping but the XFireHttpServer. I found an other issue in the JaxbWSDLBuilder that causes that Arraytypes and other basic types are not included in the wsdl The fix below fixes the fact that the ArrayType is not included in the WSDL public void addDependency(org.codehaus.xfire.wsdl.SchemaType type) { if (!hasDependency(type)) { if (type instanceof JaxbType) { JaxbType jaxbType = (JaxbType) type; if (types.contains(jaxbType)) return; classes.add(jaxbType.getActualTypeClass()); namespaces.add(jaxbType.getSchemaType().getNamespaceURI()); types.add(jaxbType); } // FIX!!!!! when Unable to render embedded object: File (hasDependency(type) and not instanceof JaxbType (for example all aegis basic types as arrayType) we still want to add it to the WSDL) not found. else super.addDependency(type); // END FIX!!!!! } else { super.addDependency(type); } }
Hide
Dan Diephouse added a comment -

Good catch! Fixing now.

Show
Dan Diephouse added a comment - Good catch! Fixing now.
Hide
Joel Turkel added a comment -

Dan,

Not sure if you're on it already or not but I believe the suggested fixes still leave a problem when sending arrays of non-null objects back and forth e.g.

@XmlRootElement
public class Foo {

  public Foo() { }

  public Foo(String name) { this.name = name; }

  @XmlElement
  public String name;

}

@WebService
public class ArrayService {

  @WebMethod
  public Foo[] getFoos() { return new Foo[] { new Foo("Bob"), new Foo("Hank") }; }

}

This ends up generating XML something like:

<Foo>
  <foo>
    <name>Bob</name>
  </foo>
</Foo>
<Foo>
  <foo>
    <name>Hank</name>
  </foo>
</Foo>

I'm not very familiar with the code but I think the problem is in org.codehaus.xfire.aegis.type.basic.ArrayType.writeValue(). I believe the correct implementation of this method should be:

protected void writeValue(Object value, 
                              MessageWriter writer, 
                              MessageContext context, 
                              Type type,
                              String name,
                              String ns) 
        throws XFireFault
    {
        type = AegisBindingProvider.getWriteType(context, value, type);
        
        // Bug Fix
        if (type.isWriteOuter()) 
        {
        	writer = writer.getElementWriter(name, ns);
        }
        
        if (value==null && type.isNillable())
        	writer.writeXsiNil();
        else
            type.writeObject( value, writer, context );

        // Bug Fix
        if (type.isWriteOuter()) 
        {
        	writer.close();
        }
    }

Not sure if I'm interpreting the isWriteOuter flag correctly, but it seems to fix the problem???

Thanks,
Joel Turkel

Show
Joel Turkel added a comment - Dan, Not sure if you're on it already or not but I believe the suggested fixes still leave a problem when sending arrays of non-null objects back and forth e.g.
@XmlRootElement
public class Foo {

  public Foo() { }

  public Foo(String name) { this.name = name; }

  @XmlElement
  public String name;

}

@WebService
public class ArrayService {

  @WebMethod
  public Foo[] getFoos() { return new Foo[] { new Foo("Bob"), new Foo("Hank") }; }

}
This ends up generating XML something like:
<Foo>
  <foo>
    <name>Bob</name>
  </foo>
</Foo>
<Foo>
  <foo>
    <name>Hank</name>
  </foo>
</Foo>
I'm not very familiar with the code but I think the problem is in org.codehaus.xfire.aegis.type.basic.ArrayType.writeValue(). I believe the correct implementation of this method should be:
protected void writeValue(Object value, 
                              MessageWriter writer, 
                              MessageContext context, 
                              Type type,
                              String name,
                              String ns) 
        throws XFireFault
    {
        type = AegisBindingProvider.getWriteType(context, value, type);
        
        // Bug Fix
        if (type.isWriteOuter()) 
        {
        	writer = writer.getElementWriter(name, ns);
        }
        
        if (value==null && type.isNillable())
        	writer.writeXsiNil();
        else
            type.writeObject( value, writer, context );

        // Bug Fix
        if (type.isWriteOuter()) 
        {
        	writer.close();
        }
    }
Not sure if I'm interpreting the isWriteOuter flag correctly, but it seems to fix the problem??? Thanks, Joel Turkel
Hide
Dan Diephouse added a comment -

Thanks Joel. I actually had just found that. It turns out there was one other problem though too. In the Java5TypeCreator it wouldn't kick back to the JaxbTypeCreator when it was creating the collection's generic type. I'm committing a fix in a few minutes then cutting 1.2.5!

Show
Dan Diephouse added a comment - Thanks Joel. I actually had just found that. It turns out there was one other problem though too. In the Java5TypeCreator it wouldn't kick back to the JaxbTypeCreator when it was creating the collection's generic type. I'm committing a fix in a few minutes then cutting 1.2.5!
Hide
Dan Diephouse added a comment -

Thanks to Tom, this has been fixed in SVN.

Thanks for everyone's extreme patience with this issue, it was a bit difficult to track down. In the midst of cutting 1.2.5 right now, so it'll be fixed real soon!

Show
Dan Diephouse added a comment - Thanks to Tom, this has been fixed in SVN. Thanks for everyone's extreme patience with this issue, it was a bit difficult to track down. In the midst of cutting 1.2.5 right now, so it'll be fixed real soon!
Hide
Oren Shoham added a comment -

Hi Dan,

I'm having the same problem in 1.2.4, and upgraded to 1.2.6, but the problem persists - List<String> is generated as ArrayOfString in the WSDL.

You mentioned that it is possible to solve this by using

<bean id="handlerMapping" class="....Jsr181HandlerMapping">
<property name="serviceFactory"><bean class=".org.codehaus.xfire.jaxb2.JaxbServiceFactory"></property>
...
</bean>

I'm not using a spring container. Can you please provide a simple Java code sample to set this up in 1.2.6?

Thanks,

Oren

Show
Oren Shoham added a comment - Hi Dan, I'm having the same problem in 1.2.4, and upgraded to 1.2.6, but the problem persists - List<String> is generated as ArrayOfString in the WSDL. You mentioned that it is possible to solve this by using <bean id="handlerMapping" class="....Jsr181HandlerMapping"> <property name="serviceFactory"><bean class=".org.codehaus.xfire.jaxb2.JaxbServiceFactory"></property> ... </bean> I'm not using a spring container. Can you please provide a simple Java code sample to set this up in 1.2.6? Thanks, Oren

People

Vote (8)
Watch (7)

Dates

  • Created:
    Updated:
    Resolved: