View Javadoc

1   package com.atlassian.plugin.util;
2   
3   import java.util.LinkedList;
4   import java.util.List;
5   
6   /**
7    * This utility provides a thread local stack of {@link ClassLoader}s.
8    * The current "top" of the stack is the thread's current context class loader.
9    * This can be used when implementing delegating plugin {@link java.util.logging.Filter}s or {@link javax.servlet.Servlet}s
10   * that need to set the {@link ClassLoader} to the {@link com.atlassian.plugin.classloader.PluginClassLoader} the filter
11   * or servlet is declared in.
12   *
13   * @since 2.5.0
14   */
15  public class ClassLoaderStack
16  {
17      private static final ThreadLocal<List<ClassLoader>> classLoaderStack = new ThreadLocal<List<ClassLoader>>()
18      {
19          protected List<ClassLoader> initialValue()
20          {
21              return new LinkedList<ClassLoader>();
22          }
23      };
24  
25      /**
26       * Makes the given classLoader the new ContextClassLoader for this thread, and pushes the current ContextClassLoader
27       * onto a ThreadLocal stack so that we can do a {@link #pop} operation later to return to that ContextClassLoader.
28       *
29       * <p>
30       * Passing null is allowed and will act as a no-op. This means that you can safely {@link #pop} a ClassLoader and {@link #push} it back in
31       * and it will work safely whether the stack was empty at time of {@link #pop} or not.
32       *
33       * @param loader The new ClassLoader to set as ContextClassLoader.
34       */
35      public static void push(ClassLoader loader)
36      {
37          if (loader == null)
38          {
39              return;
40          }
41  
42          classLoaderStack.get().add(0, Thread.currentThread().getContextClassLoader());
43          Thread.currentThread().setContextClassLoader(loader);
44      }
45  
46      /**
47       * Pops the current ContextClassLoader off the stack, setting the new ContextClassLoader to the previous one on the stack.
48       * <ul>
49       * <li>If the stack is not empty, then the current ClassLoader is replaced by the previous one on the stack, and then returned.</li>
50       * <li>If the stack is empty, then null is returned and the current ContextClassLoader is not changed.</li>
51       * </ul>
52       *
53       * @return the previous ContextClassLoader that was just replaced, or null if the stack is empty.
54       */
55      public static ClassLoader pop()
56      {
57          if (classLoaderStack.get().isEmpty())
58          {
59              return null;
60          }
61          ClassLoader currentClassLoader = Thread.currentThread().getContextClassLoader();
62          Thread.currentThread().setContextClassLoader(classLoaderStack.get().remove(0));
63          return currentClassLoader;
64      }
65  }