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
25 private final Map
26
27 private final Set
28 private final Set
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
118 synchronized (this)
119 {
120 pluginClassIndex.put(className, plugin);
121 }
122 return result;
123 }
124 catch (ClassNotFoundException e)
125 {
126
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 }