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

44,901

Solution 1

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"); 

Solution 2

The crux of this issue is there is a change in the JAXB API, the runtime implementation you are attempting to use does not match the version of the JAXB API bundled with the JDK.

In order to use a different version, you should copy coresponding versions of jaxb-api.jar and jaxws-api.jar into an endorsed lib (e.g. %JAVA_HOME%\lib\endorsed).

A complete list of options is given in section 7.1.2 of the Unofficial JAXB Guide

It is a mistake to copy the implementation jars (e.g. jaxb-impl.jar) into endorsed lib, these should simply be on your classpath.

Also note that you can get into trouble if you attempt to use a newer version of jaxb without also including a compatible version of jaxws. This is because the old jaxws attempts to reference the old jaxb, so if you're changing one make sure you do both. (Stack-trace in package com.sun.xml.internal.ws implicates an old jax-ws implementation. Even the latest release of Java still ships with old version 1 jaxb and jaxws apis).

Solution 3

another possible solution without modifying the system properties is described at API docu

https://docs.oracle.com/cd/E17802_01/webservices/webservices/docs/1.6/api/javax/xml/bind/JAXBContext.html

you can place a jaxb.properties file inside of the package of your model class.

javax.xml.bind.context.factory=com.sun.xml.internal.bind.v2.ContextFactory
Share:
44,901
matt forsythe
Author by

matt forsythe

Updated on December 22, 2020

Comments

  • matt forsythe
    matt forsythe over 3 years

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