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   
6   import org.apache.commons.collections.iterators.IteratorEnumeration;
7   import org.apache.commons.lang.Validate;
8   import org.osgi.framework.Bundle;
9   
10  import java.io.IOException;
11  import java.net.URL;
12  import java.util.Arrays;
13  import java.util.Enumeration;
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      /**
23       * Creates a classloader that delegates to the bundle
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      {
30          return new BundleClassLoader(bundle, alternativeResourceLoader);
31      }
32  
33      /**
34       * Loads a class from the bundle
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      {
43          Validate.notNull(bundle, "The bundle is required");
44          @SuppressWarnings ({ "unchecked", "UnnecessaryLocalVariable" })
45          final Class<T> loadedClass = bundle.loadClass(name);
46          return loadedClass;
47      }
48  
49      ///CLOVER:OFF
50      /**
51       * Fake classloader that delegates to a bundle
52       */
53      private static class BundleClassLoader extends ClassLoader
54      {
55          private final Bundle bundle;
56          private final AlternativeResourceLoader altResourceLoader;
57  
58          public BundleClassLoader(final Bundle bundle, AlternativeResourceLoader altResourceLoader)
59          {
60              // TODO: PLUG-433 Is this ClassLoader supposed to be able to load system classes?
61              // If not, force that there is no parent classloader with super(null);
62              Validate.notNull(bundle, "The bundle must not be null");
63              if (altResourceLoader == null)
64              {
65                  altResourceLoader = new NoOpAlternativeResourceLoader();
66              }
67              this.altResourceLoader = altResourceLoader;
68              this.bundle = bundle;
69  
70          }
71  
72          @Override
73          public Class<?> findClass(final String name) throws ClassNotFoundException
74          {
75              return bundle.loadClass(name);
76          }
77  
78          @SuppressWarnings("unchecked")
79          @Override
80          public Enumeration<URL> findResources(final String name) throws IOException
81          {
82              Enumeration<URL> e = bundle.getResources(name);
83  
84              // For some reason, getResources() sometimes returns nothing, yet getResource() will return one.  This code
85              // handles that strange case
86              if (!e.hasMoreElements())
87              {
88                  final URL resource = findResource(name);
89                  if (resource != null)
90                  {
91                      e = new IteratorEnumeration(Arrays.asList(resource).iterator());
92                  }
93              }
94              return e;
95          }
96  
97          @Override
98          public URL findResource(final String name)
99          {
100             URL url = altResourceLoader.getResource(name);
101             if (url == null)
102             {
103                 url = bundle.getResource(name);
104             }
105             return url;
106         }
107     }
108 
109 }