Typically you call DFS from within your web app and everything works as expected. Then you diligently package your DFS classes within a BOF module and try to call the same DFS service from within the module and you get javax.xml.bind.JAXBException. What happened?
Let’s look at the “calling from web app” scenario first. DFS uses JAXB to convert between Java objects and XML. In order to do this JAXB needs to dynamically load classes. Now the JAXB implementation is in the extension class loader (only one level above the bootstrap loader) and it can’t see upwards in the class loader hierarchy so it can never see the application classes that it needs to load. So how does it get to see the classes? It can’t use its own class loader for the dynamic load operation because it has no visibility. It can’t use the web app class loader directly, because it can’t find it and doesn’t even know that it’s the right class loader anyway – how would it? So how does it work?
Each thread has a thread local reference to a class loader called the thread context class loader (TCCL). This can be set and recovered programmatically. In a web app scenario this will have been set by the app server to the web app class loader (WEB-INF/lib). So JAXB (and most other Java runtime factories) use the TCCL for dynamic class load operations in order to find the application provided classes.
In the web app scenario this works just fine because the TCCL is set to the class loader that has visibility to the application classes. Within a BOF module, however, it doesn’t work. Why? Because the TCCL is still set to the web app class loader, but the classes are only visible to the module class loader which is not visible to the web app class loader (see BOF class loaders). So you get javax.xml.bind.JAXBException.
How do you fix it? Fortunately this is simple – you just set the TCCL to your module class loader around your DFS calls. Be sure to bracket this in try/finally logic to ensure the TCCL is appropriately reset.
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
try {
ClassLoader cl = getClass().getClassLoader();
Thread.currentThread().setContextClassLoader(cl);
// .. do DFS stuff
} finally {
Thread.currentThread().setContextClassLoader(tccl);
}
That’s it.
1 response so far ↓
Writing Java server methods that invoke DFS services « Say Scheveningen // May 20, 2009 at 5:02 pm
[...] Be sure that your method implementation observesthe correct thread context class loader handling. [...]