'javax.xml.ws.soap.SOAPFaultException: Unmarshalling Error: unexpected element (uri:"", local:"fault") after migrating from AXIS to CXF

The Problem

I am trying to migrate my web service client from Apache AXIS-1 to Apache CXF. The web service itself runs om AXIS-1 and is out of my control.
I generated all the CXF artifacts and the client has been working well until any fault is thrown by the service. The faulty XML looks like:

<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/">
  <soapenv:Body>
    <soapenv:Fault>
      <faultcode>soapenv:Server</faultcode>
      <faultstring>Remote error processing component card request</faultstring>
      <detail>
        <fault>
          <faultcode>:-303</faultcode>
          <faultstring>Remote error processing component card request</faultstring>
          <faultactor>remote_service</faultactor>
          <detail>
            <common-detail>
              <code>-303</code>
              <message>Remote error processing component card request</message>
            </common-detail>
          </detail>
        </fault>
      </detail>
    </soapenv:Fault>
  </soapenv:Body>
</soapenv:Envelope>

This makes the CXF generated client fall with exception:

javax.xml.bind.UnmarshalException: unexpected element (uri:"", local:"fault"). 
Expected elements are <{http://some.namespace.org}AnsBonusAutopayStatus>,<{http://some.namespace.org}AnsBonusAutopaySubscribe>,
...
<{http://some.namespace.org}fault>
    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:603)
    at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:244)
    at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportError(Loader.java:239)
    at com.sun.xml.bind.v2.runtime.unmarshaller.Loader.reportUnexpectedChildElement(Loader.java:116)
    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext$DefaultRootLoader.childElement(UnmarshallingContext.java:1009)
    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:446)
    at com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:427)
    at com.sun.xml.bind.v2.runtime.unmarshaller.InterningXmlVisitor.startElement(InterningXmlVisitor.java:71)
    at com.sun.xml.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:137)
    at com.sun.xml.bind.unmarshaller.DOMScanner.visit(DOMScanner.java:240)
    at com.sun.xml.bind.unmarshaller.DOMScanner.scan(DOMScanner.java:123)
...

AFAIU from this stacktrace, unmarshaller expects the fault element to have certain namespace declaration but it comes without one.

Can anyone suggest what is wrong with that client and how to avoid the error?

Tested Solutions

There are a lot of similar questions but not all the solutions are applicable for me because I can't change anything on the server side (at least for backward compatibility reasons) and changing the WSDL is also not appreciated. Thus changes are restricted in client side only (including its (re)generation). I've tried several possible solutions based on similar questions:

  • adding package-info.class with default namespace declaration - nothing changes;
  • force setting empty namespace for generated Fault and/or Fault_Exception classes - NullPointerException in CXF routines during constructing the exception object (see EDIT section below);
  • using SAX XmlFilter - couldn't find a way to embed it into CXF generated routines.

Additional Info

CXF generated classes for exception and its faultInfo (without any of my changes) look like:
Fault class:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "faultcode",
    "faultstring",
    "faultactor",
    "detail"
})
@XmlRootElement(name = "fault")
public class Fault {

    @XmlElement(required = true)
    protected String faultcode;
...

Fault_Exception class:

@WebFault(name = "fault", targetNamespace = "http://some.namespace.org")
public class Fault_Exception extends Exception {

    private Fault fault;

    public Fault_Exception() {
        super();
    }
...

Some WSDL excerpts. WSDL Fault message declaration:

  <wsdl:message name="Fault">
    <wsdl:part name="fault" element="fault"/>
  </wsdl:message>

XSD fault element:

<xs:element name="fault">
<xs:annotation>
  <xs:documentation>Comment describing your root element</xs:documentation>
</xs:annotation>
<xs:complexType>
  <xs:sequence>
    <xs:element name="faultcode">...</xs:element>
    <xs:element name="faultstring">...</xs:element>
    <xs:element name="faultactor">...</xs:element>
    <xs:element minOccurs="0" name="detail">...</xs:element>
  </xs:sequence>
</xs:complexType>
</xs:element>

WSDL operation description example:

<wsdl:operation name="BonusConversionState">
  <wsdl:input name="BonusConversionStateRequest" message="tns:BonusConversionStateRequest"/>
  <wsdl:output name="BonusConversionStateResponse" message="tns:BonusConversionStateResponse"/>
  <wsdl:fault name="Fault" message="tns:Fault"/>
</wsdl:operation>

WSDL root element:

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="http://some.namespace.org" 
                  xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" 
                  xmlns:tns="http://some.namespace.org" 
                  xmlns:xs="http://www.w3.org/2001/XMLSchema" 
                  xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/">

More additional info can be provided.
Thanks in advance!

EDIT:

During "investigation" I've found out that NullPointerException (that I mentioned in "Tested Solutions") is not a consequence of my faulty actions. It's a bug of CXF:

Both tickets are closed with resolution "Fixed", but in comments users report they still catch this bug. It seems that developers did not cover all the cases where custom exceptions are handled by CXF and for some reasons it sometimes leads to

org.apache.cxf.interceptor.ClientFaultConverter processFaultDetail
INFO: Exception occurred while creating exception: null
java.lang.NullPointerException

Has anybody ever faced this problem too?



Solution 1:[1]

This appears to be a case of the service not complying with its own contract where the fault is concerned. While you may not have control of the service, this is worth a discussion with the service provider.

The fault element your client is complaining about is the immediate child of the first 'detail' element.

Based on the exception message, the first child element of 'detail' is expected to be (assuming target namespace 'http://some.namespace.org') either AnsBonusAutopayStatus or AnsBonusAutopaySubscribe and not the {}fault element.

If you are able to provide a bit more of the wsdl around the fault declaration in the operation and the wsdl types additional static analysis can be provided.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 dcbyers