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