Entries categorized as ‘dfs’
Sometimes you want to invoke DFS services from within a Java Method Server method implementation.
First you should understand how to implement a method as a BOF module.
What remains is to package your module correctly for DFS service invocation. In order to do this we suggest the following:
- Create a sandboxed Java library containing the minimum classpath for your chosen DFS client, as described in the DFS SDK doc. This library can be included in all method modules that invoke DFS services.
- Create a sandboxed Java library containing your generated client classes for each service that you consume. Each library can be included in all method modules that invoke the corresponding DFS service. If you only have one or two methods then these classes could be folded into your implementation jar instead.
- Create an implementation jar for your method implementation.
Be sure that your method implementation observes the correct thread context class loader handling and you should be good to go. Here’s a little base class that takes care of that for you (note that this class must be in either your implementation jar or a sandboxed java library or it just won’t work).
package com.emc.examples;
import com.documentum.fc.client.DfSingleDocbaseModule;
import java.util.Map;
import java.io.PrintWriter;
public abstract class AbstractMethod extends DfSingleDocbaseModule implements IDfMethod
{
public final int execute(Map parameters, PrintWriter writer) throws Exception {
ClassLoader tccl = Thread.currentThread().getContextClassLoader();
try {
ClassLoader moduleLoader = AbstractMethod.class.getClassLoader();
Thread.currentThread().setContextClassLoader(moduleLoader);
return doMethod(parameters, writer);
}
finally {
Thread.currentThread().setContextClassLoader(tccl);
}
}
public abstract int doMethod(Map parameters, PrintWriter writer) throws Exception;
}
Categories: dfs · methodserver
November 25, 2008 · 1 Comment
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.
Categories: bof · dfs