We have a JAX-WS/JAXB binding to an external web-service that is working fine on Java 7 (1.7.0u80) with the included reference implementations. During migration to Java 8 (1.8.0u66) the web service calls generally work OK, however it cannot unmarshall SOAP faults and their detail elements to Java Exceptions with custom detail any longer, giving instead a prefix not bound to a namespace error.
The failure is
Caused by: javax.xml.ws.WebServiceException: java.lang.IllegalArgumentException: prefix xsd is not bound to a namespace
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.createException(SOAPFaultBuilder.java:138)
at com.sun.xml.internal.ws.client.sei.StubHandler.readResponse(StubHandler.java:238)
at com.sun.xml.internal.ws.db.DatabindingImpl.deserializeResponse(DatabindingImpl.java:189)
at com.sun.xml.internal.ws.db.DatabindingImpl.deserializeResponse(DatabindingImpl.java:276)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:104)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:77)
at com.sun.xml.internal.ws.client.sei.SEIStub.invoke(SEIStub.java:147)
at com.sun.proxy.$Proxy61.proprietaryServiceCall(Unknown Source)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at org.springframework.remoting.jaxws.JaxWsPortClientInterceptor.doInvoke(JaxWsPortClientInterceptor.java:580)
at org.springframework.remoting.jaxws.JaxWsPortClientInterceptor.doInvoke(JaxWsPortClientInterceptor.java:554)
... 56 more
Caused by: java.lang.IllegalArgumentException: prefix xsd is not bound to a namespace
at com.sun.xml.internal.bind.DatatypeConverterImpl._parseQName(DatatypeConverterImpl.java:355)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.LeafPropertyXsiLoader.selectLoader(LeafPropertyXsiLoader.java:75)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.LeafPropertyXsiLoader.startElement(LeafPropertyXsiLoader.java:58)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext._startElement(UnmarshallingContext.java:559)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.startElement(UnmarshallingContext.java:538)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.InterningXmlVisitor.startElement(InterningXmlVisitor.java:60)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.SAXConnector.startElement(SAXConnector.java:153)
at com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(DOMScanner.java:229)
at com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(DOMScanner.java:266)
at com.sun.xml.internal.bind.unmarshaller.DOMScanner.visit(DOMScanner.java:235)
at com.sun.xml.internal.bind.unmarshaller.DOMScanner.scan(DOMScanner.java:112)
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl.unmarshal0(UnmarshallerImpl.java:354)
at com.sun.xml.internal.bind.v2.runtime.BridgeImpl.unmarshal(BridgeImpl.java:124)
at com.sun.xml.internal.bind.api.Bridge.unmarshal(Bridge.java:309)
at com.sun.xml.internal.ws.db.glassfish.BridgeWrapper.unmarshal(BridgeWrapper.java:217)
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.getJAXBObject(SOAPFaultBuilder.java:304)
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.createException(SOAPFaultBuilder.java:135)
The response from the external service looks like the below (I have anonymized type names, but left everything else)
<env:Envelope xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Header/>
<env:Body>
<env:Fault>
<faultcode>env:Server</faultcode>
<faultstring>ERROR MESSAGE</faultstring>
<detail>
<n1:ProprietaryException xmlns:n1="java:com.company.service" xsi:type="n1:ProprietaryException">
<errorCode xsi:type="xsd:int">400</errorCode>
<errorReason xsi:type="xsd:string">Specific error</errorReason>
</n1:ProprietaryException>
</detail>
</env:Fault>
</env:Body>
</env:Envelope>
The problem is with the xsd:int and xsd:string in the faultCode and faultReason. It seems like the prefix/namespace declarations are not being inherited from the top level envelope when binding. The problem looks similar to this question except unlike that question this is about SOAP Fault handling, and in my case, the code is deep inside JAX-WS and JAXB so I have no idea how we could fix it or workaround it.
Unless the old code was relying on some behaviour that never should have worked, I can't help but conclude something has been broken between JAX-WS and JAXB in their Java 8 implementations.
Update (Jan 4 2016): I've also tried this with a CXF 3.1.4 client instead of the Metro RI. Same problem. It seems to be the same issue as mentioned here
Update (Jan 6 2016): I've narrowed this problem to a change introduced to the JAXB RI 2.2.6. Thus the problem can be replicated on Java 7 with a forced upgrade to JAXB RI 2.2.6. It seems it might relate to changes made in JAXB-890.
I have tested working around this in at least two different ways:
- Use Java 8 with JAXB force downgraded back to 2.2.5 (JAX-WS version doesn't seem to matter). Doesn't seem a good long-term solution.
- I found that
-Dcom.sun.xml.bind.improvedXsiTypeHandling=false
(or the equivalent.internal
property if using the bundled JDK JAXB RI) seems to workaround the issue. But I have no idea what this setting really does; or what the implications would be for the rest of the JAXB usage in my system.
Any ideas for how to proceed here?
xmlns:xsd=
declaration from theEnvelope
to theFault
element? – Photomechanical