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