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
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 an enabled plugin");
202 }
203
204 if (!plugin.isUninstallable())
205 {
206 throw new PluginException("Cannot remove an uninstallable plugin: [" + plugin.getName() + "]");
207 }
208
209 final DeploymentUnit deploymentUnit = findMatchingDeploymentUnit(plugin);
210 plugin.uninstall();
211
212 try
213 {
214
215
216
217 boolean found = false;
218 for (final DeploymentUnit unit : plugins.keySet())
219 {
220 if (unit.getPath().equals(deploymentUnit.getPath()) && !unit.equals(deploymentUnit))
221 {
222 found = true;
223 break;
224 }
225 }
226
227 if (!found)
228 {
229 scanner.remove(deploymentUnit);
230 }
231 }
232 catch (final SecurityException e)
233 {
234 throw new PluginException(e);
235 }
236
237 plugins.remove(deploymentUnit);
238 log.info("Removed plugin " + plugin.getKey());
239 }
240
241 private DeploymentUnit findMatchingDeploymentUnit(final Plugin plugin) throws PluginException
242 {
243 DeploymentUnit deploymentUnit = null;
244 for (final Map.Entry<DeploymentUnit, Plugin> entry : plugins.entrySet())
245 {
246
247
248
249 if (entry.getValue() == plugin)
250 {
251 deploymentUnit = entry.getKey();
252 break;
253 }
254 }
255
256 if (deploymentUnit == null)
257 {
258 throw new PluginException("This pluginLoader has no memory of deploying the plugin you are trying remove: [" + plugin.getName() + "]");
259 }
260 return deploymentUnit;
261 }
262
263
264
265
266
267 @PluginEventListener
268 public void onShutdown(final PluginFrameworkShutdownEvent event)
269 {
270 for (final Iterator<Plugin> it = plugins.values().iterator(); it.hasNext();)
271 {
272 final Plugin plugin = it.next();
273 if (plugin.isUninstallable())
274 {
275 plugin.uninstall();
276 }
277 it.remove();
278 }
279
280 scanner.reset();
281 }
282
283
284
285
286 @Deprecated
287 public void shutDown()
288 {
289 onShutdown(null);
290 }
291
292 @Override
293 public boolean isDynamicPluginLoader()
294 {
295 return true;
296 }
297
298
299
300
301
302
303
304
305 public String canLoad(final PluginArtifact pluginArtifact) throws PluginParseException
306 {
307 String pluginKey = null;
308 for (final PluginFactory factory : pluginFactories)
309 {
310 pluginKey = factory.canCreate(pluginArtifact);
311 if (pluginKey != null)
312 {
313 break;
314 }
315 }
316 return pluginKey;
317 }
318
319
320
321
322
323
324
325
326 protected Plugin postProcess(final Plugin plugin)
327 {
328 return plugin;
329 }
330 }