Search code examples
javaweb-serviceswsdlglassfish-3

Why does JAX-WS convert from Holder for multiple return types to single part response?


I have a WSDL from an existing service for which I'm developing a client and a simulator (to test both sides of the interface before going live). I'm using glassfish JAXWS libraries to generate code, and glassfish 3.1.2.2 as the server. We're running Java 1.6.

In the original WSDL, multiple return types are defined as multiple parts, and the auto-generated code creates Holder entries to hold them. That is, a sample return message in the WSDL looks like:

 <message name="returnTypeMessage">
    <part name="outDoc" element="tns:myResponseType"/>
    <part name="XMLAttach" element="xsd:string"/>
 </message>
 ...
<operation name="...">
  <wsdlsoap:operation soapAction="" soapActionRequired="false"/>
  <input ... />
  <output name="...response">
    <mime:multipartRelated>
      <mime:part>
        <wsdlsoap:body parts="outDoc" use="literal" />
      </mime:part>
      <mime:part>
        <mime:content part="XMLAttach" type="text/xml" />
      </mime:part>
    </mime:multipartRelated>
  </output>
</operation>

and the generated signature in the port interface looks like:

@WebMethod
public void methodName(@WebParam(name="...") inParameter,
  @WebParam(name="myResponseType", partName="outDoc") Holder<myResponseType> outDoc,
  @WebParam(name="moreData", partName="moreData") Holder<String> moreData);

So far, so good. Now, when I create a service endpoint class from this interface and deploy it to GlassFish 3.1.2.x, and then I go to the WSDL, the WSDL instead shows:

 <message name="returnTypeMessage">
    <part name="parameters" element="tns:someNewResponseType" />
 </message>
...
<operation name="...">
  <soap:operation soapAction=""/>
  <input>
    <soap:body use="literal"/>
  </input>
  <output>
    <soap:body use="literal"/>
  </output>
</operation>

and the port signature looks like:

@WebMethod
@WebResult(name="someNewResponseType", partName="parameters")
public someNewResponseType methodName(...);

So it's converted it from having multiple return types each in its own Holder to a single return parameter (class) that contains multiple elements within it.

The real problem is that then trying to run a client against this deployed service, using the former signature, fails with a "IllegalStateException: reader must be on a START_ELEMENT event, not a 8 event".

I find this behavior bothersome and would really like to get a good explanation of why I can generate a port from a WSDL, deploy an implementation of that, and it then broadcasts a WSDL that is incompatible with a client generated against that initial port definition.


Solution

  • This turned out to be a combination of a less-than-ideal WSDL (which I can't change) and parameter formatting. There are two requirements to the solution.

    First, in the port file that was generated, change the SOAPBinding from SOAPBinding.ParameterStyle.BARE to WRAPPED:

    @SOAPBinding(parameterStyle=SOAPBinding.ParameterStyle.WRAPPED)
    

    I did that using the ant replace task after generating the code using wsimport. The wsimport customization options for enableWrapperStyle weren't working (I believe because of a caveat that if it can't otherwise fully mark wrapped the wsdl it will revert to bare).

    Second, within the service implementation class, in the @WebService annotation, the endpointInterface="..." attribute had to be added. Without that, it would fail during calls. I also added:

    @WebService(..., endpointInterface="...")
    @BindingType(value=SOAPBinding.SOAP12HTTP_BINDING)
    

    Given those three items (WRAPPED, endpointInterface, BindingType) it then worked for a service and client generated from the WSDL. Any other combination resulted in some kind of error (that now appear to be related to other problems but ultimately red herrings).

    Note that the generated WSDL is still incorrect, but the client works, which is ultimately what matters.