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.slf4j.Logger;
21 import org.slf4j.LoggerFactory;
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 import java.util.Collections;
30
31
32
33
34
35
36
37 public class ScanningPluginLoader implements DynamicPluginLoader
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 Validate.notNull(pluginFactories, "The list of plugin factories must be specified");
71 Validate.notNull(pluginEventManager, "The event manager must be specified");
72 Validate.notNull(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 Collection<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 Collections.unmodifiableCollection(new ArrayList<Plugin>(plugins.values()));
100 }
101
102 protected Plugin deployPluginFromUnit(final DeploymentUnit deploymentUnit, final ModuleDescriptorFactory moduleDescriptorFactory)
103 {
104 Plugin plugin = null;
105 String errorText = "No plugin factories found for plugin file " + deploymentUnit;
106
107 String pluginKey = null;
108 for (final PluginFactory factory : pluginFactories)
109 {
110 try
111 {
112 final PluginArtifact artifact = pluginArtifactFactory.create(deploymentUnit.getPath().toURI());
113 pluginKey = factory.canCreate(artifact);
114 if (pluginKey != null)
115 {
116 plugin = factory.create(artifact, moduleDescriptorFactory);
117 if (plugin != null)
118 {
119 break;
120 }
121 }
122 }
123 catch (final Throwable ex)
124 {
125 log.error("Unable to deploy plugin '" + pluginKey + "', file " + deploymentUnit, ex);
126 errorText = ex.getMessage();
127 break;
128 }
129 }
130 if (plugin == null)
131 {
132 plugin = new UnloadablePlugin(errorText);
133 if (pluginKey != null)
134 {
135 plugin.setKey(pluginKey);
136 }
137 else
138 {
139 plugin.setKey(deploymentUnit.getPath().getName());
140 }
141
142 }
143 else
144 {
145 log.info("Plugin " + deploymentUnit + " created");
146 }
147
148 return plugin;
149 }
150
151 public boolean supportsRemoval()
152 {
153 return true;
154 }
155
156 public boolean supportsAddition()
157 {
158 return true;
159 }
160
161
162
163
164
165 public Collection<Plugin> addFoundPlugins(final ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
166 {
167
168 final Collection<DeploymentUnit> updatedDeploymentUnits = scanner.scan();
169
170
171 final List<Plugin> foundPlugins = new ArrayList<Plugin>();
172 for (final DeploymentUnit deploymentUnit : updatedDeploymentUnits)
173 {
174 if (!plugins.containsKey(deploymentUnit))
175 {
176 Plugin plugin = deployPluginFromUnit(deploymentUnit, moduleDescriptorFactory);
177 plugin = postProcess(plugin);
178 plugins.put(deploymentUnit, plugin);
179 foundPlugins.add(plugin);
180 }
181 }
182 if (foundPlugins.isEmpty())
183 {
184 log.info("No plugins found to be installed");
185 }
186
187 return foundPlugins;
188 }
189
190
191
192
193
194 public void removePlugin(final Plugin plugin) throws PluginException
195 {
196 if (plugin.getPluginState() == PluginState.ENABLED)
197 {
198 throw new PluginException("Cannot remove an enabled plugin");
199 }
200
201 if (!plugin.isUninstallable())
202 {
203 throw new PluginException("Cannot remove an uninstallable plugin: [" + plugin.getName() + "]");
204 }
205
206 final DeploymentUnit deploymentUnit = findMatchingDeploymentUnit(plugin);
207 plugin.uninstall();
208
209 try
210 {
211
212
213
214 boolean found = false;
215 for (final DeploymentUnit unit : plugins.keySet())
216 {
217 if (unit.getPath().equals(deploymentUnit.getPath()) && !unit.equals(deploymentUnit))
218 {
219 found = true;
220 break;
221 }
222 }
223
224 if (!found)
225 {
226 scanner.remove(deploymentUnit);
227 }
228 }
229 catch (final SecurityException e)
230 {
231 throw new PluginException(e);
232 }
233
234 plugins.remove(deploymentUnit);
235 log.info("Removed plugin " + plugin.getKey());
236 }
237
238 private DeploymentUnit findMatchingDeploymentUnit(final Plugin plugin) throws PluginException
239 {
240 DeploymentUnit deploymentUnit = null;
241 for (final Map.Entry<DeploymentUnit, Plugin> entry : plugins.entrySet())
242 {
243
244
245
246 if (entry.getValue() == plugin)
247 {
248 deploymentUnit = entry.getKey();
249 break;
250 }
251 }
252
253 if (deploymentUnit == null)
254 {
255 throw new PluginException("This pluginLoader has no memory of deploying the plugin you are trying remove: [" + plugin.getName() + "]");
256 }
257 return deploymentUnit;
258 }
259
260
261
262
263
264 @PluginEventListener
265 public void onShutdown(final PluginFrameworkShutdownEvent event)
266 {
267 for (final Iterator<Plugin> it = plugins.values().iterator(); it.hasNext();)
268 {
269 final Plugin plugin = it.next();
270 if (plugin.isUninstallable())
271 {
272 plugin.uninstall();
273 }
274 it.remove();
275 }
276
277 scanner.reset();
278 }
279
280
281
282
283 @Deprecated
284 public void shutDown()
285 {
286 onShutdown(null);
287 }
288
289
290
291
292
293
294
295
296 public String canLoad(final PluginArtifact pluginArtifact) throws PluginParseException
297 {
298 String pluginKey = null;
299 for (final PluginFactory factory : pluginFactories)
300 {
301 pluginKey = factory.canCreate(pluginArtifact);
302 if (pluginKey != null)
303 {
304 break;
305 }
306 }
307 return pluginKey;
308 }
309
310
311
312
313
314
315
316
317 protected Plugin postProcess(final Plugin plugin)
318 {
319 return plugin;
320 }
321 }