View Javadoc
1   package com.atlassian.plugin.osgi.util;
2   
3   import com.atlassian.plugin.util.resource.AlternativeResourceLoader;
4   import com.atlassian.plugin.util.resource.NoOpAlternativeResourceLoader;
5   import org.osgi.framework.Bundle;
6   
7   import java.io.IOException;
8   import java.net.URL;
9   import java.util.Collections;
10  import java.util.Enumeration;
11  
12  import static com.google.common.base.Preconditions.checkNotNull;
13  import static com.google.common.collect.Iterators.asEnumeration;
14  
15  /**
16   * Utility methods for accessing a bundle as if it was a classloader.
17   *
18   * @since 2.3.0
19   */
20  public class BundleClassLoaderAccessor {
21      /**
22       * Creates a classloader that delegates to the bundle
23       *
24       * @param bundle                    The bundle to delegate to
25       * @param alternativeResourceLoader An alternative resource loader to bypass bundle, can be null
26       * @return A new classloader instance
27       */
28      public static ClassLoader getClassLoader(final Bundle bundle, final AlternativeResourceLoader alternativeResourceLoader) {
29          return new BundleClassLoader(bundle, alternativeResourceLoader);
30      }
31  
32      /**
33       * Loads a class from the bundle
34       *
35       * @param bundle The bundle
36       * @param name   The name of the class to load
37       * @param <T>    The type of the class
38       * @return The class instance
39       * @throws ClassNotFoundException If the class cannot be found in the bundle
40       */
41      public static <T> Class<T> loadClass(final Bundle bundle, final String name) throws ClassNotFoundException {
42          @SuppressWarnings({"unchecked", "UnnecessaryLocalVariable"})
43          final Class<T> loadedClass = (Class<T>) checkNotNull(bundle, "The bundle is required").loadClass(name);
44          return loadedClass;
45      }
46  
47      ///CLOVER:OFF
48  
49      /**
50       * Fake classloader that delegates to a bundle
51       */
52      private static class BundleClassLoader extends ClassLoader {
53          private final Bundle bundle;
54          private final AlternativeResourceLoader altResourceLoader;
55  
56          public BundleClassLoader(final Bundle bundle, AlternativeResourceLoader altResourceLoader) {
57              super(null);
58              this.bundle = checkNotNull(bundle, "The bundle must not be null");
59              this.altResourceLoader = altResourceLoader == null ? new NoOpAlternativeResourceLoader() : altResourceLoader;
60          }
61  
62          @Override
63          public Class<?> findClass(final String name) throws ClassNotFoundException {
64              return bundle.loadClass(name);
65          }
66  
67          @SuppressWarnings("unchecked")
68          @Override
69          public Enumeration<URL> findResources(final String name) throws IOException {
70              Enumeration<URL> e = bundle.getResources(name);
71  
72              if (e == null) {
73                  e = asEnumeration(Collections.<URL>emptyList().iterator());
74              } else {
75                  // For some reason, getResources() sometimes returns nothing, yet getResource() will return one. This code
76                  // handles that strange case
77                  if (!e.hasMoreElements()) {
78                      final URL resource = findResource(name);
79                      if (resource != null) {
80                          e = asEnumeration(Collections.singleton(resource).iterator());
81                      }
82                  }
83              }
84              return e;
85          }
86  
87          @Override
88          public URL findResource(final String name) {
89              URL url = altResourceLoader.getResource(name);
90              if (url == null) {
91                  url = bundle.getResource(name);
92              }
93              return url;
94          }
95  
96          @Override
97          public String toString() {
98              final String sym = bundle.getSymbolicName();
99              return "BundleClassLoader@" + Integer.toHexString(System.identityHashCode(this)) + "[bundle=" +
100                     ((sym != null) ? sym : "") + " [" + bundle.getBundleId() + "]]";
101         }
102     }
103 
104 }