miércoles, 31 de agosto de 2011

"package javax.xml.bind.annotation does not exist" using CXF's DynamicClientFactory in OC4J 10.1.3.x

I'm using CXF 2.4.2 and DynamicClientFactory in OC4J 10.1.3.4 (and 10.1.3.5).

When DynamicClientFactory tries to compile the dinamycally generated Java sources, the following errors occur:

INFO: Created classes: XXX, YYY, ...
 C:\..\org.apache.cxf.endpoint.dynamic.DynamicClientFactory@12f767-1314800629516-src\...\XXX.java:4: package javax.xml.bind.annotation does not exist
 import javax.xml.bind.annotation.XmlAccessType;
                                  ^
 C:\..\org.apache.cxf.endpoint.dynamic.DynamicClientFactory@12f767-1314800629516-src\...\YYY.java:4: package javax.xml.bind.annotation does not exist
 import javax.xml.bind.annotation.XmlAccessType;
                                  ^
I dug into the DynamicClientFactory code and found that setupClasspath, the method which sets the Classpath for the javac compilation iterates between the supplied ClassLoader (or the Context ClassLoader if none is supplied) and the SystemClassLoader extrating the ClassPath paths for each one; this extraction is done only if the ClassLoader in turn is an instance of URLClassLoader. In OC4J, the ClassLoaders are usually instances of oracle.classloader.PolicyClassLoader (which is not an instance of URLClassLoader). For this reason, the Classpath for the compilation is not correctly set.

There was a reported bug (CXF-2549) which gives a solution for Weblogic. I didn't find a good similar solution for OC4J to send a patch.

I found the necessary compilation libraries are the JAXB ones. What I did, before calling createClient, was to set a new URLClassLoader which include the JAXB jars:

URL[] jaxbUrls = null;

jaxbUrls = new URL[]{
  new URL("file:/C:/.../jaxb-api-2.2.1.jar"),
  new URL("file:/C:/.../jaxb-impl-2.2.1.1.jar"),
  new URL("file:/C:/.../jaxb-xjc-2.2.1.1.jar")
};

ClassLoader currentCL = Thread.currentThread().getContextClassLoader();

URLClassLoader urlCL = new URLClassLoader(jaxbUrls, currentCL);

client = dcf.createClient(xxx, yyy, urlCL, zzz);

The compilation is now succesful.

DynamicClientFactory internally creates another URLClassLoader which includes the newly generated classes and set it as the current thread's Context Class Loader; for this reason, when working with DynamicClientFactory in a multithreaded environment, there are some gotchas relating the ClassLoaders, but I thing this is material for another post.

EDIT: I found a better way to initialize the JAXB jars URL's array:

import oracle.classloader.PolicyClassLoader;
import oracle.classloader.SharedCodeSource;

Class someJaxbClazz = Class.forName("javax.xml.bind.annotation.XmlAccessType");
PolicyClassLoader jaxbClazzClassLoader = (PolicyClassLoader)someJaxbClazz.getClassLoader();
SharedCodeSource[] sharedCodeSources = jaxbClazzClassLoader.getCodeSources(false);

for(SharedCodeSource sharedCodeSource : sharedCodeSources) {
    URL location = sharedCodeSource.getLocation();
    if(location.toString().toLowerCase().contains("jaxb")) {
        System.out.println("JAXB jar URL found->" + location);
    }
}

In my case, it printed somethig like:

JAXB jar URL found->file:/C:/.../cxf/2.4.2/jaxb-api-2.2.1.jar
JAXB jar URL found->file:/C:/.../cxf/2.4.2/jaxb-impl-2.2.1.1.jar
JAXB jar URL found->file:/C:/.../cxf/2.4.2/jaxb-xjc-2.2.1.1.jar

:-)





No hay comentarios: