I have a third party jar that ships with a jaxb-impl.jar and includes it in its manifest classpath. The problem is, it seems as though supplying your own version of JAXB (regardless of which version it may be) seems to break the SoapFaultBuilder in JAX-WS.
According to the Unofficial JAXB Guide, it seems as though Sun deliberately changed the package name when it folded JAXB into the JDK in order to avoid conflicts with the stand-alone version. However, the SoapFaultBuilder (part of JAX-WS I believe) that ships with the JDK is explicitly dependent on the new, internal package name. This causes it to fail when building a fault message if you've added a stand-alone JAXB jar (even if it is the same version number of JAXB).
Here is my small test case: I make a trivial Web Service:
package wstest;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
//Service Endpoint Interface
@WebService
@SOAPBinding(style = Style.RPC)
public interface HelloWorld{
@WebMethod String getHelloWorldAsString(String name);
}
And an implementation that simply throws an exception. (Since the problem only occurs in the SOAPFaultBuilder):
package wstest;
import javax.jws.WebService;
//Service Implementation
@WebService(endpointInterface = "wstest.HelloWorld")
public class HelloWorldImpl implements HelloWorld{
@Override
public String getHelloWorldAsString(String name) {
//return "Hello World JAX-WS " + name;
throw new RuntimeException("Exception for: " + name);
}
}
And a class to publish the web service:
package wstest;
import javax.xml.ws.Endpoint;
//Endpoint publisher
public class HelloWorldPublisher{
public static void main(String[] args) {
Endpoint.publish("http://localhost:9999/ws/hello", new HelloWorldImpl());
}
}
I run the HelloWorldPublisher, and then run this client against it:
package wstest;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
public class HelloWorldClient{
public static void main(String[] args) throws Exception {
URL url = new URL("http://localhost:9999/ws/hello?wsdl");
//1st argument service URI, refer to wsdl document above
//2nd argument is service name, refer to wsdl document above
QName qname = new QName("http://wstest/", "HelloWorldImplService");
Service service = Service.create(url, qname);
HelloWorld hello = service.getPort(HelloWorld.class);
System.out.println(hello.getHelloWorldAsString("Matt"));
}
}
This correctly spits out the exception that was thrown by the Web Service. However, when I add any version of jaxb-impl.jar, whether on the classpath or in the endorsed lib, I get this stack trace:
Exception in thread "main" java.lang.ExceptionInInitializerError
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:107)
at com.sun.xml.internal.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:78)
at com.sun.xml.internal.ws.client.sei.SEIStub.invoke(SEIStub.java:107)
at $Proxy19.getHelloWorldAsString(Unknown Source)
at wstest.HelloWorldClient.main(HelloWorldClient.java:21)
Caused by: java.lang.ClassCastException: com.sun.xml.bind.v2.runtime.JAXBContextImpl cannot be cast to com.sun.xml.internal.bind.api.JAXBRIContext
at com.sun.xml.internal.ws.fault.SOAPFaultBuilder.<clinit>(SOAPFaultBuilder.java:533)
... 5 more
The exception occurs because com.sun.xml.bind.v2.runtime.JAXBContextImpl from my jaxb-impl extends com.sun.xml.bind.api.JAXBRIContext instead of com.sun.xml.internal.bind.api.JAXBRIContext (note the missing 'internal' sub-package in the package hierarchy).
Also according to the Unofficial JAXB Guide, they say you need to use endorsed lib in order to correctly override the version of JAXB. As it turns out though, SOAPFaultBuilder uses JAXBContext.newInstance() to search the classpath for a file named /META-INF/services/javax.xml.bind.JAXBContext
, then manually load (and reflexively create) a JAXBContext based on the class name specified in the file. So it doesn't matter - classpath or endorsed lib gives you the same behavior.
One workaround is to add -Djavax.xml.bind.JAXBContext=com.sun.xml.internal.bind.v2.ContextFactory
to the command line, which causes JAXBContext.newInstance() to ignore the /META-INF/services/javax.xml.bind.JAXBContext
file on the classpath and manually specifies the built-in version of JAXB. The other workaround is to simply not specify your own JAXB and use the version built into the JDK, but it seems from the Unofficial JAXB Guide, that Sun designed this system to be able to handle supplying your own JAXB implementation. Has anyone been able to successfully supply a version of JAXB and still be able to successfully capture fault messages? (Everything runs fine for me as long as there are no faults generated by the Web Service).
I was stuck on this problem but was able to use the "work around" listed in this forum Q&A by setting a system property like so:
System.setProperty("javax.xml.bind.JAXBContext",
"com.sun.xml.internal.bind.v2.ContextFactory");