Tuesday, August 9, 2016

Remembering to Reset Thread Context Class Loader

I'm having a difficult time thinking of anything I like less about working with Java than working with class loaders. This is particularly true when working with application servers or OSGi where the use of multiple class loaders is prevalent and the ability to use class loaders transparently is reduced. I agree with the OSGI Alliance Blog post What You Should Know about Class Loaders that "in a modular environment, class loader code wreaks havoc."

Neil Bartlett has written the blog post The Dreaded Thread Context Class Loader in which he describes why the thread context class loader was introduced and why its use is not "OSGi-friendly." Bartlett states that there are rare cases in which "a library only consults the TCCL," but that in those rare cases "we are somewhat stuck" and "will have to explicitly set the TCCL from our own code before calling into the library."

Alex Miller has also written about the Thread Context Class Loader (TCCL) and points out that "Java frameworks do not follow consistent patterns for classloading" and that "many common and important frameworks DO use the thread context classloader (JMX, JAXP, JNDI, etc)." He emphasizes this, " If you are using a J2EE application server, you are almost certainly relying on code using the thread context classloader." In that post, Miller presents a dynamic proxy-based solution for helping in cases where one needs to "set the thread context classloader" and then "remember the original context classloader and re-set it."

The Knopflerfish Framework, an OSGi implementation, describes how to use the Thread Context Class Loader in the "Programming" section of its documentation. The following quote is excerpted from the "Setting the context classloader" section of Knopflerfish 5.2's "Programming" documentation:

Many external libraries, like most JNDI lookup services requires a correctly set thread context classloader. If this is not set, ClassNotFoundException, or similar might be thrown even if you have included all necessary libs. To fix this, simple spawn a new thread in the activator and do the work from that thread. ... It is not recommended to set the context class loader persistently on the startup thread, since that thread might not be unique for your bundle. Effects might vary depending on OSGi vendor. If you don't spawn a new thread, you must reset the context class loader before returning.

Knopflerish provides a simple class, org.knopflerfish.util.ClassLoaderUtil, that supports switching to a provided class loader (probably would often be the thread context class loader in an OSGi application) and ensures via finally clause that the original context class loader is reset after the operation is completed. I have implemented my own adaptation of that class that is shown in the next code listing.

ClassLoaderSwitcher.java

package dustin.examples.classloader;

/**
 * Utility class for running operations on an explicitly specified class loader.
 */
public class ClassLoaderSwitcher
{
   /**
    * Execute the specified action on the provided class loader.
    *
    * @param classLoaderToSwitchTo Class loader from which the
    *    provided action should be executed.
    * @param actionToPerformOnProvidedClassLoader Action to be
    *    performed on the provided class loader.
    * @param <T> Type of Object returned by specified action method.
    * @return Object returned by the specified action method.
    */
   public static <T> T executeActionOnSpecifiedClassLoader(
      final ClassLoader classLoaderToSwitchTo,
      final ExecutableAction<T> actionToPerformOnProvidedClassLoader)
   {
      final ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
      try
      {
         Thread.currentThread().setContextClassLoader(classLoaderToSwitchTo);
         return actionToPerformOnProvidedClassLoader.run();
      }
      finally
      {
         Thread.currentThread().setContextClassLoader(originalClassLoader);
      }
   }

   /**
    * Execute the specified action on the provided class loader.
    *
    * @param classLoaderToSwitchTo Class loader from which the
    *    provided action should be executed.
    * @param actionToPerformOnProvidedClassLoader Action to be
    *    performed on the provided class loader.
    * @param <T> Type of Object returned by specified action method.
    * @return Object returned by the specified action method.
    * @throws Exception Exception that might be thrown by the
    *    specified action.
    */
   public static <T> T executeActionOnSpecifiedClassLoader(
      final ClassLoader classLoaderToSwitchTo,
      final ExecutableExceptionableAction<T> actionToPerformOnProvidedClassLoader) throws Exception
   {
      final ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
      try
      {
         Thread.currentThread().setContextClassLoader(classLoaderToSwitchTo);
         return actionToPerformOnProvidedClassLoader.run();
      }
      finally
      {
         Thread.currentThread().setContextClassLoader(originalClassLoader);
      }
   }
}

The two methods defined on the ClassLoaderSwitcher class each take an interface as one of their parameters along with a specified class loader. The interfaces prescribe an object with a run() method and that run() method will be executed against the provided class loader. The next two code listings show the interfaces ExecutableAction and ExecutableExceptionableAction.

ExecutableAction.java

package dustin.examples.classloader;

/**
 * Encapsulates action to be executed.
 */
public interface ExecutableAction<T>
{
   /**
    * Execute the operation.
    *
    * @return Optional value returned by this operation;
    *    implementations should document what, if anything,
    *    is returned by implementations of this method.
    */
   T run();
}

ExecutableExceptionableAction.java

package dustin.examples.classloader;

/**
 * Describes action to be executed that is declared
 * to throw a checked exception.
 */
public interface ExecutableExceptionableAction<T>
{
   /**
    * Execute the operation.
    *
    * @return Optional value returned by this operation;
    *    implementations should document what, if anything,
    *    is returned by implementations of this method.
    * @throws Exception that might be possibly thrown by this
    *    operation.
    */
   T run() throws Exception;
}

Clients calling the methods defined on the ClassLoaderSwitcher class won't necessarily have fewer lines of code than they'd have doing the temporary context class loader switching themselves, but using a common class such as this one ensures that the context class loader is always changed back to the original class loader and thus removes the need for the developer to ensure the reset is available and prevents the "reset" from being inadvertently removed at some point or moved too late in the process at some point.

A client that needs to temporarily change the context class loader for an operation might do so as shown next:

Temporarily Switching ClassLoader Directly to Execute Action

final ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
try
{
   Thread.currentThread().setContextClassLoader(BundleActivator.class.getClassLoader());
   final String returnedClassLoaderString =
      String.valueOf(Thread.currentThread().getContextClassLoader())
}
finally
{
   Thread.currentThread().setContextClassLoader(originalClassLoader);
}

There are not that many lines of code, but one has to remember to reset the context class loader to its original class loader. Using the ClassLoaderSwitcher utility class to do the same thing is demonstrated next.

Using ClassLoaderSwitcher to Switch Class Loader to Execute Action (pre-JDK 8)

final String returnedClassLoaderString = ClassLoaderSwitcher.executeActionOnSpecifiedClassLoader(
   BundleActivator.class.getClassLoader(),
   new ExecutableAction<String>()
   {
      @Override
      public String run()
      {
         return String.valueOf(Thread.currentThread().getContextClassLoader());
      }
   });

This last example wasn't shorter than the first, but the developer did not need to worry about resetting the context class loader explicitly in the second example. Note that these two examples reference BundleActivator to get an Activator/System class loader in an OSGi application. This is what I used here, but any class that was loaded on the appropriate class loader could be used here instead of BundleActivator. Another thing to note is that my examples use a very simple operation executed on the specified classloader (returning a String representation of the current thread context class loader) that works well here because it makes it easy for me to see that the specified class loader was used. In realistic scenarios, this method could be anything one needed to run on the specified class loader.

If the method I'm invoking on the specified class loader throws a checked exception, I can use the other overloaded method (of the same name) provided by ClassLoaderSwitcher to run that method. This is demonstrated in the next code listing.

Use of ClassLoaderSwitcher with Method that Might Throw Checked Exception (pre-JDK 8)

String returnedClassLoaderString = null;
try
{
   returnedClassLoaderString = ClassLoaderSwitcher.executeActionOnSpecifiedClassLoader(
      BundleActivator.class.getClassLoader(),
      new ExecutableExceptionableAction<String>()
      {
         @Override
         public String run() throws Exception
         {
            return mightThrowException();
         }
      });
}
catch (Exception exception)
{
   System.out.println("Exception thrown while trying to run action.");
}

With JDK 8, we can make the client code more concise. The next two code listings contain methods corresponding to the methods shown in the previous two code listings, but changed to JDK 8 style.

Using ClassLoaderSwitcher to Switch Class Loader to Execute Action (JDK 8 Style)

final String returnedClassLoaderString = ClassLoaderSwitcher.executeActionOnSpecifiedClassLoader(
   urlClassLoader,
   (ExecutableAction<String>) () ->
   {
      return String.valueOf(Thread.currentThread().getContextClassLoader());
   });

Use of ClassLoaderSwitcher with Method that Might Throw Checked Exception (JDK 8 Style)

String returnedClassLoaderString = null;
try
{
   returnedClassLoaderString = ClassLoaderSwitcher.executeActionOnSpecifiedClassLoader(
      urlClassLoader,
      (ExecutableExceptionableAction<String>) () -> {
         return mightThrowException();
      });
}
catch (Exception exception)
{
   System.out.println("Exception thrown while trying to run action.");
}

The lambda expressions of JDK 8 make the client code using ClassLoaderSwitcher more concise (and arguably more readable) than directly setting and resetting the context class loader and at the same time provide greater safety by ensuring that the context class loader is always switched back to its original class loader.

Conclusion

Although it's undoubtedly best to avoid switching the context class loader as much as possible, there may be times when you have no other reasonable choice. In those times, encapsulating the multiple steps involved in the switch and switch back into a single method that can be called by clients adds safety to the operation and can even allow the client to have more concise code if written in JDK 8.

Additional References

Some of these references have already been mentioned and even highlighted in this post, but I include them again here for convenience.

No comments: