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<String,Plugin>();
25 private final Map<String,Plugin> pluginClassIndex = new HashMap<String,Plugin>();
26
27 private final Set<String> missedPluginResource = new HashSet<String>();
28 private final Set<String> missedPluginClass = new HashSet<String>();
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 = 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 = 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<Plugin> plugins = pluginAccessor.getEnabledPlugins();
111 for (Plugin plugin : plugins)
112 {
113 try
114 {
115 Class result = plugin.getClassLoader().loadClass(className);
116
117 synchronized (this)
118 {
119 pluginClassIndex.put(className, plugin);
120 }
121 return result;
122 }
123 catch (ClassNotFoundException e)
124 {
125
126 }
127 }
128 synchronized (this)
129 {
130 missedPluginClass.add(className);
131 }
132 return null;
133 }
134
135 private URL getResourceFromPlugins(String name)
136 {
137 final boolean isMissedResource;
138 synchronized (this)
139 {
140 isMissedResource = missedPluginResource.contains(name);
141 }
142 if (isMissedResource)
143 {
144 return null;
145 }
146 final Collection<Plugin> plugins = pluginAccessor.getEnabledPlugins();
147 for (Plugin plugin : plugins)
148 {
149 final URL resource = plugin.getClassLoader().getResource(name);
150 if (resource != null)
151 {
152 synchronized (this)
153 {
154 pluginResourceIndex.put(name, plugin);
155 }
156 return resource;
157 }
158 }
159 synchronized (this)
160 {
161 missedPluginResource.add(name);
162 }
163 return null;
164 }
165
166 private boolean isPluginEnabled(Plugin plugin)
167 {
168 return plugin != null && pluginAccessor.isPluginEnabled(plugin.getKey());
169 }
170
171 public synchronized void notifyUninstallPlugin(Plugin plugin)
172 {
173 flushMissesCaches();
174 for (Iterator it = pluginResourceIndex.entrySet().iterator(); it.hasNext();)
175 {
176 final Map.Entry resourceEntry = (Map.Entry) it.next();
177 final Plugin pluginForResource = (Plugin) resourceEntry.getValue();
178 if (plugin.getKey().equals(pluginForResource.getKey()))
179 {
180 it.remove();
181 }
182 }
183 for (Iterator it = pluginClassIndex.entrySet().iterator(); it.hasNext();)
184 {
185 final Map.Entry pluginClassEntry = (Map.Entry) it.next();
186 final Plugin pluginForClass = (Plugin) pluginClassEntry.getValue();
187 if (plugin.getKey().equals(pluginForClass.getKey()))
188 {
189 it.remove();
190 }
191 }
192 }
193
194 public synchronized void notifyPluginOrModuleEnabled()
195 {
196 flushMissesCaches();
197 }
198
199 private void flushMissesCaches()
200 {
201 missedPluginClass.clear();
202 missedPluginResource.clear();
203 }
204 }