View Javadoc

1   package com.atlassian.plugin.classloader;
2   
3   import com.atlassian.plugin.Plugin;
4   import com.atlassian.plugin.PluginAccessor;
5   import org.apache.commons.logging.Log;
6   import org.apache.commons.logging.LogFactory;
7   
8   import java.net.URL;
9   import java.util.Collection;
10  import java.util.Iterator;
11  import java.util.Map;
12  import java.util.HashMap;
13  import java.util.Set;
14  import java.util.HashSet;
15  
16  /**
17   *
18   */
19  public class PluginsClassLoader extends AbstractClassLoader
20  {
21      private static final Log log = LogFactory.getLog(PluginsClassLoader.class);
22      private final PluginAccessor pluginAccessor;
23  
24      private final Map/*<String,Plugin>*/ pluginResourceIndex = new HashMap();
25      private final Map/*<String,Plugin>*/ pluginClassIndex = new HashMap();
26  
27      private final Set/*<String>*/ missedPluginResource = new HashSet();
28      private final Set/*<String>*/ missedPluginClass = new HashSet();
29  
30      public PluginsClassLoader(PluginAccessor pluginAccessor)
31      {
32          this(null,pluginAccessor);
33      }
34  
35      public PluginsClassLoader(ClassLoader parent, PluginAccessor pluginAccessor)
36      {
37          super(parent);
38          if (pluginAccessor == null)
39          {
40              throw new IllegalArgumentException("The plugin accessor should not be null.");
41          }
42          this.pluginAccessor = pluginAccessor;
43      }
44  
45      protected URL findResource(final String name)
46      {
47          final Plugin indexedPlugin;
48          synchronized (this)
49          {
50               indexedPlugin = (Plugin) pluginResourceIndex.get(name);
51          }
52          final URL result;
53          if (isPluginEnabled(indexedPlugin))
54          {
55              result = indexedPlugin.getClassLoader().getResource(name);
56          }
57          else
58          {
59              result = getResourceFromPlugins(name);
60          }
61          if (log.isDebugEnabled())
62          {
63              log.debug("Find resource [ " + name + " ], found [ " + result + " ]");
64          }
65          return result;
66      }
67  
68      protected Class findClass(String className) throws ClassNotFoundException
69      {
70          final Plugin indexedPlugin;
71          synchronized (this)
72          {
73              indexedPlugin = (Plugin) pluginClassIndex.get(className);
74          }
75  
76          final Class result;
77          if (isPluginEnabled(indexedPlugin))
78          {
79              result = indexedPlugin.getClassLoader().loadClass(className);
80          }
81          else
82          {
83              result = loadClassFromPlugins(className);
84          }
85          if (log.isDebugEnabled())
86          {
87              log.debug("Find class [ " + className + " ], found [ " + result + " ]");
88          }
89          if (result != null)
90          {
91              return result;
92          }
93          else
94          {
95              throw new ClassNotFoundException(className);
96          }
97      }
98  
99      private Class loadClassFromPlugins(String className)
100     {
101         final boolean isMissedClassName;
102         synchronized (this)
103         {
104             isMissedClassName = missedPluginClass.contains(className);
105         }
106         if (isMissedClassName)
107         {
108             return null;
109         }
110         final Collection plugins = pluginAccessor.getEnabledPlugins();
111         for (Iterator i = plugins.iterator(); i.hasNext();)
112         {
113             final Plugin plugin = (Plugin) i.next();
114             try
115             {
116                 Class result = plugin.getClassLoader().loadClass(className);
117                 //loadClass should never return null
118                 synchronized (this)
119                 {
120                     pluginClassIndex.put(className, plugin);
121                 }
122                 return result;
123             }
124             catch (ClassNotFoundException e)
125             {
126                 // continue searching the other plugins
127             }
128         }
129         synchronized (this)
130         {
131             missedPluginClass.add(className);
132         }
133         return null;
134     }
135 
136     private URL getResourceFromPlugins(String name)
137     {
138         final boolean isMissedResource;
139         synchronized (this)
140         {
141             isMissedResource = missedPluginResource.contains(name);
142         }
143         if (isMissedResource)
144         {
145             return null;
146         }
147         final Collection plugins = pluginAccessor.getEnabledPlugins();
148         for (Iterator i = plugins.iterator(); i.hasNext();)
149         {
150             final Plugin plugin = (Plugin) i.next();
151             final URL resource = plugin.getClassLoader().getResource(name);
152             if (resource != null)
153             {
154                 synchronized (this)
155                 {
156                     pluginResourceIndex.put(name, plugin);
157                 }
158                 return resource;
159             }
160         }
161         synchronized (this)
162         {
163             missedPluginResource.add(name);
164         }
165         return null;
166     }
167 
168     private boolean isPluginEnabled(Plugin plugin)
169     {
170         return plugin != null && pluginAccessor.isPluginEnabled(plugin.getKey());
171     }
172 
173     public synchronized void notifyUninstallPlugin(Plugin plugin)
174     {
175         flushMissesCaches();
176         for (Iterator it = pluginResourceIndex.entrySet().iterator(); it.hasNext();)
177         {
178             final Map.Entry resourceEntry = (Map.Entry) it.next();
179             final Plugin pluginForResource = (Plugin) resourceEntry.getValue();
180             if (plugin.getKey().equals(pluginForResource.getKey()))
181             {
182                 it.remove();
183             }
184         }
185         for (Iterator it = pluginClassIndex.entrySet().iterator(); it.hasNext();)
186         {
187             final Map.Entry pluginClassEntry = (Map.Entry) it.next();
188             final Plugin pluginForClass = (Plugin) pluginClassEntry.getValue();
189             if (plugin.getKey().equals(pluginForClass.getKey()))
190             {
191                 it.remove();
192             }
193         }
194     }
195 
196     public synchronized void notifyPluginOrModuleEnabled()
197     {
198         flushMissesCaches();
199     }
200 
201     private void flushMissesCaches()
202     {
203         missedPluginClass.clear();
204         missedPluginResource.clear();
205     }
206 }