My jax-ws webservice client returns only empty objects
Asked Answered
P

3

8

I have a third party webservice for which I generate a client using wsimport. Each call to the webservice completes successfully, but the response object I get back has all its fields set to null. Monitoring the network I can see that on the wire all of the XML elements in the response message have values in them, so the object should have non-null data in it. Also, a client for the same service generated with old axis1 and called with the same data returns a non-empty response. Any idea what's happening? (In case it makes any difference I'm using MOXy's implementation of JAXB).

Update: I've been able to narrow it down. The wsdl defines object in its own namespace, say http://www.acme.com/ws. The response I get from the service is

<?xml version="1.0" encoding="UTF-8"?>
... SOAP envelope ...
<ns1:opINFOWLResponse xmlns:ns1="http://www.acme.com/ws" 
 xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns1:responseINFOWL xsi:type="ns1:responseINFOWL">
<result>6003</result>
<ndserr/>
<transid>61437594</transid>
<descriptionerr>BLAH.</descriptionerr>
</ns1:responseINFOWL>
</ns1:opINFOWLResponse>
... SOAP closing tags ...

and is unmarshalled to a non null OpINFOWLResponse which wraps around a non null responseINFOWL object with all the fields set to null. Just for fun I've tried writing a couple of lines to unmarshal the above snippet (after stripping the SOAP overhead)

JAXBContext ctx = JAXBContext.newInstance(OpINFOWLResponse.class);
Unmarshaller u = ctx.createUnmarshaller();

OpINFOWLResponse o = (OpINFOWLResponse) u.unmarshal(new StringReader(theSnippetAbove));
ResponseINFOWL w = o.getResponseINFOWL();

and I get the same result. If I change the XML above to

<?xml version="1.0" encoding="UTF-8"?>
<ns1:opINFOWLResponse xmlns:ns1="http://www.acme.com/ws" 
 xmlns:xsd="http://www.w3.org/2001/XMLSchema" 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ns1:responseINFOWL xsi:type="ns1:responseINFOWL">
<ns1:result>6003</ns1:result>
<ns1:ndserr/>
<ns1:transid>61437594</ns1:transid>
<ns1:descriptionerr>BLAH.</ns1:descriptionerr>
</ns1:responseINFOWL>
</ns1:opINFOWLResponse>

Everything works fine. Bummer.

Update (again): Same behaviour with both jaxb-RI and Moxy. Still have no idea what's wrong.

Update (Sep. 9): The suggestion below about namespace qualification being wrong is interesting, but I supposed wsimport would get things right. Anyway, this is my package-info.java

@XmlSchema(
namespace = "http://www.acme.com/ws", 
elementFormDefault = XmlNsForm.QUALIFIED)
package it.sky.guidaTv.service.remote;

import javax.xml.bind.annotation.XmlSchema;
import javax.xml.bind.annotation.XmlNsForm;

and this is the relevant part of the ResponseINFOWL class

/*
 * <p>Java class for responseINFOWL complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType name="responseINFOWL">
 *   &lt;complexContent>
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       &lt;sequence>
 *         &lt;element name="result" type="{http://www.w3.org/2001/XMLSchema}string"/>
 *         &lt;element name="descriptionerr" type="{http://www.w3.org/2001/XMLSchema}string"/>
 *         &lt;element name="transid" type="{http://www.w3.org/2001/XMLSchema}string"/>
 *         &lt;element name="ndserr" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
 *         &lt;element name="wallet" type="{http://www.acme.com/ws}t_wallet" minOccurs="0"/>
 *       &lt;/sequence>
 *     &lt;/restriction>
 *   &lt;/complexContent>
 * &lt;/complexType>
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "responseINFOWL", propOrder = {
"result", "descriptionerr", "transid", "ndserr", "wallet" })
public class ResponseINFOWL {

@XmlElement(required = true)
protected String result;
@XmlElement(required = true)
protected String descriptionerr;
@XmlElement(required = true)
protected String transid;
protected String ndserr;
protected TWallet wallet;

    // getters, setters and all.

}

I've tried playing a bit with the namespaces in package-info but still no joy.

Paulitapaulk answered 24/8, 2011 at 10:59 Comment(2)
Can you provide samples from the messages and classes? This will help determine where the mismatch in the mapping is.Accord
Maybe I could post suitably anonymized wsdl file and test class, everything else in my case is generated by wsimport. Funny thing is that other services from the same third party work fine.Paulitapaulk
H
3

I recently ran into the exact same problem you encountered, and it came down to the fact that the service I was contacting was returning something different from what its WSDL advertised. The service used an old version of Apache Axis (1.4) with behaviour that conflicts with current JAX-WS implementations.

In particular, the namespace on the actual response body contents was NOT what was expected by the client code generated by JAX-WS's wsimport utility. For example, the actual response looked something like this, with the serviceResponse and all its children in namespace "http://foo.com":

<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Body>
        <serviceResponse xmlns="http://foo.com">
            <messageReturn>
                <messageId>12345</messageId>
                <status>Ok</status>
            </messageReturn>
        </serviceResponse>
    </soapenv:Body>
</soapenv:Envelope>

In contrast to what was actually coming back, the client stubs generated by wsimport were expecting something like the response below, with the serviceResponse element in namespace "http://foo.com" and the contained child messageReturn element in the anonymous namespace.

<?xml version="1.0" encoding="utf-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <soapenv:Body>
        <n1:serviceResponse xmlns:n1="http://foo.com">
            <messageReturn>
                <messageId>12345</messageId>
                <status>Ok</status>
            </messageReturn>
        </n1:serviceResponse>
    </soapenv:Body>
</soapenv:Envelope>

Since I could not change the service I was consuming, I instead wrote a new WSDL myself that used a wrapped doc-literal binding to explicitly control the expected structure of the response (and request, of course). There is a really good article on WSDL binding types over IBM Developerworks.

The WSDL I created looked something like this:

<?xml version="1.0" encoding="UTF-8"?>

<wsdl:definitions targetNamespace="http://foo.com"
                  xmlns:tns="http://foo.com"
                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
                  xmlns:wsdlsoap="http://schemas.xmlsoap.org/wsdl/soap/"
                  xmlns:xsd="http://www.w3.org/2001/XMLSchema">

    <!-- Define the XML types we need to send and receive (used by the message definitions below) -->
    <wsdl:types>
        <schema targetNamespace="http://foo.com" xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">

            <!-- Reusable types -->
            <complexType name="ResponseType">
                <sequence>
                    <element name="messageId" nillable="true" type="xsd:string" />
                    <element name="status" nillable="true" type="xsd:string" />
                </sequence>
            </complexType>

            <complexType name="InputType">
                <sequence>
                    <element name="firstName" nillable="true" type="xsd:string" />
                    <element name="lastName" nillable="true" type="xsd:string" />
                    <element name="command" nillable="true" type="xsd:string" />
                </sequence>
            </complexType>


            <!-- Specific input/output elements used in wsdl:message definitions -->
            <element name="serviceResponse">
                <complexType>
                    <sequence>
                        <element name="messageReturn" type="tns:ResponseType" />
                    </sequence>
                </complexType>
            </element>

            <element name="serviceRequest">
                <complexType>
                    <sequence>
                        <element name="message" type="tns:InputType" />
                    </sequence>
                </complexType>
            </element>
        </schema>
    </wsdl:types>


    <!-- Define the WSDL messages we send/receive (used by the port definition below) -->
    <wsdl:message name="serviceResponseMessage">
        <wsdl:part name="part1Name" element="tns:serviceResponse" />
    </wsdl:message>

    <wsdl:message name="serviceRequestMessage">
        <wsdl:part name="part1name" element="tns:serviceRequest" />
    </wsdl:message>


    <!-- Define the WSDL port (used by the binding definition below) -->
    <wsdl:portType name="ServicePort">
        <wsdl:operation name="serviceOperation">
            <wsdl:input message="tns:serviceRequestMessage" />
            <wsdl:output message="tns:serviceResponseMessage" />
        </wsdl:operation>
    </wsdl:portType>


    <!-- Define the WSDL binding of the port (used by the service definition below) -->
    <wsdl:binding name="ServiceSoapBinding" type="tns:ServicePort">
        <wsdlsoap:binding style="document" transport="http://schemas.xmlsoap.org/soap/http" />

        <wsdl:operation name="serviceOperation">
            <wsdlsoap:operation soapAction="" />

            <wsdl:input>
                <wsdlsoap:body use="literal" />
            </wsdl:input>

            <wsdl:output>
                <wsdlsoap:body use="literal" />
            </wsdl:output>
        </wsdl:operation>
    </wsdl:binding>


    <!-- Finally, define the actual WSDL service! -->
    <wsdl:service name="UserCommandService">
        <wsdl:port binding="tns:ServiceSoapBinding" name="ServicePort">
            <!-- This address is just a placeholder, since the actual target URL will be specified at runtime -->
            <wsdlsoap:address location="http://localhost:8080/blah" />
        </wsdl:port>
    </wsdl:service>
</wsdl:definitions>

With the custom WSDL, I was able to use wsimport to generate client stubs that work perfectly with the service. As well, with the wrapped doc-literal approach, I completely control the expected structure and namespace of the request/response, so I can implement multiple namespaces in that XML if necessary.

Enjoy...

Hittite answered 26/9, 2016 at 14:17 Comment(0)
A
1

Please correct me if I have your use case incorrect.

You can unmarshal:

<?xml version="1.0" encoding="UTF-8"?>
<ns1:opINFOWLResponse xmlns:ns1="http://www.acme.com/ws"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <ns1:responseINFOWL xsi:type="ns1:responseINFOWL">
        <ns1:result>6003</ns1:result>
        <ns1:ndserr />
        <ns1:transid>61437594</ns1:transid>
        <ns1:descriptionerr>BLAH.</ns1:descriptionerr>
    </ns1:responseINFOWL>
</ns1:opINFOWLResponse>

But cannot unmarshal:

<?xml version="1.0" encoding="UTF-8"?>
<ns1:opINFOWLResponse xmlns:ns1="http://www.acme.com/ws"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <ns1:responseINFOWL xsi:type="ns1:responseINFOWL">
        <result>6003</result>
        <ndserr />
        <transid>61437594</transid>
        <descriptionerr>BLAH.</descriptionerr>
    </ns1:responseINFOWL>
</ns1:opINFOWLResponse>

This means that the namespace qualification in your JAXB mappings is incorrect. The following may help:

If you could post the class that maps to this section of XML, and the package-info class if there is one, then I can help you modify the mappings.

Accord answered 8/9, 2011 at 20:49 Comment(0)
B
1

I had the same problem, in my case, my java service was expecting xml elements without namespace, but the service was reponsing with namespaces. I fixed by adding @XmlElement annotation setting the expected namespace and element name as follows:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "MyResponseType", propOrder = { "someProp" })
public class MyResponseType {
  @XmlElement(namespace = "http://www.your-namespace.com/schema/v1.0", name = "someProp")
  protected Integer someProp;
}
Bulldoze answered 27/7, 2020 at 21:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.