Showing posts with label OSGi. Show all posts
Showing posts with label OSGi. Show all posts

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.

Tuesday, September 21, 2010

JavaOne 2010: OSGi Migration Headaches

Krasimir Semerdzhiev and Peter Peshev presented "OSGi Migration Headaches" at JavaOne 2010. The speakers took turns presenting and started with an introduction regarding OSGi, what it is, and why it is useful. This is one of those presentations that was useful to hear, but will be even more useful as a reference when working with OSGi. The slides contained several common problems encountered when introducing OSGi into a Java application.  For each problem, common issues or causes of that problematic symptom are listed.  It's a great format for a reference. Many of the issues and problem causes the speakers identified are
not specific to OSGi and so their discussion of those issues is generally useful to Java developers.

The speakers recommended Eclipse Plugin Development Environment (PDE) for creating bundle manifests.  This is easier and less error prone than hand-writing these bundle manifests.

Upon first bundle activation, one is likely to see java.lang.ClassNotFoundException. They listed possible causes of this (missing import-package, missing export-package, import version mismatch, forgotten/missing component, wrong boot class delegation property, file permissions problems). There is a potential problem of specifying versions incorrectly because of subtle differences in version specification syntax.

The java.lang.NoClassDefFoundError means class is not found but cannot get instance of. Possible causes include exception in static block, missing imported class, class version mismatch, HotSpot reflection JIT optimizations (BugID 6265952 related to 15 method calls).

The java.lang.ClassCastException can be caused by additional copies of the OSGi framework classes, undesirable additional JARs on the classpath, and conflict/clash between RT.jar and application libs.

The speakers discussed possible issues underyling the situation when it works in Eclipse but not in Equinox. See also the article OSGi with Eclipse Equinox - Tutorial.

The speakers talked about dependencies on startup order. They also discussed classloader resource assumptions and possible issues: JAR files, folders with class files/packages, URLs to JARs or classes, or new File call after loader resources iteration. Boot delegation specifics got its own slide: com.foo.* includes all sub-packages but not com.foo package itself.

Speakers recommended using Eclipse Memory Analyzer when trying to determine cause of conflicts, errors, and exceptions when using OSGi.

This presentation is one of those which would definitely be helpful to have on hand (hard copy of soft copy) as a reference when using OSGi. It would nice to be able to look up the common causes of these various identified issues by symptom.