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?
Fault
class, the incoming interceptor chain should be enriched withXSLTInInterceptor
. The interceptor (through XSLT) should export all the sensible data (fault code and message) from inner element to the topmostsoapenv:Fault
level. This should help to avoid the CXF bug issues. After that the client code should be modified to obtain error info fromSOAPFaultException
(not generated exception class). Be careful, the solution may worsen performance (increase client methods CPU execution time). - Toparvion