0
votes

I'm trying to pull data from a webservice using JAX-WS and code generated by wsimport. I'm able to send the request and get a response from the server, but JAX-WS is throwing an exception when it tries to read the response because no elements in the response body have a declared namespace.

Exception in thread "main" com.sun.xml.internal.ws.streaming.XMLStreamReaderException: unexpected XML tag. expected: {http://www.theserver.com/cmdb_rel_ci}getRecordsResponse but found: {null}getRecordsResponse

WSDL Excerpt:

<wsdl:definitions xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:http="http://schemas.xmlsoap.org/wsdl/http/" xmlns:tns="http://www.theserver.com/cmdb_rel_ci" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:sncns="http://www.theserver.com" xmlns:mime="http://schemas.xmlsoap.org/wsdl/mime/" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" targetNamespace="http://www.theserver.com">
  <wsdl:types>
    <xsd:schema elementFormDefault="unqualified" targetNamespace="http://www.theserver.com/cmdb_rel_ci">
      <xsd:element name="getRecords">
        <xsd:complexType>
          <xsd:sequence>
            <!-- Request arguments -->
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
      <xsd:element name="getRecordsResponse">
        <xsd:complexType>
          <xsd:sequence>
            <xsd:element maxOccurs="unbounded" minOccurs="0" name="getRecordsResult">
              <xsd:complexType>
                <xsd:sequence>
                  <!-- Response Fields -->
                </xsd:sequence>
              </xsd:complexType>
            </xsd:element>
          </xsd:sequence>
        </xsd:complexType>
      </xsd:element>
    </xsd:schema>
  </wsdl:types>
  <wsdl:message name="getRecordsSoapIn">
    <wsdl:part name="cmdb_rel_ci" element="tns:getRecords"></wsdl:part>
  </wsdl:message>
  <wsdl:message name="getRecordsSoapOut">
    <wsdl:part name="cmdb_rel_ci" element="tns:getRecordsResponse"></wsdl:part>
  </wsdl:message>
</wsdl:definitions>

Successful Request and Response in SoapUI:

<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns2="http://www.theserver.com/cmdb_rel_ci">
  <soapenv:Header/>
  <soapenv:Body>
    <ns2:getRecords>
      <arg1>value</arg1>
      <!-- Remaining Arguments -->
    </ns2:getRecords>
  </soapenv:Body>
</soapenv:Envelope>


<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <SOAP-ENV:Body>
    <getRecordsResponse>
      <getRecordsResult>
        <resultField1>value</resultField1>
        <resultField2>value2</resultField2>
        <!-- etc. -->
      </getRecordsResult>
      <!-- Other getRecordsResult elements -->
    </getRecordsResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

How do I tell JAX-WS that the <getRecordsResponse> element won't have a namespace? I've tried setting targetNamespace = "" in the @ResponseWrapper annotation for getRecords(), but that just makes it expect the targetNamespace parameter from @WebService instead. And when I tried as a last-ditch effort to set the WebService's target namespace blank, it tried to infer a namespace from the Java package name (e.g. "http://my_package.com").

2

2 Answers

0
votes

I was unable to find a way to tell JAX-WS not to expect a namespace, but I was able to work around it by pre-processing the response in a SOAPHandler and manually adding a namespace to the XML element (by giving it a new QName that included a namespace declaration).

MyHandler.java:

public class MyHandler implements SOAPHandler<SOAPMessageContext> {
    @Override
    public void close(MessageContext context) { 
    }

    @Override
    public boolean handleMessage(SOAPMessageContext context) {
        //Don't intercept outbound messages (SOAP Requests)
        if ((Boolean)context.get(MessageContext.MESSAGE_OUTBOUND_PROPERTY)) {return true;}

        SOAPMessage message = context.getMessage();
        try {
            SOAPBody body = message.getSOAPBody();
            Iterator<SOAPElement> bodyIterator = body.getChildElements();
            SOAPElement responseElement = bodyIterator.next();
            responseElement.setElementQName(new QName("http://www.theserver.com/cmdb_rel_ci", origGetRecordsResponse.getLocalName(), "cmdb"));
        } catch (Exception e) {e.printStackTrace();}
        return true;
    }

    @Override
    public boolean handleFault(SOAPMessageContext context) {
        return false;
    }

    @Override
    public Set<QName> getHeaders() {
        return Collections.emptySet();
    }
}

Then I just had to add my Handler to the HandlerChain with the following code in my main class, and it worked.

Binding binding = ((BindingProvider)port).getBinding();
List<Handler> handlers = binding.getHandlerChain();
handlers.add(new MyHandler());
binding.setHandlerChain(handlers);
0
votes

The question here is How do I tell JAX-WS that the element won't have a namespace? I've tried setting targetNamespace = "" in the @ResponseWrapper annotation for getRecords(), but that just makes it expect the targetNamespace parameter from @WebService instead.

Answer is here https://docs.oracle.com/cd/E13222_01/wls/docs92/webserv/annotations.html

TO make sure that targetNamespace is not ignored, change the SOAPBinding style to SOAPBinding.Style.Document

Information from the above url which gives more insights into the issue.

The XML namespace of the return value. This value is used only used for document-style Web Services, in which the return value maps to an XML element. The default value is the targetNamespace of the Web Service.