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 }