1 package com.atlassian.plugin.loaders;
2
3 import com.atlassian.annotations.Internal;
4 import com.atlassian.plugin.InstallationMode;
5 import com.atlassian.plugin.ModuleDescriptor;
6 import com.atlassian.plugin.ModuleDescriptorFactory;
7 import com.atlassian.plugin.Plugin;
8 import com.atlassian.plugin.PluginArtifact;
9 import com.atlassian.plugin.PluginArtifactFactory;
10 import com.atlassian.plugin.PluginDependencies;
11 import com.atlassian.plugin.PluginException;
12 import com.atlassian.plugin.PluginInformation;
13 import com.atlassian.plugin.PluginState;
14 import com.atlassian.plugin.Resourced;
15 import com.atlassian.plugin.elements.ResourceDescriptor;
16 import com.atlassian.plugin.elements.ResourceLocation;
17 import com.atlassian.plugin.event.PluginEventManager;
18 import com.atlassian.plugin.event.events.PluginFrameworkShutdownEvent;
19 import com.atlassian.plugin.factories.PluginFactory;
20 import com.atlassian.plugin.impl.UnloadablePlugin;
21 import com.atlassian.plugin.loaders.classloading.DeploymentUnit;
22 import com.atlassian.plugin.loaders.classloading.Scanner;
23 import com.google.common.collect.Iterables;
24 import org.dom4j.Element;
25 import org.junit.Before;
26 import org.junit.Rule;
27 import org.junit.Test;
28 import org.junit.rules.ExpectedException;
29 import org.junit.runner.RunWith;
30 import org.mockito.Mock;
31 import org.mockito.junit.MockitoJUnitRunner;
32
33 import javax.annotation.Nonnull;
34 import javax.annotation.Nullable;
35 import java.io.File;
36 import java.io.InputStream;
37 import java.net.URL;
38 import java.util.Arrays;
39 import java.util.Collection;
40 import java.util.Collections;
41 import java.util.Date;
42 import java.util.List;
43 import java.util.Optional;
44 import java.util.Set;
45
46 import static org.hamcrest.MatcherAssert.assertThat;
47 import static org.hamcrest.Matchers.containsInAnyOrder;
48 import static org.hamcrest.Matchers.is;
49 import static org.hamcrest.Matchers.nullValue;
50 import static org.junit.Assert.assertEquals;
51 import static org.junit.Assert.assertNotNull;
52 import static org.junit.Assert.assertNotSame;
53 import static org.junit.Assert.assertSame;
54 import static org.junit.Assert.assertTrue;
55 import static org.junit.Assert.fail;
56 import static org.mockito.ArgumentMatchers.any;
57 import static org.mockito.Mockito.mock;
58 import static org.mockito.Mockito.never;
59 import static org.mockito.Mockito.verify;
60 import static org.mockito.Mockito.when;
61
62 @RunWith(MockitoJUnitRunner.Silent.class)
63 public class TestScanningPluginLoader {
64 private static final String PLUGIN_KEY = "plugin-key";
65
66 @Rule
67 public final ExpectedException expectedException = ExpectedException.none();
68
69 @Mock
70 private PluginArtifactFactory pluginArtifactFactory;
71
72 @Mock
73 private PluginArtifact pluginArtifact;
74
75 @Mock
76 private PluginFactory pluginFactory;
77
78 @Mock
79 private ModuleDescriptorFactory moduleDescriptorFactory;
80
81 @Mock
82 private Plugin plugin;
83
84 @Mock
85 private Scanner scanner;
86
87 @Mock
88 private PluginEventManager pluginEventManager;
89
90 @Mock
91 private Element module;
92
93 private DeploymentUnit deploymentUnit;
94
95 @Before
96 public void configureMocks() {
97 deploymentUnit = new DeploymentUnit(new File("foo.jar"));
98 when(plugin.getKey()).thenReturn(PLUGIN_KEY);
99 when(pluginArtifactFactory.create(deploymentUnit.getPath().toURI())).thenReturn(pluginArtifact);
100 when(pluginFactory.canCreate(pluginArtifact)).thenReturn("foo");
101 when(scanner.getDeploymentUnits()).thenReturn(Arrays.asList(deploymentUnit));
102 }
103
104 @Test
105 public void loadAllPluginsLoadsPlugin() {
106 when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
107
108 final ScanningPluginLoader loader = buildScanningPluginLoader();
109 final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
110 assertThat(plugins, containsInAnyOrder(plugin));
111 }
112
113 @Test
114 public void removeEnabledPluginFails() {
115 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
116
117 final ScanningPluginLoader loader = buildScanningPluginLoader();
118
119 expectedException.expect(PluginException.class);
120 expectedException.expectMessage(PLUGIN_KEY);
121 loader.removePlugin(plugin);
122 }
123
124 @Test
125 public void removeNotUninstallablePluginFails() {
126 when(plugin.isUninstallable()).thenReturn(false);
127
128 final ScanningPluginLoader loader = buildScanningPluginLoader();
129
130 expectedException.expect(PluginException.class);
131 expectedException.expectMessage(PLUGIN_KEY);
132 loader.removePlugin(plugin);
133 }
134
135 @Test
136 public void removeDeleteablePluginDoesUninstallAndDelete() {
137 when(plugin.isUninstallable()).thenReturn(true);
138 when(plugin.isDeleteable()).thenReturn(true);
139 when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
140
141 final ScanningPluginLoader loader = buildScanningPluginLoader();
142 final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
143 assertThat(plugins, containsInAnyOrder(plugin));
144 loader.removePlugin(plugin);
145 verify(plugin).uninstall();
146 verify(scanner).remove(deploymentUnit);
147 }
148
149 @Test
150 public void removeNotDeleteablePluginDoesUninstallButDoesntDelete() {
151 when(plugin.isUninstallable()).thenReturn(true);
152 when(plugin.isDeleteable()).thenReturn(false);
153 when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
154
155 final ScanningPluginLoader loader = buildScanningPluginLoader();
156 final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
157 assertThat(plugins, containsInAnyOrder(plugin));
158 loader.removePlugin(plugin);
159 verify(plugin).uninstall();
160 verify(scanner, never()).remove(any(DeploymentUnit.class));
161 }
162
163 @Test
164 public void discardedPluginIsNotTracked() {
165 when(plugin.isUninstallable()).thenReturn(true);
166 when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
167
168 final ScanningPluginLoader loader = buildScanningPluginLoader();
169 final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
170 assertThat(plugins, containsInAnyOrder(plugin));
171 loader.discardPlugin(plugin);
172 try {
173
174 loader.removePlugin(plugin);
175 fail();
176 } catch (final PluginException pe) {
177
178 }
179
180 loader.onShutdown(mock(PluginFrameworkShutdownEvent.class));
181 verify(plugin, never()).uninstall();
182 }
183
184 @Test
185 public void shutdownUninstallsUninstallablePlugin() {
186 when(plugin.isUninstallable()).thenReturn(true);
187 when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
188
189 final ScanningPluginLoader loader = buildScanningPluginLoader();
190 loader.loadAllPlugins(moduleDescriptorFactory);
191 loader.onShutdown(mock(PluginFrameworkShutdownEvent.class));
192 verify(plugin).uninstall();
193 }
194
195 @Test
196 public void shutdownDoesNotUninstallNotUninstallablePlugin() {
197 when(plugin.isUninstallable()).thenReturn(false);
198 when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
199
200 final ScanningPluginLoader loader = buildScanningPluginLoader();
201 loader.loadAllPlugins(moduleDescriptorFactory);
202 loader.onShutdown(mock(PluginFrameworkShutdownEvent.class));
203 verify(plugin, never()).uninstall();
204 }
205
206 @Test
207 public void factoryThrowingRuntimeExceptionYieldsUnloadablePlugin() {
208 factoryThrowingYieldsUnloadablePlugin(new IllegalArgumentException());
209 }
210
211 @Test
212 public void factoryThrowingErrorYieldsUnloadablePlugin() {
213 factoryThrowingYieldsUnloadablePlugin(new NoClassDefFoundError());
214 }
215
216 private void factoryThrowingYieldsUnloadablePlugin(final Throwable throwable) {
217 when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenThrow(throwable);
218
219 final ScanningPluginLoader loader = buildScanningPluginLoader();
220 final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
221 assertNotNull(plugins);
222 assertEquals(1, Iterables.size(plugins));
223 assertTrue(Iterables.getOnlyElement(plugins) instanceof UnloadablePlugin);
224 }
225
226 @Test
227 public void pluginLoaderCallsPostProcess() {
228 when(plugin.isUninstallable()).thenReturn(true);
229 when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
230
231 final ScanningPluginLoader loader = new ScanningPluginLoader(
232 scanner, Collections.singletonList(pluginFactory), pluginArtifactFactory, pluginEventManager) {
233 @Override
234 protected Plugin postProcess(final Plugin plugin) {
235 return new WrappedPlugin(plugin);
236 }
237 };
238 final Iterable<Plugin> allPlugins = loader.loadAllPlugins(moduleDescriptorFactory);
239 assertPluginsIsWrapperFor(allPlugins, plugin);
240
241 final DeploymentUnit unitB = new DeploymentUnit(new File("bar.jar"));
242 final PluginArtifact pluginArtifactB = mock(PluginArtifact.class);
243 final Plugin pluginB = mock(Plugin.class);
244 when(scanner.scan()).thenReturn(Arrays.asList(unitB));
245 when(pluginArtifactFactory.create(unitB.getPath().toURI())).thenReturn(pluginArtifactB);
246 when(pluginFactory.canCreate(pluginArtifactB)).thenReturn("bar");
247 when(pluginFactory.create(pluginArtifactB, moduleDescriptorFactory)).thenReturn(pluginB);
248
249 final Iterable<Plugin> foundPlugins = loader.loadFoundPlugins(moduleDescriptorFactory);
250 assertPluginsIsWrapperFor(foundPlugins, pluginB);
251 }
252
253 @Test
254 public void createModule() {
255 final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class);
256
257 final ScanningPluginLoader scanningPluginLoader = buildScanningPluginLoader();
258
259 when(pluginFactory.createModule(plugin, module, moduleDescriptorFactory)).thenReturn(moduleDescriptor);
260
261 assertThat(scanningPluginLoader.createModule(plugin, module, moduleDescriptorFactory), is(moduleDescriptor));
262 }
263
264 @Test
265 public void createModuleNoFactory() {
266 final ScanningPluginLoader scanningPluginLoader = buildScanningPluginLoader();
267
268 when(pluginFactory.createModule(plugin, module, moduleDescriptorFactory)).thenReturn(null);
269
270 assertThat(scanningPluginLoader.createModule(plugin, module, moduleDescriptorFactory), nullValue());
271 }
272
273
274
275
276
277
278
279 private static class WrappedPlugin implements Plugin {
280
281 private final Plugin delegate;
282
283 public WrappedPlugin(final Plugin plugin) {
284 delegate = plugin;
285 }
286
287 @Override
288 public int getPluginsVersion() {
289 return delegate.getPluginsVersion();
290 }
291
292 @Override
293 public void setPluginsVersion(int version) {
294 delegate.setPluginsVersion(version);
295 }
296
297 @Override
298 public String getName() {
299 return delegate.getName();
300 }
301
302 @Override
303 public void setName(String name) {
304 delegate.setName(name);
305 }
306
307 @Override
308 public String getI18nNameKey() {
309 return delegate.getI18nNameKey();
310 }
311
312 @Override
313 public void setI18nNameKey(String i18nNameKey) {
314 delegate.setI18nNameKey(i18nNameKey);
315 }
316
317 @Override
318 public String getKey() {
319 return delegate.getKey();
320 }
321
322 @Override
323 public void setKey(String aPackage) {
324 delegate.setKey(aPackage);
325 }
326
327 @Override
328 public void addModuleDescriptor(ModuleDescriptor<?> moduleDescriptor) {
329 delegate.addModuleDescriptor(moduleDescriptor);
330 }
331
332 @Override
333 public Collection<ModuleDescriptor<?>> getModuleDescriptors() {
334 return delegate.getModuleDescriptors();
335 }
336
337 @Override
338 public ModuleDescriptor<?> getModuleDescriptor(String key) {
339 return delegate.getModuleDescriptor(key);
340 }
341
342 @Override
343 public <M> List<ModuleDescriptor<M>> getModuleDescriptorsByModuleClass(Class<M> moduleClass) {
344 return delegate.getModuleDescriptorsByModuleClass(moduleClass);
345 }
346
347 @Override
348 public InstallationMode getInstallationMode() {
349 return delegate.getInstallationMode();
350 }
351
352 @Override
353 public boolean isEnabledByDefault() {
354 return delegate.isEnabledByDefault();
355 }
356
357 @Override
358 public void setEnabledByDefault(boolean enabledByDefault) {
359 delegate.setEnabledByDefault(enabledByDefault);
360 }
361
362 @Override
363 public PluginInformation getPluginInformation() {
364 return delegate.getPluginInformation();
365 }
366
367 @Override
368 public void setPluginInformation(PluginInformation pluginInformation) {
369 delegate.setPluginInformation(pluginInformation);
370 }
371
372 @Override
373 public void setResources(Resourced resources) {
374 delegate.setResources(resources);
375 }
376
377 @Override
378 public PluginState getPluginState() {
379 return delegate.getPluginState();
380 }
381
382 @Override
383 public boolean isSystemPlugin() {
384 return delegate.isSystemPlugin();
385 }
386
387 @Override
388 public void setSystemPlugin(boolean system) {
389 delegate.setSystemPlugin(system);
390 }
391
392 @Override
393 public boolean containsSystemModule() {
394 return delegate.containsSystemModule();
395 }
396
397 @Override
398 public boolean isBundledPlugin() {
399 return delegate.isBundledPlugin();
400 }
401
402 @Override
403 public Date getDateLoaded() {
404 return delegate.getDateLoaded();
405 }
406
407 @Override
408 public Date getDateInstalled() {
409 return delegate.getDateInstalled();
410 }
411
412 @Override
413 public boolean isUninstallable() {
414 return delegate.isUninstallable();
415 }
416
417 @Override
418 public boolean isDeleteable() {
419 return delegate.isDeleteable();
420 }
421
422 @Override
423 public boolean isDynamicallyLoaded() {
424 return delegate.isDynamicallyLoaded();
425 }
426
427 @Override
428 public <T> Class<T> loadClass(String clazz, Class<?> callingClass) throws ClassNotFoundException {
429 return delegate.loadClass(clazz, callingClass);
430 }
431
432 @Override
433 public ClassLoader getClassLoader() {
434 return delegate.getClassLoader();
435 }
436
437 @Override
438 public URL getResource(String path) {
439 return delegate.getResource(path);
440 }
441
442 @Override
443 public InputStream getResourceAsStream(String name) {
444 return delegate.getResourceAsStream(name);
445 }
446
447 @Override
448 public void install() throws PluginException {
449 delegate.install();
450 }
451
452 @Override
453 public void uninstall() throws PluginException {
454 delegate.uninstall();
455 }
456
457 @Override
458 public void enable() throws PluginException {
459 delegate.enable();
460 }
461
462 @Override
463 public void disable() throws PluginException {
464 delegate.disable();
465 }
466
467 @Override
468 @Nonnull
469 public PluginDependencies getDependencies() {
470 return delegate.getDependencies();
471 }
472
473 @Override
474 public Set<String> getActivePermissions() {
475 return delegate.getActivePermissions();
476 }
477
478 @Override
479 public boolean hasAllPermissions() {
480 return delegate.hasAllPermissions();
481 }
482
483 @Override
484 public void resolve() {
485 delegate.resolve();
486 }
487
488 @Override
489 @Nullable
490 public Date getDateEnabling() {
491 return delegate.getDateEnabling();
492 }
493
494 @Override
495 @Nullable
496 public Date getDateEnabled() {
497 return delegate.getDateEnabled();
498 }
499
500 @Override
501 @Internal
502 public PluginArtifact getPluginArtifact() {
503 return delegate.getPluginArtifact();
504 }
505
506 @Override
507 public Optional<String> getScopeKey() {
508 return delegate.getScopeKey();
509 }
510
511 @Override
512 public List<ResourceDescriptor> getResourceDescriptors() {
513 return delegate.getResourceDescriptors();
514 }
515
516 @Override
517 public ResourceDescriptor getResourceDescriptor(String type, String name) {
518 return delegate.getResourceDescriptor(type, name);
519 }
520
521 @Override
522 public ResourceLocation getResourceLocation(String type, String name) {
523 return delegate.getResourceLocation(type, name);
524 }
525
526 @Override
527 public int compareTo(Plugin o) {
528 return delegate.compareTo(o);
529 }
530 }
531
532 private void assertPluginsIsWrapperFor(final Iterable<Plugin> plugins, final Plugin originalPlugin) {
533 assertNotNull(plugins);
534 assertEquals(1, Iterables.size(plugins));
535 final Plugin loadedPlugin = Iterables.getOnlyElement(plugins);
536 assertNotSame(loadedPlugin, originalPlugin);
537 assertTrue(loadedPlugin instanceof WrappedPlugin);
538 final WrappedPlugin wrappedPlugin = (WrappedPlugin) loadedPlugin;
539 assertSame(wrappedPlugin.delegate, originalPlugin);
540 }
541
542 private ScanningPluginLoader buildScanningPluginLoader() {
543 return new ScanningPluginLoader(scanner, Collections.singletonList(pluginFactory), pluginArtifactFactory, pluginEventManager);
544 }
545 }