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