Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Supplying a different version of JAXB for JAX-WS in Java 1.6

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).

like image 556
matt forsythe Avatar asked Jan 04 '13 17:01

matt forsythe


People also ask

What can I use instead of JAXB?

XOM, JDOM, dom4j, etc. etc. Projects like Castor and Apache XMLBeans predate JAXB, so you could have a look at those. Ulf Dittmer wrote: XOM, JDOM, dom4j, etc.

Is JAXB deprecated?

In Java SE 9 and 10, the module java.se.ee, which contains JAX-WS, JAXB and a few other Java EE APIs, is still included but is deprecated, and not included in the module path by default. You need to explicitly enable it if you want to use it.

How do I know what version of JAX-WS I have?

You can learn which version of jax-ws by means wsgen or wsimport. You can find them bin subdirectory of your jdk installation.

Is JAXB part of Java?

1.2. As of Java 11, JAXB is not part of the JRE anymore and you need to configure the relevant libraries via your dependency management system, for example Maven or Gradle. This tutorial demonstrates both approaches. The JAXB reference implementation is developed on Github Jaxb api project page.


1 Answers

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");  
like image 79
Frank Avatar answered Sep 19 '22 22:09

Frank