I’m attempting to utilize the Apache CXF library to create a dynamic client that will read a WSDL and parse it into Java objects for processing.
The application into which this logic is being implemented has been built using Spring Boot 2.7 and JDK11. The JAR artifact runs on the embedded Tomcat container provided with Spring Boot.
JDK11 has removed the JAXB libraries that are necessary to create the objects, but they are being indirectly added from other dependencies specified in the Gradle file (jakarta.xml.bind-api-2.3.3.jar, jaxb-impl-2.3.0.1.jar, and jaxb-core-2.3.0.1.jar).
I’m creating the dynamic client as follows.
this.wsdlClient = dcf.createClient(this.serviceURL + "?wsdl");
When I run the client, the application isn’t able to compile the source files created from the WSDL and an exception is thrown.
javax.xml.bind.JAXBException: "report.balance" doesnt contain ObjectFactory.class or jaxb.index
I assumed that the JAXB libraries in my application’s JAR file are not on the classpath when CXF attempts to compile the source files. Therefore, I tried creating the dynamic client and specifying a new classloader with the required JAR files located in a folder in the directory where the application is running.
this.wsdlClient = dcf.createClient(this.serviceURL + "?wsdl", new URLClassLoader(new URL[] { new File("applibs\jakarta.xml.bind-api-2.3.3.jar").toURI().toURL(), new File("applibs\jaxb-impl-2.3.0.1.jar").toURI().toURL(), new File("applibs\jaxb-core-2.3.0.1.jar").toURI().toURL(), new File("applibs\javax.activation-api-1.2.0.jar").toURI().toURL() }));
The good news is that the source files created from the WSDL are able to be successfully compiled!
Unfortunately, a ClassCastException is thrown when the dynamic client checks if the object instance is of type JAXBContext.
javax.xml.bind.JAXBException: Provider class com.sun.xml.bind.v2.ContextFactory could not be instantiated: javax.xml.bind.JAXBException: ClassCastException: attempting to cast jar:file:/C:/app/applibs/jakarta.xml.bind-api-2.3.3.jar!/javax/xml/bind/JAXBContext.class to jar:file:/C:/app/build/libs/app.jar!/BOOT-INF/lib/jakarta.xml.bind-api-2.3.3.jar!/javax/xml/bind/JAXBContext.class. Please make sure that you are specifying the proper ClassLoader.
Despite the fact that the JAXBContext class is the same class, I think the presence of two of the jakarta.xml.bind-api JAR files (one in the application JAR and one in the applibs folder) is causing a conflict, but I’m unsure why since I’m specifying an entirely new classloader to be used by CXF to compile the Java source code generated from the WSDL.
I cannot exclude the jakarta.xml.bind-api-2.3.3.jar from the application JAR file as it is needed by other dependencies.
Is there something that I’m missing in my approach?
Advertisement
Answer
For anyone that comes across this question in the future, here’s the solution.
For the creation of the dynamic client, include the Jakarta XML Bind API JAR in a separate classloader passed to the constructor.
this.wsdlClient = dcf.createClient(this.serviceURL + "?wsdl", new URLClassLoader(new URL[] { new File("applibs\jakarta.xml.bind-api-2.3.3.jar").toURI().toURL() }));
In the JAR file, ensure that version 2.3.3 of the JAXB Runtime JAR is included in your application JAR file. The Gradle code for adding this dependency is shown below.
implementation group: 'org.glassfish.jaxb', name: 'jaxb-runtime', version: '2.3.3'
Version 2.3.6 of the jaxb-runtime JAR was originally included in the application JAR file, and this caused the issue described above.