1 package com.atlassian.plugin.loaders;
2
3 import com.atlassian.plugin.DefaultPluginArtifactFactory;
4 import com.atlassian.plugin.ModuleDescriptorFactory;
5 import com.atlassian.plugin.Plugin;
6 import com.atlassian.plugin.PluginArtifact;
7 import com.atlassian.plugin.PluginArtifactFactory;
8 import com.atlassian.plugin.PluginException;
9 import com.atlassian.plugin.PluginParseException;
10 import com.atlassian.plugin.PluginState;
11 import com.atlassian.plugin.event.PluginEventListener;
12 import com.atlassian.plugin.event.PluginEventManager;
13 import com.atlassian.plugin.event.events.PluginFrameworkShutdownEvent;
14 import com.atlassian.plugin.factories.PluginFactory;
15 import com.atlassian.plugin.impl.UnloadablePlugin;
16 import com.atlassian.plugin.loaders.classloading.DeploymentUnit;
17 import com.atlassian.plugin.loaders.classloading.Scanner;
18
19 import org.apache.commons.lang.Validate;
20 import org.apache.commons.logging.Log;
21 import org.apache.commons.logging.LogFactory;
22
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Iterator;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.TreeMap;
29
30
31
32
33
34
35
36 public class ScanningPluginLoader implements DynamicPluginLoader
37 {
38 private static Log log = LogFactory.getLog(DirectoryPluginLoader.class);
39 protected final com.atlassian.plugin.loaders.classloading.Scanner scanner;
40 protected final Map<DeploymentUnit, Plugin> plugins;
41 protected final List<PluginFactory> pluginFactories;
42 protected final PluginArtifactFactory pluginArtifactFactory;
43
44
45
46
47
48
49
50
51
52 public ScanningPluginLoader(final Scanner scanner, final List<PluginFactory> pluginFactories, final PluginEventManager pluginEventManager)
53 {
54 this(scanner, pluginFactories, new DefaultPluginArtifactFactory(), pluginEventManager);
55 }
56
57
58
59
60
61
62
63
64
65
66 public ScanningPluginLoader(final Scanner scanner, final List<PluginFactory> pluginFactories, final PluginArtifactFactory pluginArtifactFactory, final PluginEventManager pluginEventManager)
67 {
68 Validate.notNull(pluginFactories, "The list of plugin factories must be specified");
69 Validate.notNull(pluginEventManager, "The event manager must be specified");
70 Validate.notNull(scanner, "The scanner must be specified");
71
72 plugins = new TreeMap<DeploymentUnit, Plugin>();
73
74 this.pluginArtifactFactory = pluginArtifactFactory;
75 this.scanner = scanner;
76 this.pluginFactories = new ArrayList<PluginFactory>(pluginFactories);
77
78 pluginEventManager.register(this);
79 }
80
81 public Collection<Plugin> loadAllPlugins(final ModuleDescriptorFactory moduleDescriptorFactory)
82 {
83 scanner.scan();
84
85 for (final DeploymentUnit deploymentUnit : scanner.getDeploymentUnits())
86 {
87 Plugin plugin = deployPluginFromUnit(deploymentUnit, moduleDescriptorFactory);
88 plugin = postProcess(plugin);
89 plugins.put(deploymentUnit, plugin);
90 }
91
92 if (scanner.getDeploymentUnits().isEmpty())
93 {
94 log.info("No plugins found to be deployed");
95 }
96
97 return plugins.values();
98 }
99
100 protected Plugin deployPluginFromUnit(final DeploymentUnit deploymentUnit, final ModuleDescriptorFactory moduleDescriptorFactory)
101 {
102 Plugin plugin = null;
103 String errorText = "No plugin factories found for plugin file " + deploymentUnit;
104
105 String pluginKey = null;
106 for (final PluginFactory factory : pluginFactories)
107 {
108 try
109 {
110 final PluginArtifact artifact = pluginArtifactFactory.create(deploymentUnit.getPath().toURI());
111 pluginKey = factory.canCreate(artifact);
112 if (pluginKey != null)
113 {
114 plugin = factory.create(artifact, moduleDescriptorFactory);
115 if (plugin != null)
116 {
117 break;
118 }
119 }
120 }
121 catch (final RuntimeException ex)
122 {
123 log.error("Unable to deploy plugin '" + pluginKey + "', file " + deploymentUnit, ex);
124 errorText = ex.getMessage();
125 break;
126 }
127 }
128 if (plugin == null)
129 {
130 plugin = new UnloadablePlugin(errorText);
131 if (pluginKey != null)
132 {
133 plugin.setKey(pluginKey);
134 }
135 else
136 {
137 plugin.setKey(deploymentUnit.getPath().getName());
138 }
139
140 }
141 else
142 {
143 log.info("Plugin " + deploymentUnit + " created");
144 }
145
146 return plugin;
147 }
148
149 public boolean supportsRemoval()
150 {
151 return true;
152 }
153
154 public boolean supportsAddition()
155 {
156 return true;
157 }
158
159
160
161
162
163 public Collection<Plugin> addFoundPlugins(final ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
164 {
165
166 final Collection<DeploymentUnit> updatedDeploymentUnits = scanner.scan();
167
168
169 final List<Plugin> foundPlugins = new ArrayList<Plugin>();
170 for (final DeploymentUnit deploymentUnit : updatedDeploymentUnits)
171 {
172 if (!plugins.containsKey(deploymentUnit))
173 {
174 final Plugin plugin = deployPluginFromUnit(deploymentUnit, moduleDescriptorFactory);
175 plugins.put(deploymentUnit, plugin);
176 foundPlugins.add(plugin);
177 }
178 }
179 if (foundPlugins.isEmpty())
180 {
181 log.info("No plugins found to be installed");
182 }
183
184 return foundPlugins;
185 }
186
187
188
189
190
191 public void removePlugin(final Plugin plugin) throws PluginException
192 {
193 if (plugin.getPluginState() == PluginState.ENABLED)
194 {
195 throw new PluginException("Cannot remove an enabled plugin");
196 }
197
198 if (!plugin.isUninstallable())
199 {
200 throw new PluginException("Cannot remove an uninstallable plugin: [" + plugin.getName() + "]");
201 }
202
203 final DeploymentUnit deploymentUnit = findMatchingDeploymentUnit(plugin);
204 plugin.uninstall();
205
206 try
207 {
208
209
210
211 boolean found = false;
212 for (final DeploymentUnit unit : plugins.keySet())
213 {
214 if (unit.getPath().equals(deploymentUnit.getPath()) && !unit.equals(deploymentUnit))
215 {
216 found = true;
217 break;
218 }
219 }
220
221 if (!found)
222 {
223 scanner.remove(deploymentUnit);
224 }
225 }
226 catch (final SecurityException e)
227 {
228 throw new PluginException(e);
229 }
230
231 plugins.remove(deploymentUnit);
232 log.info("Removed plugin " + plugin.getKey());
233 }
234
235 private DeploymentUnit findMatchingDeploymentUnit(final Plugin plugin) throws PluginException
236 {
237 DeploymentUnit deploymentUnit = null;
238 for (final Map.Entry<DeploymentUnit, Plugin> entry : plugins.entrySet())
239 {
240
241
242
243 if (entry.getValue() == plugin)
244 {
245 deploymentUnit = entry.getKey();
246 break;
247 }
248 }
249
250 if (deploymentUnit == null)
251 {
252 throw new PluginException("This pluginLoader has no memory of deploying the plugin you are trying remove: [" + plugin.getName() + "]");
253 }
254 return deploymentUnit;
255 }
256
257
258
259
260
261 @PluginEventListener
262 public void onShutdown(final PluginFrameworkShutdownEvent event)
263 {
264 for (final Iterator<Plugin> it = plugins.values().iterator(); it.hasNext();)
265 {
266 final Plugin plugin = it.next();
267 plugin.uninstall();
268 it.remove();
269 }
270
271 scanner.reset();
272 }
273
274
275
276
277 @Deprecated
278 public void shutDown()
279 {
280 onShutdown(null);
281 }
282
283
284
285
286
287
288
289
290 public String canLoad(final PluginArtifact pluginArtifact) throws PluginParseException
291 {
292 String pluginKey = null;
293 for (final PluginFactory factory : pluginFactories)
294 {
295 pluginKey = factory.canCreate(pluginArtifact);
296 if (pluginKey != null)
297 {
298 break;
299 }
300 }
301 return pluginKey;
302 }
303
304
305
306
307
308
309
310
311 protected Plugin postProcess(final Plugin plugin)
312 {
313 return plugin;
314 }
315 }