1 package com.atlassian.plugin.manager;
2
3 import com.atlassian.plugin.DefaultModuleDescriptorFactory;
4 import com.atlassian.plugin.JarPluginArtifact;
5 import com.atlassian.plugin.MockApplication;
6 import com.atlassian.plugin.MockModuleDescriptor;
7 import com.atlassian.plugin.ModuleDescriptor;
8 import com.atlassian.plugin.ModuleDescriptorFactory;
9 import com.atlassian.plugin.Permissions;
10 import com.atlassian.plugin.Plugin;
11 import com.atlassian.plugin.PluginAccessor;
12 import com.atlassian.plugin.PluginArtifact;
13 import com.atlassian.plugin.PluginException;
14 import com.atlassian.plugin.PluginInformation;
15 import com.atlassian.plugin.PluginInstaller;
16 import com.atlassian.plugin.PluginParseException;
17 import com.atlassian.plugin.PluginRestartState;
18 import com.atlassian.plugin.PluginState;
19 import com.atlassian.plugin.descriptors.AbstractModuleDescriptor;
20 import com.atlassian.plugin.descriptors.MockUnusedModuleDescriptor;
21 import com.atlassian.plugin.descriptors.RequiresRestart;
22 import com.atlassian.plugin.event.PluginEventListener;
23 import com.atlassian.plugin.event.PluginEventManager;
24 import com.atlassian.plugin.event.events.PluginContainerUnavailableEvent;
25 import com.atlassian.plugin.event.events.PluginDisabledEvent;
26 import com.atlassian.plugin.event.events.PluginEnabledEvent;
27 import com.atlassian.plugin.event.events.PluginFrameworkShutdownEvent;
28 import com.atlassian.plugin.event.events.PluginModuleAvailableEvent;
29 import com.atlassian.plugin.event.events.PluginModuleDisabledEvent;
30 import com.atlassian.plugin.event.events.PluginModuleEnabledEvent;
31 import com.atlassian.plugin.event.events.PluginModuleUnavailableEvent;
32 import com.atlassian.plugin.event.impl.DefaultPluginEventManager;
33 import com.atlassian.plugin.event.listeners.FailListener;
34 import com.atlassian.plugin.event.listeners.PassListener;
35 import com.atlassian.plugin.factories.LegacyDynamicPluginFactory;
36 import com.atlassian.plugin.factories.PluginFactory;
37 import com.atlassian.plugin.factories.XmlDynamicPluginFactory;
38 import com.atlassian.plugin.hostcontainer.DefaultHostContainer;
39 import com.atlassian.plugin.impl.AbstractDelegatingPlugin;
40 import com.atlassian.plugin.impl.StaticPlugin;
41 import com.atlassian.plugin.impl.UnloadablePlugin;
42 import com.atlassian.plugin.loaders.DirectoryPluginLoader;
43 import com.atlassian.plugin.loaders.DynamicPluginLoader;
44 import com.atlassian.plugin.loaders.PluginLoader;
45 import com.atlassian.plugin.loaders.SinglePluginLoader;
46 import com.atlassian.plugin.loaders.classloading.DirectoryPluginLoaderUtils;
47 import com.atlassian.plugin.manager.store.MemoryPluginPersistentStateStore;
48 import com.atlassian.plugin.metadata.ClasspathFilePluginMetadata;
49 import com.atlassian.plugin.mock.MockAnimal;
50 import com.atlassian.plugin.mock.MockAnimalModuleDescriptor;
51 import com.atlassian.plugin.mock.MockBear;
52 import com.atlassian.plugin.mock.MockMineral;
53 import com.atlassian.plugin.mock.MockMineralModuleDescriptor;
54 import com.atlassian.plugin.mock.MockThing;
55 import com.atlassian.plugin.mock.MockVegetableModuleDescriptor;
56 import com.atlassian.plugin.mock.MockVegetableSubclassModuleDescriptor;
57 import com.atlassian.plugin.module.ModuleFactory;
58 import com.atlassian.plugin.parsers.DescriptorParser;
59 import com.atlassian.plugin.parsers.DescriptorParserFactory;
60 import com.atlassian.plugin.predicate.ModuleDescriptorPredicate;
61 import com.atlassian.plugin.predicate.PluginPredicate;
62 import com.atlassian.plugin.repositories.FilePluginInstaller;
63 import com.atlassian.plugin.test.PluginJarBuilder;
64 import com.google.common.collect.ImmutableList;
65 import com.google.common.collect.ImmutableSet;
66 import com.google.common.collect.Iterables;
67 import com.google.common.collect.Lists;
68
69 import org.apache.commons.io.FileUtils;
70 import org.apache.commons.io.IOUtils;
71 import org.junit.After;
72 import org.junit.Before;
73 import org.junit.Test;
74
75 import java.io.File;
76 import java.io.FileOutputStream;
77 import java.io.IOException;
78 import java.io.InputStream;
79 import java.net.URISyntaxException;
80 import java.net.URL;
81 import java.util.ArrayList;
82 import java.util.Arrays;
83 import java.util.Collection;
84 import java.util.Collections;
85 import java.util.List;
86 import java.util.concurrent.atomic.AtomicBoolean;
87
88 import static com.atlassian.plugin.loaders.classloading.DirectoryPluginLoaderUtils.PADDINGTON_JAR;
89 import static com.google.common.collect.ImmutableList.copyOf;
90 import static java.util.Arrays.asList;
91 import static java.util.Collections.singleton;
92 import static org.junit.Assert.*;
93 import static org.mockito.Matchers.any;
94 import static org.mockito.Matchers.isA;
95 import static org.mockito.Mockito.doThrow;
96 import static org.mockito.Mockito.mock;
97 import static org.mockito.Mockito.never;
98 import static org.mockito.Mockito.times;
99 import static org.mockito.Mockito.verify;
100 import static org.mockito.Mockito.when;
101 import static org.hamcrest.Matchers.hasSize;
102
103 public class TestDefaultPluginManager
104 {
105
106
107
108 protected DefaultPluginManager manager;
109
110 private PluginPersistentStateStore pluginStateStore;
111 private DefaultModuleDescriptorFactory moduleDescriptorFactory;
112
113 private DirectoryPluginLoader directoryPluginLoader;
114 protected PluginEventManager pluginEventManager;
115 private File pluginsDirectory;
116 private File pluginsTestDir;
117
118 private void createFillAndCleanTempPluginDirectory() throws IOException
119 {
120 final DirectoryPluginLoaderUtils.ScannerDirectories directories = DirectoryPluginLoaderUtils.createFillAndCleanTempPluginDirectory();
121 pluginsDirectory = directories.pluginsDirectory;
122 pluginsTestDir = directories.pluginsTestDir;
123 }
124
125 @Before
126 public void setUp() throws Exception
127 {
128 pluginEventManager = new DefaultPluginEventManager();
129
130 pluginStateStore = new MemoryPluginPersistentStateStore();
131 moduleDescriptorFactory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
132 }
133
134 @After
135 public void tearDown() throws Exception
136 {
137 manager = null;
138 moduleDescriptorFactory = null;
139 pluginStateStore = null;
140
141 if (directoryPluginLoader != null)
142 {
143 directoryPluginLoader = null;
144 }
145 }
146
147 protected DefaultPluginManager newDefaultPluginManager(PluginLoader... pluginLoaders)
148 {
149 manager = new DefaultPluginManager(pluginStateStore, copyOf(pluginLoaders), moduleDescriptorFactory, pluginEventManager, true);
150 return manager;
151 }
152
153 protected PluginAccessor getPluginAccessor()
154 {
155 return manager;
156 }
157
158 @Test
159 public void testRetrievePlugins() throws PluginParseException
160 {
161 manager = newDefaultPluginManager(
162 new SinglePluginLoader("test-atlassian-plugin.xml"),
163 new SinglePluginLoader("test-disabled-plugin.xml"));
164
165 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
166 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
167 manager.init();
168
169 assertThat(manager.getPlugins(), hasSize(2));
170 assertThat(manager.getEnabledPlugins(), hasSize(1));
171 manager.enablePlugin("test.disabled.plugin");
172 assertThat(manager.getEnabledPlugins(), hasSize(2));
173 }
174
175 private enum FailureMode{
176 FAIL_TO_ENABLE,
177 FAIL_TO_DISABLE
178 }
179
180 private ModuleDescriptor<Object> mockFailingModuleDescriptor(final String completeKey, final FailureMode... failureModes)
181 {
182 return new AbstractModuleDescriptor<Object>(ModuleFactory.LEGACY_MODULE_FACTORY)
183 {
184 @Override
185 public String getKey()
186 {
187 return completeKey.substring(completeKey.lastIndexOf(":") + 1, completeKey.length());
188 }
189
190 @Override
191 public String getCompleteKey()
192 {
193 return completeKey;
194 }
195
196 @Override
197 public void enabled()
198 {
199 if (Lists.newArrayList(failureModes).contains(FailureMode.FAIL_TO_ENABLE))
200 throw new IllegalArgumentException("Cannot enable");
201 }
202
203 @Override
204 public void disabled()
205 {
206 if (Lists.newArrayList(failureModes).contains(FailureMode.FAIL_TO_DISABLE))
207 throw new IllegalArgumentException("Cannot disable");
208 }
209
210 @Override
211 public Object getModule()
212 {
213 return null;
214 }
215 };
216 }
217
218 @Test
219 public void testEnableModuleFailed() throws PluginParseException
220 {
221 final PluginLoader mockPluginLoader = mock(PluginLoader.class);
222 final ModuleDescriptor<Object> badModuleDescriptor = mockFailingModuleDescriptor("foo:bar", FailureMode.FAIL_TO_ENABLE);
223
224 final AbstractModuleDescriptor goodModuleDescriptor = mock(AbstractModuleDescriptor.class);
225 when(goodModuleDescriptor.getKey()).thenReturn("baz");
226 when(goodModuleDescriptor.getCompleteKey()).thenReturn("foo:baz");
227 when(goodModuleDescriptor.isEnabledByDefault()).thenReturn(true);
228
229 Plugin plugin = mockStaticPlugin("foo", goodModuleDescriptor, badModuleDescriptor);
230
231 when(mockPluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Collections.singletonList(plugin));
232
233 pluginEventManager.register(new FailListener(PluginEnabledEvent.class));
234
235 MyModuleDisabledListener listener = new MyModuleDisabledListener(goodModuleDescriptor);
236 pluginEventManager.register(listener);
237
238 manager = newDefaultPluginManager(mockPluginLoader);
239 manager.init();
240
241 assertThat(getPluginAccessor().getPlugins(), hasSize(1));
242 assertThat(getPluginAccessor().getEnabledPlugins(), hasSize(0));
243 plugin = getPluginAccessor().getPlugin("foo");
244 assertFalse(plugin.getPluginState() == PluginState.ENABLED);
245 assertTrue(plugin instanceof UnloadablePlugin);
246 assertTrue(listener.isCalled());
247 }
248
249 public static class MyModuleDisabledListener
250 {
251 private final ModuleDescriptor goodModuleDescriptor;
252 private volatile boolean disableCalled = false;
253
254 public MyModuleDisabledListener(ModuleDescriptor goodModuleDescriptor)
255 {
256 this.goodModuleDescriptor = goodModuleDescriptor;
257 }
258
259 @PluginEventListener
260 public void onDisable(PluginModuleDisabledEvent evt)
261 {
262 if (evt.getModule().equals(goodModuleDescriptor))
263 {
264 disableCalled = true;
265 }
266 }
267
268 public boolean isCalled()
269 {
270 return disableCalled;
271 }
272 }
273
274 @Test
275 public void testEnabledModuleOutOfSyncWithPlugin() throws PluginParseException
276 {
277 final PluginLoader mockPluginLoader = mock(PluginLoader.class);
278 Plugin plugin = new StaticPlugin();
279 plugin.setKey("foo");
280 plugin.setEnabledByDefault(true);
281 plugin.setPluginInformation(new PluginInformation());
282
283 when(mockPluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Collections.singletonList(plugin));
284
285 manager = newDefaultPluginManager(mockPluginLoader);
286 manager.init();
287
288 assertThat(getPluginAccessor().getPlugins(), hasSize(1));
289 assertThat(getPluginAccessor().getEnabledPlugins(), hasSize(1));
290 plugin = getPluginAccessor().getPlugin("foo");
291 assertTrue(plugin.getPluginState() == PluginState.ENABLED);
292 assertTrue(getPluginAccessor().isPluginEnabled("foo"));
293 plugin.disable();
294 assertFalse(plugin.getPluginState() == PluginState.ENABLED);
295 assertFalse(getPluginAccessor().isPluginEnabled("foo"));
296 }
297
298 @Test
299 public void testDisablePluginModuleWithCannotDisableAnnotation()
300 {
301 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
302 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
303 moduleDescriptorFactory.addModuleDescriptor("bullshit", MockUnusedModuleDescriptor.class);
304 moduleDescriptorFactory.addModuleDescriptor("vegetable", MockVegetableModuleDescriptor.class);
305
306 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
307 manager.init();
308
309 final String pluginKey = "test.atlassian.plugin";
310 final String disablableModuleKey = pluginKey + ":bear";
311 final String moduleKey = pluginKey + ":veg";
312
313
314 manager.disablePluginModule(disablableModuleKey);
315 assertNull(getPluginAccessor().getEnabledPluginModule(disablableModuleKey));
316
317
318 manager.disablePluginModule(moduleKey);
319 assertNotNull(getPluginAccessor().getEnabledPluginModule(moduleKey));
320 }
321
322 @Test
323 public void testDisablePluginModuleWithCannotDisableAnnotationInSuperclass()
324 {
325 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
326 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
327 moduleDescriptorFactory.addModuleDescriptor("bullshit", MockUnusedModuleDescriptor.class);
328 moduleDescriptorFactory.addModuleDescriptor("vegetable", MockVegetableModuleDescriptor.class);
329 moduleDescriptorFactory.addModuleDescriptor("vegetableSubclass", MockVegetableSubclassModuleDescriptor.class);
330
331 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
332 manager.init();
333
334 final String pluginKey = "test.atlassian.plugin";
335 final String disablableModuleKey = pluginKey + ":bear";
336 final String moduleKey = pluginKey + ":vegSubclass";
337
338
339 manager.disablePluginModule(disablableModuleKey);
340 assertNull(getPluginAccessor().getEnabledPluginModule(disablableModuleKey));
341
342
343 manager.disablePluginModule(moduleKey);
344 assertNotNull(getPluginAccessor().getEnabledPluginModule(moduleKey));
345 }
346
347 @Test
348 public void testEnabledDisabledRetrieval() throws PluginParseException
349 {
350 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
351 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
352 moduleDescriptorFactory.addModuleDescriptor("bullshit", MockUnusedModuleDescriptor.class);
353 moduleDescriptorFactory.addModuleDescriptor("vegetable", MockVegetableModuleDescriptor.class);
354
355 final PassListener enabledListener = new PassListener(PluginEnabledEvent.class);
356 final PassListener disabledListener = new PassListener(PluginDisabledEvent.class);
357 pluginEventManager.register(enabledListener);
358 pluginEventManager.register(disabledListener);
359
360 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
361 manager.init();
362
363
364 assertNull(getPluginAccessor().getPlugin("bull:shit"));
365 assertNull(getPluginAccessor().getEnabledPlugin("bull:shit"));
366 assertNull(getPluginAccessor().getPluginModule("bull:shit"));
367 assertNull(getPluginAccessor().getEnabledPluginModule("bull:shit"));
368 assertTrue(getPluginAccessor().getEnabledModuleDescriptorsByClass(NothingModuleDescriptor.class).isEmpty());
369 assertTrue(getPluginAccessor().getEnabledModuleDescriptorsByType("bullshit").isEmpty());
370
371 final String pluginKey = "test.atlassian.plugin";
372 final String moduleKey = pluginKey + ":bear";
373
374
375 assertNotNull(getPluginAccessor().getPlugin(pluginKey));
376 assertNotNull(getPluginAccessor().getEnabledPlugin(pluginKey));
377 assertNotNull(getPluginAccessor().getPluginModule(moduleKey));
378 assertNotNull(getPluginAccessor().getEnabledPluginModule(moduleKey));
379 assertNull(getPluginAccessor().getEnabledPluginModule(pluginKey + ":shit"));
380 assertFalse(getPluginAccessor().getEnabledModuleDescriptorsByClass(MockAnimalModuleDescriptor.class).isEmpty());
381 assertFalse(getPluginAccessor().getEnabledModuleDescriptorsByType("animal").isEmpty());
382 assertFalse(getPluginAccessor().getEnabledModulesByClass(MockBear.class).isEmpty());
383 assertEquals(new MockBear(), getPluginAccessor().getEnabledModulesByClass(MockBear.class).get(0));
384 enabledListener.assertCalled();
385
386
387 manager.disablePlugin(pluginKey);
388 assertNotNull(getPluginAccessor().getPlugin(pluginKey));
389 assertNull(getPluginAccessor().getEnabledPlugin(pluginKey));
390 assertNotNull(getPluginAccessor().getPluginModule(moduleKey));
391 assertNull(getPluginAccessor().getEnabledPluginModule(moduleKey));
392 assertTrue(getPluginAccessor().getEnabledModulesByClass(com.atlassian.plugin.mock.MockBear.class).isEmpty());
393 assertTrue(getPluginAccessor().getEnabledModuleDescriptorsByClass(MockAnimalModuleDescriptor.class).isEmpty());
394 assertTrue(getPluginAccessor().getEnabledModuleDescriptorsByType("animal").isEmpty());
395 disabledListener.assertCalled();
396
397
398 manager.enablePlugin(pluginKey);
399 assertNotNull(getPluginAccessor().getPlugin(pluginKey));
400 assertNotNull(getPluginAccessor().getEnabledPlugin(pluginKey));
401 assertNotNull(getPluginAccessor().getPluginModule(moduleKey));
402 assertNotNull(getPluginAccessor().getEnabledPluginModule(moduleKey));
403 assertFalse(getPluginAccessor().getEnabledModulesByClass(com.atlassian.plugin.mock.MockBear.class).isEmpty());
404 assertFalse(getPluginAccessor().getEnabledModuleDescriptorsByClass(MockAnimalModuleDescriptor.class).isEmpty());
405 assertFalse(getPluginAccessor().getEnabledModuleDescriptorsByType("animal").isEmpty());
406 enabledListener.assertCalled();
407
408
409 pluginEventManager.register(new FailListener(PluginEnabledEvent.class));
410 manager.disablePluginModule(moduleKey);
411 assertNotNull(getPluginAccessor().getPlugin(pluginKey));
412 assertNotNull(getPluginAccessor().getEnabledPlugin(pluginKey));
413 assertNotNull(getPluginAccessor().getPluginModule(moduleKey));
414 assertNull(getPluginAccessor().getEnabledPluginModule(moduleKey));
415 assertTrue(getPluginAccessor().getEnabledModulesByClass(com.atlassian.plugin.mock.MockBear.class).isEmpty());
416 assertTrue(getPluginAccessor().getEnabledModuleDescriptorsByClass(MockAnimalModuleDescriptor.class).isEmpty());
417 assertTrue(getPluginAccessor().getEnabledModuleDescriptorsByType("animal").isEmpty());
418
419
420 pluginEventManager.register(new FailListener(PluginDisabledEvent.class));
421 manager.enablePluginModule(moduleKey);
422 assertNotNull(getPluginAccessor().getPlugin(pluginKey));
423 assertNotNull(getPluginAccessor().getEnabledPlugin(pluginKey));
424 assertNotNull(getPluginAccessor().getPluginModule(moduleKey));
425 assertNotNull(getPluginAccessor().getEnabledPluginModule(moduleKey));
426 assertFalse(getPluginAccessor().getEnabledModulesByClass(com.atlassian.plugin.mock.MockBear.class).isEmpty());
427 assertFalse(getPluginAccessor().getEnabledModuleDescriptorsByClass(MockAnimalModuleDescriptor.class).isEmpty());
428 assertFalse(getPluginAccessor().getEnabledModuleDescriptorsByType("animal").isEmpty());
429 }
430
431 @Test
432 public void testDuplicatePluginKeysAreIgnored() throws PluginParseException
433 {
434 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
435
436 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"), new SinglePluginLoader("test-atlassian-plugin.xml"));
437 manager.init();
438 assertThat(getPluginAccessor().getEnabledPlugins(), hasSize(1));
439 }
440
441 @Test
442 public void testDuplicateSnapshotVersionsAreNotLoaded() throws PluginParseException
443 {
444 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
445
446 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-snapshot-plugin.xml"), new SinglePluginLoader("test-atlassian-snapshot-plugin.xml"));
447 manager.init();
448 assertThat(getPluginAccessor().getEnabledPlugins(), hasSize(1));
449 }
450
451 @Test
452 public void testChangedSnapshotVersionIsLoaded() throws PluginParseException
453 {
454 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
455
456 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-snapshot-plugin.xml"), new SinglePluginLoader("test-atlassian-snapshot-plugin-changed-same-version.xml"));
457 manager.init();
458 assertThat(getPluginAccessor().getEnabledPlugins(), hasSize(1));
459 final Plugin plugin = getPluginAccessor().getPlugin("test.atlassian.plugin");
460 assertEquals("1.1-SNAPSHOT", plugin.getPluginInformation().getVersion());
461 assertEquals("This plugin descriptor has been changed!", plugin.getPluginInformation().getDescription());
462 }
463
464 @Test
465 public void testLoadOlderDuplicatePlugin()
466 {
467 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
468 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
469
470 manager = newDefaultPluginManager(new MultiplePluginLoader("test-atlassian-plugin-newer.xml"), new MultiplePluginLoader("test-atlassian-plugin.xml", "test-another-plugin.xml"));
471 manager.init();
472 assertThat(getPluginAccessor().getEnabledPlugins(), hasSize(2));
473 }
474
475 @Test
476 public void testLoadOlderDuplicatePluginDoesNotTryToEnableIt()
477 {
478 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
479 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
480
481 final Plugin plugin = new StaticPlugin()
482 {
483 @Override
484 protected PluginState enableInternal()
485 {
486 fail("enable() must never be called on a earlier version of plugin when later version is installed");
487 return null;
488 }
489
490 @Override
491 public void disableInternal()
492 {
493 fail("disable() must never be called on a earlier version of plugin when later version is installed");
494 }
495 };
496 plugin.setKey("test.atlassian.plugin");
497 plugin.getPluginInformation().setVersion("1.0");
498
499 manager = newDefaultPluginManager(new MultiplePluginLoader("test-atlassian-plugin-newer.xml"));
500 manager.init();
501 manager.addPlugins(null, Collections.singletonList(plugin));
502 }
503
504 @Test
505 public void testLoadNewerDuplicatePlugin()
506 {
507 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
508 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
509
510 manager = newDefaultPluginManager(
511 new SinglePluginLoader("test-atlassian-plugin.xml"),
512 new SinglePluginLoader("test-atlassian-plugin-newer.xml"));
513 manager.init();
514 assertThat(getPluginAccessor().getEnabledPlugins(), hasSize(1));
515 final Plugin plugin = getPluginAccessor().getPlugin("test.atlassian.plugin");
516 assertEquals("1.1", plugin.getPluginInformation().getVersion());
517 }
518
519 @Test
520 public void testLoadNewerDuplicateDynamicPluginPreservesPluginState() throws PluginParseException
521 {
522 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
523 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
524
525 manager = newDefaultPluginManager(new SinglePluginLoaderWithRemoval("test-atlassian-plugin.xml"));
526 manager.init();
527
528 pluginStateStore.save(PluginPersistentState.Builder.create(pluginStateStore.load()).setEnabled(manager.getPlugin("test.atlassian.plugin"),
529 false).toState());
530
531 assertFalse(getPluginAccessor().isPluginEnabled("test.atlassian.plugin"));
532 manager.shutdown();
533
534 manager = newDefaultPluginManager(new SinglePluginLoaderWithRemoval("test-atlassian-plugin-newer.xml"));
535 manager.init();
536
537 final Plugin plugin = getPluginAccessor().getPlugin("test.atlassian.plugin");
538 assertEquals("1.1", plugin.getPluginInformation().getVersion());
539 assertFalse(getPluginAccessor().isPluginEnabled("test.atlassian.plugin"));
540 }
541
542 @Test
543 public void testLoadNewerDuplicateDynamicPluginPreservesModuleState() throws PluginParseException
544 {
545 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
546 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
547
548 manager = newDefaultPluginManager(new SinglePluginLoaderWithRemoval("test-atlassian-plugin.xml"));
549 manager.init();
550
551 pluginStateStore.save(PluginPersistentState.Builder.create(pluginStateStore.load()).setEnabled(
552 getPluginAccessor().getPluginModule("test.atlassian.plugin:bear"), false).toState());
553
554 manager.shutdown();
555
556 manager = newDefaultPluginManager(new SinglePluginLoaderWithRemoval("test-atlassian-plugin-newer.xml"));
557 manager.init();
558
559 final Plugin plugin = getPluginAccessor().getPlugin("test.atlassian.plugin");
560 assertEquals("1.1", plugin.getPluginInformation().getVersion());
561 assertFalse(getPluginAccessor().isPluginModuleEnabled("test.atlassian.plugin:bear"));
562 assertTrue(getPluginAccessor().isPluginModuleEnabled("test.atlassian.plugin:gold"));
563 }
564
565 @Test
566 public void testLoadChangedDynamicPluginWithSameVersionNumberDoesNotReplaceExisting() throws PluginParseException
567 {
568 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
569 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
570
571 manager = newDefaultPluginManager(
572 new SinglePluginLoaderWithRemoval("test-atlassian-plugin.xml"),
573 new SinglePluginLoaderWithRemoval("test-atlassian-plugin-changed-same-version.xml"));
574 manager.init();
575
576 final Plugin plugin = getPluginAccessor().getPlugin("test.atlassian.plugin");
577 assertEquals("Test Plugin", plugin.getName());
578 }
579
580 @Test
581 public void testGetPluginsWithPluginMatchingPluginPredicate() throws Exception
582 {
583 final Plugin plugin = mockTestPlugin(Collections.emptyList());
584
585 final PluginPredicate mockPluginPredicate = mock(PluginPredicate.class);
586 when(mockPluginPredicate.matches(plugin)).thenReturn(true);
587
588 manager = newDefaultPluginManager();
589 manager.addPlugins(null, Collections.singletonList(plugin));
590 final Collection<Plugin> plugins = getPluginAccessor().getPlugins(mockPluginPredicate);
591
592 assertThat(plugins, hasSize(1));
593 assertTrue(plugins.contains(plugin));
594 verify(mockPluginPredicate).matches(any(Plugin.class));
595 }
596
597 @Test
598 public void testGetPluginsWithPluginNotMatchingPluginPredicate() throws Exception
599 {
600 final Plugin plugin = mockTestPlugin(Collections.emptyList());
601
602 final PluginPredicate mockPluginPredicate = mock(PluginPredicate.class);
603
604 manager = newDefaultPluginManager();
605 manager.addPlugins(null, Collections.singletonList(plugin));
606 final Collection<Plugin> plugins = getPluginAccessor().getPlugins(mockPluginPredicate);
607
608 assertThat(plugins, hasSize(0));
609 }
610
611 @Test
612 public void testGetPluginModulesWithModuleMatchingPredicate() throws Exception
613 {
614 final MockThing module = new MockThing()
615 {
616 };
617 @SuppressWarnings("unchecked")
618 final ModuleDescriptor<MockThing> moduleDescriptor = mock(ModuleDescriptor.class);
619 when(moduleDescriptor.getModule()).thenReturn(module);
620 when(moduleDescriptor.getCompleteKey()).thenReturn("some-plugin-key:module");
621 when(moduleDescriptor.isEnabledByDefault()).thenReturn(true);
622
623 final Plugin plugin = mockTestPlugin(Collections.singleton(moduleDescriptor));
624 when(plugin.getModuleDescriptor("module")).thenReturn((ModuleDescriptor) moduleDescriptor);
625
626 final ModuleDescriptorPredicate mockModulePredicate = mock(ModuleDescriptorPredicate.class);
627 when(mockModulePredicate.matches(moduleDescriptor)).thenReturn(true);
628
629 manager = newDefaultPluginManager();
630 manager.addPlugins(null, Collections.singletonList(plugin));
631 @SuppressWarnings("unchecked")
632 final ModuleDescriptorPredicate<MockThing> predicate = (ModuleDescriptorPredicate<MockThing>) mockModulePredicate;
633 final Collection<MockThing> modules = getPluginAccessor().getModules(predicate);
634
635 assertThat(modules, hasSize(1));
636 assertTrue(modules.contains(module));
637
638 verify(mockModulePredicate).matches(moduleDescriptor);
639 }
640
641 @Test
642 public void testGetPluginModulesWithGetModuleThrowingException() throws Exception
643 {
644 final Plugin badPlugin = new StaticPlugin();
645 badPlugin.setKey("bad");
646 final MockModuleDescriptor<Object> badDescriptor = new MockModuleDescriptor<Object>(badPlugin, "bad", new Object())
647 {
648 @Override
649 public Object getModule()
650 {
651 throw new RuntimeException();
652 }
653 };
654 badPlugin.addModuleDescriptor(badDescriptor);
655
656 final Plugin goodPlugin = new StaticPlugin();
657 goodPlugin.setKey("good");
658 final MockModuleDescriptor<Object> goodDescriptor = new MockModuleDescriptor<Object>(goodPlugin, "good", new Object());
659 goodPlugin.addModuleDescriptor(goodDescriptor);
660
661 manager = newDefaultPluginManager();
662 manager.addPlugins(null, Arrays.asList(goodPlugin, badPlugin));
663 manager.enablePlugin("bad");
664 manager.enablePlugin("good");
665
666 assertTrue(getPluginAccessor().isPluginEnabled("bad"));
667 assertTrue(getPluginAccessor().isPluginEnabled("good"));
668 final Collection<Object> modules = getPluginAccessor().getEnabledModulesByClass(Object.class);
669
670 assertThat(modules, hasSize(1));
671 assertFalse(getPluginAccessor().isPluginEnabled("bad"));
672 assertTrue(getPluginAccessor().isPluginEnabled("good"));
673 }
674
675 @Test
676 public void testGetPluginModulesWith2GetModulesThrowingExceptionOnlyNotifiesOnce() throws Exception
677 {
678 final Plugin badPlugin = new StaticPlugin();
679 badPlugin.setKey("bad");
680 final MockModuleDescriptor<Object> badDescriptor = new MockModuleDescriptor<Object>(badPlugin, "bad", new Object())
681 {
682 @Override
683 public Object getModule()
684 {
685 throw new RuntimeException();
686 }
687 };
688 badPlugin.addModuleDescriptor(badDescriptor);
689 final MockModuleDescriptor<Object> badDescriptor2 = new MockModuleDescriptor<Object>(badPlugin, "bad2", new Object())
690 {
691 @Override
692 public Object getModule()
693 {
694 throw new RuntimeException();
695 }
696 };
697 badPlugin.addModuleDescriptor(badDescriptor2);
698
699 final Plugin goodPlugin = new StaticPlugin();
700 goodPlugin.setKey("good");
701 final MockModuleDescriptor<Object> goodDescriptor = new MockModuleDescriptor<Object>(goodPlugin, "good", new Object());
702 goodPlugin.addModuleDescriptor(goodDescriptor);
703 DisabledPluginCounter counter = new DisabledPluginCounter();
704 pluginEventManager.register(counter);
705
706 manager = newDefaultPluginManager();
707 manager.addPlugins(null, Arrays.asList(goodPlugin, badPlugin));
708 manager.enablePlugin("bad");
709 manager.enablePlugin("good");
710
711 assertTrue(getPluginAccessor().isPluginEnabled("bad"));
712 assertTrue(getPluginAccessor().isPluginEnabled("good"));
713 final Collection<Object> modules = getPluginAccessor().getEnabledModulesByClass(Object.class);
714
715 assertThat(modules, hasSize(1));
716 assertFalse(getPluginAccessor().isPluginEnabled("bad"));
717 assertTrue(getPluginAccessor().isPluginEnabled("good"));
718 assertEquals(1, counter.disableCount);
719 }
720
721 public static class DisabledPluginCounter
722 {
723 int disableCount = 0;
724 @PluginEventListener
725 public void consume(PluginDisabledEvent element)
726 {
727 disableCount++;
728 }
729 }
730
731 @Test
732 public void testGetPluginModulesWithModuleNotMatchingPredicate() throws Exception
733 {
734 @SuppressWarnings("unchecked")
735 final ModuleDescriptor<MockThing> moduleDescriptor = mock(ModuleDescriptor.class);
736 when(moduleDescriptor.getCompleteKey()).thenReturn("some-plugin-key:module");
737 when(moduleDescriptor.isEnabledByDefault()).thenReturn(true);
738
739 final Plugin plugin = mockTestPlugin(Collections.singleton(moduleDescriptor));
740 when(plugin.getModuleDescriptor("module")).thenReturn((ModuleDescriptor) moduleDescriptor);
741
742 @SuppressWarnings("unchecked")
743 final ModuleDescriptorPredicate<MockThing> predicate = mock(ModuleDescriptorPredicate.class);
744
745 manager = newDefaultPluginManager();
746 manager.addPlugins(null, Collections.singletonList(plugin));
747 final Collection<MockThing> modules = getPluginAccessor().getModules(predicate);
748
749 assertThat(modules, hasSize(0));
750
751 verify(predicate).matches(moduleDescriptor);
752 }
753
754 @Test
755 public void testGetPluginModuleDescriptorWithModuleMatchingPredicate() throws Exception
756 {
757 @SuppressWarnings("unchecked")
758 final ModuleDescriptor<MockThing> moduleDescriptor = mock(ModuleDescriptor.class);
759 when(moduleDescriptor.getCompleteKey()).thenReturn("some-plugin-key:module");
760 when(moduleDescriptor.isEnabledByDefault()).thenReturn(true);
761
762 final Plugin plugin = mockTestPlugin(Collections.singleton(moduleDescriptor));
763 when(plugin.getModuleDescriptor("module")).thenReturn((ModuleDescriptor) moduleDescriptor);
764
765 @SuppressWarnings("unchecked")
766 final ModuleDescriptorPredicate<MockThing> predicate = mock(ModuleDescriptorPredicate.class);
767 when(predicate.matches(moduleDescriptor)).thenReturn(true);
768
769 manager = newDefaultPluginManager();
770 manager.addPlugins(null, Collections.singletonList(plugin));
771 final Collection<ModuleDescriptor<MockThing>> modules = getPluginAccessor().getModuleDescriptors(predicate);
772
773 assertThat(modules, hasSize(1));
774 assertTrue(modules.contains(moduleDescriptor));
775
776 verify(predicate).matches(moduleDescriptor);
777 }
778
779 @Test
780 public void testGetPluginModuleDescriptorsWithModuleNotMatchingPredicate() throws Exception
781 {
782 @SuppressWarnings("unchecked")
783 final ModuleDescriptor<MockThing> moduleDescriptor = mock(ModuleDescriptor.class);
784 when(moduleDescriptor.getCompleteKey()).thenReturn("some-plugin-key:module");
785 when(moduleDescriptor.isEnabledByDefault()).thenReturn(true);
786
787 final Plugin plugin = mockTestPlugin(Collections.singleton(moduleDescriptor));
788 when(plugin.getModuleDescriptor("module")).thenReturn((ModuleDescriptor) moduleDescriptor);
789
790 @SuppressWarnings("unchecked")
791 final ModuleDescriptorPredicate<MockThing> predicate = mock(ModuleDescriptorPredicate.class);
792
793 manager = newDefaultPluginManager();
794 manager.addPlugins(null, Collections.singletonList(plugin));
795 final Collection<MockThing> modules = getPluginAccessor().getModules(predicate);
796
797 assertThat(modules, hasSize(0));
798
799 verify(predicate).matches(moduleDescriptor);
800 }
801
802 private Plugin mockTestPlugin(Collection moduleDescriptors)
803 {
804 final Plugin mockPlugin = mock(Plugin.class);
805 when(mockPlugin.getKey()).thenReturn("some-plugin-key");
806 when(mockPlugin.isEnabledByDefault()).thenReturn(true);
807 when(mockPlugin.isEnabled()).thenReturn(true);
808 when(mockPlugin.getPluginState()).thenReturn(PluginState.ENABLED);
809 when(mockPlugin.getModuleDescriptors()).thenReturn(moduleDescriptors);
810 return mockPlugin;
811 }
812
813 @Test
814 public void testGetPluginAndModules() throws PluginParseException
815 {
816 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
817 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
818
819 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
820 manager.init();
821
822 final Plugin plugin = manager.getPlugin("test.atlassian.plugin");
823 assertNotNull(plugin);
824 assertEquals("Test Plugin", plugin.getName());
825
826 final ModuleDescriptor<?> bear = plugin.getModuleDescriptor("bear");
827 assertEquals(bear, getPluginAccessor().getPluginModule("test.atlassian.plugin:bear"));
828 }
829
830 @Test
831 public void testGetModuleByModuleClassOneFound() throws PluginParseException
832 {
833 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
834 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
835
836 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
837 manager.init();
838
839 final List<MockAnimalModuleDescriptor> animalDescriptors = getPluginAccessor().getEnabledModuleDescriptorsByClass(MockAnimalModuleDescriptor.class);
840 assertNotNull(animalDescriptors);
841 assertThat(animalDescriptors, hasSize(1));
842 final ModuleDescriptor<MockAnimal> moduleDescriptor = animalDescriptors.iterator().next();
843 assertEquals("Bear Animal", moduleDescriptor.getName());
844
845 final List<MockMineralModuleDescriptor> mineralDescriptors = getPluginAccessor().getEnabledModuleDescriptorsByClass(MockMineralModuleDescriptor.class);
846 assertNotNull(mineralDescriptors);
847 assertThat(mineralDescriptors, hasSize(1));
848 final ModuleDescriptor<MockMineral> mineralDescriptor = mineralDescriptors.iterator().next();
849 assertEquals("Bar", mineralDescriptor.getName());
850 }
851
852 @Test
853 public void testGetModuleByModuleClassAndDescriptor() throws PluginParseException
854 {
855 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
856 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
857
858 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
859 manager.init();
860
861 final Collection<MockBear> bearModules = getPluginAccessor().getEnabledModulesByClassAndDescriptor(
862 new Class[]{MockAnimalModuleDescriptor.class, MockMineralModuleDescriptor.class}, MockBear.class);
863 assertNotNull(bearModules);
864 assertThat(bearModules, hasSize(1));
865 assertTrue(bearModules.iterator().next() instanceof MockBear);
866
867 final Collection<MockBear> noModules = getPluginAccessor().getEnabledModulesByClassAndDescriptor(new Class[]{}, MockBear.class);
868 assertNotNull(noModules);
869 assertThat(noModules, hasSize(0));
870
871 final Collection<MockThing> mockThings = getPluginAccessor().getEnabledModulesByClassAndDescriptor(
872 new Class[]{MockAnimalModuleDescriptor.class, MockMineralModuleDescriptor.class}, MockThing.class);
873 assertNotNull(mockThings);
874 assertThat(mockThings, hasSize(2));
875 assertTrue(mockThings.iterator().next() instanceof MockThing);
876 assertTrue(mockThings.iterator().next() instanceof MockThing);
877
878 final Collection<MockThing> mockThingsFromMineral = getPluginAccessor().getEnabledModulesByClassAndDescriptor(
879 new Class[]{MockMineralModuleDescriptor.class}, MockThing.class);
880 assertNotNull(mockThingsFromMineral);
881 assertThat(mockThingsFromMineral, hasSize(1));
882 final Object o = mockThingsFromMineral.iterator().next();
883 assertTrue(o instanceof MockMineral);
884 }
885
886 @Test
887 public void testGetModuleByModuleClassNoneFound() throws PluginParseException
888 {
889 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
890 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
891
892 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
893 manager.init();
894
895 class MockSilver implements MockMineral
896 {
897 public int getWeight()
898 {
899 return 3;
900 }
901 }
902
903 final Collection<MockSilver> descriptors = getPluginAccessor().getEnabledModulesByClass(MockSilver.class);
904 assertNotNull(descriptors);
905 assertTrue(descriptors.isEmpty());
906 }
907
908 @Test
909 public void testGetModuleDescriptorsByType() throws PluginParseException
910 {
911 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
912 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
913
914 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
915 manager.init();
916
917 Collection<ModuleDescriptor<MockThing>> descriptors = getPluginAccessor().getEnabledModuleDescriptorsByType("animal");
918 assertNotNull(descriptors);
919 assertThat(descriptors, hasSize(1));
920 ModuleDescriptor<MockThing> moduleDescriptor = descriptors.iterator().next();
921 assertEquals("Bear Animal", moduleDescriptor.getName());
922
923 descriptors = getPluginAccessor().getEnabledModuleDescriptorsByType("mineral");
924 assertNotNull(descriptors);
925 assertThat(descriptors, hasSize(1));
926 moduleDescriptor = descriptors.iterator().next();
927 assertEquals("Bar", moduleDescriptor.getName());
928
929 try
930 {
931 getPluginAccessor().getEnabledModuleDescriptorsByType("foobar");
932 }
933 catch (final IllegalArgumentException e)
934 {
935 fail("Shouldn't have thrown exception.");
936 }
937 }
938
939 @Test
940 public void testRetrievingDynamicResources() throws PluginParseException, IOException
941 {
942 createFillAndCleanTempPluginDirectory();
943
944 final DefaultPluginManager manager = makeClassLoadingPluginManager();
945
946 final InputStream is = manager.getPluginResourceAsStream("test.atlassian.plugin.classloaded", "atlassian-plugin.xml");
947 assertNotNull(is);
948 IOUtils.closeQuietly(is);
949 }
950
951 @Test
952 public void testGetDynamicPluginClass() throws IOException, PluginParseException
953 {
954 createFillAndCleanTempPluginDirectory();
955
956 final DefaultPluginManager manager = makeClassLoadingPluginManager();
957 try
958 {
959 manager.getDynamicPluginClass("com.atlassian.plugin.mock.MockPooh");
960 }
961 catch (final ClassNotFoundException e)
962 {
963 fail(e.getMessage());
964 }
965 }
966
967 @Test
968 public void testGetEnabledPluginsDoesNotReturnEnablingPlugins() throws Exception
969 {
970 final PluginLoader mockPluginLoader = mock(PluginLoader.class);
971
972 final Plugin firstPlugin = new StaticPlugin();
973 firstPlugin.setKey("first");
974 firstPlugin.setEnabledByDefault(false);
975 firstPlugin.setPluginInformation(new PluginInformation());
976
977 manager = newDefaultPluginManager();
978 manager.enablePluginState(firstPlugin, pluginStateStore);
979
980 final Plugin secondPlugin = new StaticPlugin()
981 {
982 public PluginState enableInternal()
983 {
984 try
985 {
986
987 assertThat(manager.getPlugins(), hasSize(2));
988 assertThat("First plugin should not be enabled", manager.getEnabledPlugins(), hasSize(0));
989 }
990 catch (Exception e)
991 {
992 throw new RuntimeException(e);
993 }
994 return PluginState.ENABLED;
995 }
996
997 public void disableInternal()
998 {
999
1000 }
1001 };
1002 secondPlugin.setKey("second");
1003 secondPlugin.setEnabledByDefault(false);
1004 secondPlugin.setPluginInformation(new PluginInformation());
1005 manager.enablePluginState(secondPlugin, pluginStateStore);
1006
1007 when(mockPluginLoader.loadAllPlugins(any(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(firstPlugin, secondPlugin));
1008
1009 manager = newDefaultPluginManager(mockPluginLoader);
1010 manager.init();
1011 }
1012
1013 @Test
1014 public void testFindingNewPlugins() throws PluginParseException, IOException
1015 {
1016 createFillAndCleanTempPluginDirectory();
1017
1018
1019 final File paddington = new File(pluginsTestDir, PADDINGTON_JAR);
1020 paddington.delete();
1021
1022 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1023
1024 assertThat(manager.getPlugins(), hasSize(1));
1025 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded2"));
1026
1027
1028 FileUtils.copyDirectory(pluginsDirectory, pluginsTestDir);
1029
1030 manager.scanForNewPlugins();
1031 assertThat(manager.getPlugins(), hasSize(2));
1032 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded2"));
1033 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded"));
1034
1035 manager.scanForNewPlugins();
1036 assertThat(manager.getPlugins(), hasSize(2));
1037 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded2"));
1038 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded"));
1039 }
1040
1041 @Test
1042 public void testFindingNewPluginsNotLoadingRestartRequiredDescriptors() throws PluginParseException, IOException
1043 {
1044 createFillAndCleanTempPluginDirectory();
1045
1046 final DynamicSinglePluginLoader dynamicSinglePluginLoader = new DynamicSinglePluginLoader("test.atlassian.plugin", "test-requiresRestart-plugin.xml");
1047 manager = makeClassLoadingPluginManager(dynamicSinglePluginLoader);
1048
1049 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1050
1051 assertThat(manager.getPlugins(), hasSize(2));
1052 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded2"));
1053
1054
1055 dynamicSinglePluginLoader.canLoad.set(true);
1056
1057 manager.scanForNewPlugins();
1058 assertThat(manager.getPlugins(), hasSize(3));
1059 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded2"));
1060 assertNotNull(manager.getPlugin("test.atlassian.plugin"));
1061
1062 final Plugin plugin = manager.getPlugin("test.atlassian.plugin");
1063 assertTrue(plugin instanceof UnloadablePlugin);
1064 assertTrue(((UnloadablePlugin)plugin).getErrorText().contains("foo"));
1065
1066 assertEquals(PluginRestartState.INSTALL, manager.getPluginRestartState("test.atlassian.plugin"));
1067 }
1068
1069
1070
1071
1072 @Test
1073 public void testFindingUpgradePluginsNotLoadingRestartRequiredDescriptors() throws PluginParseException, IOException
1074 {
1075 createFillAndCleanTempPluginDirectory();
1076
1077 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1078
1079 final DynamicSinglePluginLoader dynamicSinglePluginLoader = new DynamicSinglePluginLoader("test.atlassian.plugin.classloaded2", "test-requiresRestartWithUpgrade-plugin.xml");
1080 manager = makeClassLoadingPluginManager(dynamicSinglePluginLoader);
1081
1082 assertThat(manager.getPlugins(), hasSize(2));
1083 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded2"));
1084 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1085
1086
1087 dynamicSinglePluginLoader.canLoad.set(true);
1088
1089 manager.scanForNewPlugins();
1090 assertThat(manager.getPlugins(), hasSize(2));
1091 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded2"));
1092 assertEquals(PluginRestartState.UPGRADE, manager.getPluginRestartState("test.atlassian.plugin.classloaded2"));
1093 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1094 }
1095
1096 @Test
1097 public void testInstallPluginThatRequiresRestart() throws PluginParseException, IOException, InterruptedException
1098 {
1099 createFillAndCleanTempPluginDirectory();
1100 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1101 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1102 assertThat(manager.getPlugins(), hasSize(2));
1103
1104 new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1105 "<atlassian-plugin name='Test 2' i18n-name-key='test.name' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1106 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1107 manager.scanForNewPlugins();
1108
1109 assertThat(manager.getPlugins(), hasSize(3));
1110 Plugin plugin = manager.getPlugin("test.restartrequired");
1111 assertNotNull(plugin);
1112 assertEquals("Test 2", plugin.getName());
1113 assertEquals("test.name", plugin.getI18nNameKey());
1114 assertEquals(1, plugin.getPluginsVersion());
1115 assertEquals("1.0", plugin.getPluginInformation().getVersion());
1116 assertFalse(manager.isPluginEnabled("test.restartrequired"));
1117 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1118 assertEquals(PluginRestartState.INSTALL, manager.getPluginRestartState("test.restartrequired"));
1119
1120 manager.shutdown();
1121 manager.init();
1122
1123 assertThat(manager.getPlugins(), hasSize(3));
1124 assertNotNull(plugin);
1125 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1126 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1127 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1128 }
1129
1130 @Test
1131 public void testInstallPluginThatRequiresRestartThenRevert() throws PluginParseException, IOException, InterruptedException
1132 {
1133 createFillAndCleanTempPluginDirectory();
1134 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1135 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1136 manager.setPluginInstaller(new FilePluginInstaller(pluginsTestDir));
1137 assertThat(manager.getPlugins(), hasSize(2));
1138
1139 File pluginJar = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1140 "<atlassian-plugin name='Test 2' i18n-name-key='test.name' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1141 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build();
1142 manager.installPlugin(new JarPluginArtifact(pluginJar));
1143
1144 assertThat(manager.getPlugins(), hasSize(3));
1145 Plugin plugin = manager.getPlugin("test.restartrequired");
1146 assertNotNull(plugin);
1147 assertEquals("Test 2", plugin.getName());
1148 assertEquals("test.name", plugin.getI18nNameKey());
1149 assertEquals(1, plugin.getPluginsVersion());
1150 assertEquals("1.0", plugin.getPluginInformation().getVersion());
1151 assertFalse(manager.isPluginEnabled("test.restartrequired"));
1152 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1153 assertEquals(PluginRestartState.INSTALL, manager.getPluginRestartState("test.restartrequired"));
1154
1155 manager.revertRestartRequiredChange("test.restartrequired");
1156 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1157
1158 manager.shutdown();
1159 manager.init();
1160
1161 assertThat(manager.getPlugins(), hasSize(2));
1162 assertFalse(manager.isPluginEnabled("test.restartrequired"));
1163 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1164 }
1165
1166 @Test
1167 public void testUpgradePluginThatRequiresRestart() throws PluginParseException, IOException, InterruptedException
1168 {
1169 createFillAndCleanTempPluginDirectory();
1170 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1171
1172 final File origFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1173 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1174 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1175
1176 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1177
1178 assertThat(manager.getPlugins(), hasSize(3));
1179 assertNotNull(manager.getPlugin("test.restartrequired"));
1180 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1181 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1182 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1183
1184
1185 Thread.sleep(1000);
1186 final File updateFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1187 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>2.0</version>",
1188 " </plugin-info>", " <requiresRestart key='foo' />", " <requiresRestart key='bar' />", "</atlassian-plugin>").build();
1189
1190 origFile.delete();
1191 FileUtils.moveFile(updateFile, origFile);
1192
1193 manager.scanForNewPlugins();
1194 assertThat(manager.getPlugins(), hasSize(3));
1195 assertNotNull(manager.getPlugin("test.restartrequired"));
1196 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1197 assertEquals(PluginRestartState.UPGRADE, manager.getPluginRestartState("test.restartrequired"));
1198 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1199
1200 manager.shutdown();
1201 manager.init();
1202
1203 assertThat(manager.getPlugins(), hasSize(3));
1204 assertNotNull(manager.getPlugin("test.restartrequired"));
1205 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1206 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(2));
1207 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1208 }
1209
1210 @Test
1211 public void testUpgradePluginThatRequiresRestartThenReverted() throws PluginParseException, IOException, InterruptedException
1212 {
1213 createFillAndCleanTempPluginDirectory();
1214 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1215
1216 new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1217 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>",
1218 " <plugin-info>",
1219 " <version>1.0</version>",
1220 " </plugin-info>",
1221 " <requiresRestart key='foo' />",
1222 "</atlassian-plugin>")
1223 .build(pluginsTestDir);
1224
1225 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1226 manager.setPluginInstaller(new FilePluginInstaller(pluginsTestDir));
1227
1228 assertThat(manager.getPlugins(), hasSize(3));
1229 assertNotNull(manager.getPlugin("test.restartrequired"));
1230 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1231 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1232 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1233
1234
1235 Thread.sleep(1000);
1236 final File updateFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1237 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>2.0</version>",
1238 " </plugin-info>", " <requiresRestart key='foo' />", " <requiresRestart key='bar' />", "</atlassian-plugin>").build();
1239
1240 manager.installPlugin(new JarPluginArtifact(updateFile));
1241
1242 assertThat(manager.getPlugins(), hasSize(3));
1243 assertNotNull(manager.getPlugin("test.restartrequired"));
1244 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1245 assertEquals(PluginRestartState.UPGRADE, manager.getPluginRestartState("test.restartrequired"));
1246 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1247
1248 manager.revertRestartRequiredChange("test.restartrequired");
1249 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1250
1251 manager.shutdown();
1252 manager.init();
1253
1254 assertThat(manager.getPlugins(), hasSize(3));
1255 assertNotNull(manager.getPlugin("test.restartrequired"));
1256 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1257 assertEquals("1.0", manager.getPlugin("test.restartrequired").getPluginInformation().getVersion());
1258 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1259 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1260 }
1261
1262 @Test
1263 public void testUpgradePluginThatRequiresRestartThenRevertedRevertsToOriginalPlugin() throws PluginParseException, IOException, InterruptedException
1264 {
1265 createFillAndCleanTempPluginDirectory();
1266 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1267
1268
1269 new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1270 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1271 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1272
1273 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1274 manager.setPluginInstaller(new FilePluginInstaller(pluginsTestDir));
1275
1276 assertThat(manager.getPlugins(), hasSize(3));
1277 assertNotNull(manager.getPlugin("test.restartrequired"));
1278 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1279 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1280 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1281
1282
1283 Thread.sleep(1000);
1284 final File updateFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1285 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>2.0</version>",
1286 " </plugin-info>", " <requiresRestart key='foo' />", " <requiresRestart key='bar' />", "</atlassian-plugin>").build();
1287
1288
1289 manager.installPlugin(new JarPluginArtifact(updateFile));
1290
1291 assertThat(manager.getPlugins(), hasSize(3));
1292 assertNotNull(manager.getPlugin("test.restartrequired"));
1293 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1294 assertEquals(PluginRestartState.UPGRADE, manager.getPluginRestartState("test.restartrequired"));
1295 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1296
1297 Thread.sleep(1000);
1298 final File updateFile2 = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1299 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>3.0</version>",
1300 " </plugin-info>", " <requiresRestart key='foo' />", " <requiresRestart key='bar' />", "</atlassian-plugin>").build();
1301
1302
1303 manager.installPlugin(new JarPluginArtifact(updateFile2));
1304
1305 assertThat(manager.getPlugins(), hasSize(3));
1306 assertNotNull(manager.getPlugin("test.restartrequired"));
1307 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1308 assertEquals(PluginRestartState.UPGRADE, manager.getPluginRestartState("test.restartrequired"));
1309 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1310
1311
1312 manager.revertRestartRequiredChange("test.restartrequired");
1313 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1314 assertEquals("1.0", manager.getPlugin("test.restartrequired").getPluginInformation().getVersion());
1315
1316 manager.shutdown();
1317 manager.init();
1318
1319 assertThat(manager.getPlugins(), hasSize(3));
1320 assertNotNull(manager.getPlugin("test.restartrequired"));
1321 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1322 assertEquals("1.0", manager.getPlugin("test.restartrequired").getPluginInformation().getVersion());
1323 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1324 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1325 }
1326
1327 @Test
1328 public void testUpgradePluginThatRequiresRestartMultipleTimeStaysUpgraded() throws PluginParseException, IOException, InterruptedException
1329 {
1330 createFillAndCleanTempPluginDirectory();
1331 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1332
1333 final File origFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1334 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1335 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1336
1337 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1338 manager.setPluginInstaller(new FilePluginInstaller(pluginsTestDir));
1339
1340 assertThat(manager.getPlugins(), hasSize(3));
1341 assertNotNull(manager.getPlugin("test.restartrequired"));
1342 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1343 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1344 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1345
1346
1347 Thread.sleep(1000);
1348 final File updateFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1349 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>2.0</version>",
1350 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build();
1351
1352 manager.installPlugin(new JarPluginArtifact(updateFile));
1353
1354 assertThat(manager.getPlugins(), hasSize(3));
1355 assertNotNull(manager.getPlugin("test.restartrequired"));
1356 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1357 assertEquals(PluginRestartState.UPGRADE, manager.getPluginRestartState("test.restartrequired"));
1358 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1359
1360 Thread.sleep(1000);
1361 final File updateFile2 = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1362 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>3.0</version>",
1363 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build();
1364
1365 manager.installPlugin(new JarPluginArtifact(updateFile2));
1366
1367 assertThat(manager.getPlugins(), hasSize(3));
1368 assertNotNull(manager.getPlugin("test.restartrequired"));
1369 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1370 assertEquals(PluginRestartState.UPGRADE, manager.getPluginRestartState("test.restartrequired"));
1371 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1372
1373 manager.shutdown();
1374 manager.init();
1375
1376 assertThat(manager.getPlugins(), hasSize(3));
1377 assertNotNull(manager.getPlugin("test.restartrequired"));
1378 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1379 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1380 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1381 }
1382
1383 @Test
1384 public void testUpgradePluginThatPreviouslyRequiredRestart() throws PluginParseException, IOException, InterruptedException
1385 {
1386 createFillAndCleanTempPluginDirectory();
1387 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1388
1389 final File origFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1390 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1391 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1392
1393 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1394
1395 assertThat(manager.getPlugins(), hasSize(3));
1396 assertNotNull(manager.getPlugin("test.restartrequired"));
1397 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1398 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1399 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1400
1401
1402 Thread.sleep(1000);
1403 final File updateFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1404 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>2.0</version>",
1405 " </plugin-info>", "</atlassian-plugin>").build(pluginsTestDir);
1406
1407 origFile.delete();
1408 FileUtils.moveFile(updateFile, origFile);
1409
1410 manager.scanForNewPlugins();
1411 assertThat(manager.getPlugins(), hasSize(3));
1412 assertNotNull(manager.getPlugin("test.restartrequired"));
1413 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1414 assertEquals(PluginRestartState.UPGRADE, manager.getPluginRestartState("test.restartrequired"));
1415 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1416
1417 manager.shutdown();
1418 manager.init();
1419
1420 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1421 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1422 }
1423
1424 @Test
1425 public void testInstallPluginThatPreviouslyRequiredRestart() throws PluginParseException, IOException, InterruptedException
1426 {
1427 createFillAndCleanTempPluginDirectory();
1428 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1429
1430 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1431
1432 assertThat(manager.getPlugins(), hasSize(2));
1433 assertNull(manager.getPlugin("test.restartrequired"));
1434
1435 final File origFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1436 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1437 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1438
1439 manager.scanForNewPlugins();
1440 assertThat(manager.getPlugins(), hasSize(3));
1441 assertNotNull(manager.getPlugin("test.restartrequired"));
1442 assertFalse(manager.isPluginEnabled("test.restartrequired"));
1443 assertEquals(PluginRestartState.INSTALL, manager.getPluginRestartState("test.restartrequired"));
1444
1445
1446 Thread.sleep(1000);
1447 final File updateFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1448 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>2.0</version>",
1449 " </plugin-info>", "</atlassian-plugin>").build(pluginsTestDir);
1450
1451 origFile.delete();
1452 FileUtils.moveFile(updateFile, origFile);
1453
1454 manager.scanForNewPlugins();
1455 assertThat(manager.getPlugins(), hasSize(3));
1456 assertNotNull(manager.getPlugin("test.restartrequired"));
1457 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1458 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1459 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1460
1461 manager.shutdown();
1462 manager.init();
1463
1464 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1465 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1466 }
1467
1468 @Test
1469 public void testInstallPluginMoreThanOnceStaysAsInstall() throws PluginParseException, IOException, InterruptedException
1470 {
1471 createFillAndCleanTempPluginDirectory();
1472 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1473
1474 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1475
1476 assertThat(manager.getPlugins(), hasSize(2));
1477 assertNull(manager.getPlugin("test.restartrequired"));
1478 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1479
1480 final File origFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1481 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1482 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1483
1484 manager.scanForNewPlugins();
1485 assertThat(manager.getPlugins(), hasSize(3));
1486 assertNotNull(manager.getPlugin("test.restartrequired"));
1487 assertFalse(manager.isPluginEnabled("test.restartrequired"));
1488 assertEquals(PluginRestartState.INSTALL, manager.getPluginRestartState("test.restartrequired"));
1489
1490
1491 Thread.sleep(1000);
1492 final File updateFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1493 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>2.0</version>",
1494 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1495
1496 origFile.delete();
1497 FileUtils.moveFile(updateFile, origFile);
1498
1499 manager.scanForNewPlugins();
1500 assertThat(manager.getPlugins(), hasSize(3));
1501 assertNotNull(manager.getPlugin("test.restartrequired"));
1502 assertFalse(manager.isPluginEnabled("test.restartrequired"));
1503 assertEquals(PluginRestartState.INSTALL, manager.getPluginRestartState("test.restartrequired"));
1504
1505 manager.shutdown();
1506 manager.init();
1507
1508 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1509 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1510 }
1511
1512 @Test
1513 public void testRemovePluginThatRequiresRestart() throws PluginParseException, IOException
1514 {
1515 createFillAndCleanTempPluginDirectory();
1516 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1517
1518 final File pluginFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1519 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1520 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1521
1522 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1523
1524 assertThat(manager.getPlugins(), hasSize(3));
1525 assertNotNull(manager.getPlugin("test.restartrequired"));
1526 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1527 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1528 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1529
1530 manager.uninstall(manager.getPlugin("test.restartrequired"));
1531
1532 assertThat(manager.getPlugins(), hasSize(3));
1533 assertNotNull(manager.getPlugin("test.restartrequired"));
1534 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1535 assertEquals(PluginRestartState.REMOVE, manager.getPluginRestartState("test.restartrequired"));
1536 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1537
1538 manager.shutdown();
1539 manager.init();
1540
1541 assertFalse(pluginFile.exists());
1542 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1543 assertThat(manager.getPlugins(), hasSize(2));
1544 }
1545
1546 @Test
1547 public void testRemovePluginThatRequiresRestartThenReverted() throws PluginParseException, IOException
1548 {
1549 createFillAndCleanTempPluginDirectory();
1550 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1551
1552 final File pluginFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1553 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1554 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1555
1556 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1557
1558 assertThat(manager.getPlugins(), hasSize(3));
1559 assertNotNull(manager.getPlugin("test.restartrequired"));
1560 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1561 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1562 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1563
1564 manager.uninstall(manager.getPlugin("test.restartrequired"));
1565
1566 assertThat(manager.getPlugins(), hasSize(3));
1567 assertNotNull(manager.getPlugin("test.restartrequired"));
1568 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1569 assertEquals(PluginRestartState.REMOVE, manager.getPluginRestartState("test.restartrequired"));
1570 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1571
1572 manager.revertRestartRequiredChange("test.restartrequired");
1573 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1574
1575 manager.shutdown();
1576 manager.init();
1577
1578 assertThat(manager.getPlugins(), hasSize(3));
1579 assertNotNull(manager.getPlugin("test.restartrequired"));
1580 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1581 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1582 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1583 }
1584
1585 @Test
1586 public void testRemovePluginThatRequiresRestartViaSubclass() throws PluginParseException, IOException
1587 {
1588 createFillAndCleanTempPluginDirectory();
1589 moduleDescriptorFactory.addModuleDescriptor("requiresRestartSubclass", RequiresRestartSubclassModuleDescriptor.class);
1590
1591 final File pluginFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1592 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1593 " </plugin-info>", " <requiresRestartSubclass key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1594
1595 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1596
1597 assertThat(manager.getPlugins(), hasSize(3));
1598 assertNotNull(manager.getPlugin("test.restartrequired"));
1599 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1600 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartSubclassModuleDescriptor.class), hasSize(1));
1601 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1602
1603 manager.uninstall(manager.getPlugin("test.restartrequired"));
1604
1605 assertThat(manager.getPlugins(), hasSize(3));
1606 assertNotNull(manager.getPlugin("test.restartrequired"));
1607 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1608 assertEquals(PluginRestartState.REMOVE, manager.getPluginRestartState("test.restartrequired"));
1609 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartSubclassModuleDescriptor.class), hasSize(1));
1610
1611 manager.shutdown();
1612 manager.init();
1613
1614 assertFalse(pluginFile.exists());
1615 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartSubclassModuleDescriptor.class), hasSize(0));
1616 assertThat(manager.getPlugins(), hasSize(2));
1617 }
1618
1619 @Test
1620 public void testDisableEnableOfPluginThatRequiresRestart() throws PluginParseException, IOException
1621 {
1622 createFillAndCleanTempPluginDirectory();
1623 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1624
1625 new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1626 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1627 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1628
1629 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1630
1631 assertThat(manager.getPlugins(), hasSize(3));
1632 assertNotNull(manager.getPlugin("test.restartrequired"));
1633 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1634 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1635 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1636
1637 manager.disablePlugin("test.restartrequired");
1638 assertFalse(manager.isPluginEnabled("test.restartrequired"));
1639 manager.enablePlugins("test.restartrequired");
1640
1641 assertThat(manager.getPlugins(), hasSize(3));
1642 assertNotNull(manager.getPlugin("test.restartrequired"));
1643 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1644 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1645 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1646 }
1647
1648 @Test
1649 public void testCannotRemovePluginFromStaticLoader() throws PluginParseException, IOException
1650 {
1651 createFillAndCleanTempPluginDirectory();
1652 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1653
1654 directoryPluginLoader = new DirectoryPluginLoader(
1655 pluginsTestDir,
1656 ImmutableList.<PluginFactory>of(new LegacyDynamicPluginFactory(PluginAccessor.Descriptor.FILENAME), new XmlDynamicPluginFactory(new MockApplication().setKey("key"))),
1657 pluginEventManager);
1658
1659 manager = newDefaultPluginManager(directoryPluginLoader, new SinglePluginLoader("test-requiresRestart-plugin.xml"));
1660 manager.init();
1661
1662 assertThat(getPluginAccessor().getPlugins(), hasSize(3));
1663 assertNotNull(getPluginAccessor().getPlugin("test.atlassian.plugin"));
1664 assertTrue(getPluginAccessor().isPluginEnabled("test.atlassian.plugin"));
1665 assertThat(getPluginAccessor().getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1666 assertEquals(PluginRestartState.NONE, getPluginAccessor().getPluginRestartState("test.atlassian.plugin"));
1667
1668 try
1669 {
1670 manager.uninstall(getPluginAccessor().getPlugin("test.atlassian.plugin"));
1671 fail();
1672 }
1673 catch (final PluginException ex)
1674 {
1675
1676 }
1677
1678 assertThat(getPluginAccessor().getPlugins(), hasSize(3));
1679 assertNotNull(getPluginAccessor().getPlugin("test.atlassian.plugin"));
1680 assertTrue(getPluginAccessor().isPluginEnabled("test.atlassian.plugin"));
1681 assertEquals(PluginRestartState.NONE, getPluginAccessor().getPluginRestartState("test.atlassian.plugin"));
1682 assertThat(getPluginAccessor().getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1683 }
1684
1685 private DefaultPluginManager makeClassLoadingPluginManager(PluginLoader... pluginLoaders) throws PluginParseException
1686 {
1687 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
1688
1689 directoryPluginLoader = new DirectoryPluginLoader(pluginsTestDir, ImmutableList.<PluginFactory>of(
1690 new LegacyDynamicPluginFactory(PluginAccessor.Descriptor.FILENAME),
1691 new XmlDynamicPluginFactory(new MockApplication().setKey("key"))), pluginEventManager);
1692
1693 final DefaultPluginManager manager = newDefaultPluginManager(Iterables.toArray(ImmutableList.<PluginLoader>builder().add(directoryPluginLoader).addAll(copyOf(pluginLoaders)).build(), PluginLoader.class));
1694
1695 manager.init();
1696 return manager;
1697 }
1698
1699 @Test
1700 public void testRemovingPlugins() throws PluginException, IOException
1701 {
1702 createFillAndCleanTempPluginDirectory();
1703
1704 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1705 assertThat(manager.getPlugins(), hasSize(2));
1706 final MockAnimalModuleDescriptor moduleDescriptor = (MockAnimalModuleDescriptor) manager.getPluginModule("test.atlassian.plugin.classloaded:paddington");
1707 assertFalse(moduleDescriptor.disabled);
1708 final PassListener disabledListener = new PassListener(PluginDisabledEvent.class);
1709 pluginEventManager.register(disabledListener);
1710 final Plugin plugin = manager.getPlugin("test.atlassian.plugin.classloaded");
1711 manager.uninstall(plugin);
1712 assertTrue("Module must have had disable() called before being removed", moduleDescriptor.disabled);
1713
1714
1715 assertTrue(pluginStateStore.load().getPluginStateMap(plugin).isEmpty());
1716
1717 assertThat(manager.getPlugins(), hasSize(1));
1718
1719 assertNull(manager.getPlugin("test.atlassian.plugin.classloaded"));
1720 assertEquals(1, pluginsTestDir.listFiles().length);
1721 disabledListener.assertCalled();
1722 }
1723
1724 @Test
1725 public void testPluginModuleAvailableAfterInstallation()
1726 {
1727 PluginLoader pluginLoader = mock(PluginLoader.class);
1728 when(pluginLoader.supportsRemoval()).thenReturn(true);
1729 Plugin plugin = mock(Plugin.class);
1730 when(plugin.getKey()).thenReturn("dynPlugin");
1731 when(plugin.isEnabledByDefault()).thenReturn(true);
1732 when(plugin.isDeleteable()).thenReturn(true);
1733 when(plugin.isUninstallable()).thenReturn(true);
1734 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
1735 when(plugin.compareTo(any(Plugin.class))).thenReturn(-1);
1736 when (pluginLoader.loadAllPlugins(any(ModuleDescriptorFactory.class))).thenReturn(asList(plugin));
1737
1738 manager = newDefaultPluginManager(pluginLoader);
1739 manager.init();
1740
1741 PluginModuleEnabledListener listener = new PluginModuleEnabledListener();
1742 pluginEventManager.register(listener);
1743 Collection<ModuleDescriptor<?>> mods = new ArrayList<ModuleDescriptor<?>>();
1744 MockModuleDescriptor<String> moduleDescriptor = new MockModuleDescriptor<String>(plugin, "foo", "foo");
1745 mods.add(moduleDescriptor);
1746 when(plugin.getModuleDescriptors()).thenReturn(mods);
1747 when(plugin.getModuleDescriptor("foo")).thenReturn((ModuleDescriptor)moduleDescriptor);
1748 pluginEventManager.broadcast(new PluginModuleAvailableEvent(moduleDescriptor));
1749
1750 assertTrue(getPluginAccessor().isPluginModuleEnabled("dynPlugin:foo"));
1751 assertTrue(listener.called);
1752 }
1753
1754 @Test
1755 public void testPluginModuleAvailableAfterInstallationButConfiguredToBeDisabled()
1756 {
1757 PluginLoader pluginLoader = mock(PluginLoader.class);
1758 when(pluginLoader.supportsRemoval()).thenReturn(true);
1759 Plugin plugin = mock(Plugin.class);
1760 when(plugin.getKey()).thenReturn("dynPlugin");
1761 when(plugin.isEnabledByDefault()).thenReturn(true);
1762 when(plugin.isDeleteable()).thenReturn(true);
1763 when(plugin.isUninstallable()).thenReturn(true);
1764 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
1765 when(plugin.compareTo(any(Plugin.class))).thenReturn(-1);
1766 when (pluginLoader.loadAllPlugins(any(ModuleDescriptorFactory.class))).thenReturn(asList(plugin));
1767
1768 manager = newDefaultPluginManager(pluginLoader);
1769 manager.init();
1770
1771 MockModuleDescriptor<String> moduleDescriptor = new MockModuleDescriptor<String>(plugin, "foo", "foo");
1772
1773 manager.disablePluginModuleState(moduleDescriptor, manager.getStore());
1774
1775 PluginModuleEnabledListener listener = new PluginModuleEnabledListener();
1776 pluginEventManager.register(listener);
1777 Collection<ModuleDescriptor<?>> mods = new ArrayList<ModuleDescriptor<?>>();
1778 mods.add(moduleDescriptor);
1779
1780 when(plugin.getModuleDescriptors()).thenReturn(mods);
1781 when(plugin.getModuleDescriptor("foo")).thenReturn((ModuleDescriptor)moduleDescriptor);
1782 pluginEventManager.broadcast(new PluginModuleAvailableEvent(moduleDescriptor));
1783
1784 assertFalse(getPluginAccessor().isPluginModuleEnabled("dynPlugin:foo"));
1785 assertFalse(listener.called);
1786 }
1787
1788 @Test
1789 public void testPluginModuleUnavailableAfterInstallation()
1790 {
1791 PluginLoader pluginLoader = mock(PluginLoader.class);
1792 when(pluginLoader.supportsRemoval()).thenReturn(true);
1793 Plugin plugin = mock(Plugin.class);
1794 when(plugin.getKey()).thenReturn("dynPlugin");
1795 when(plugin.isEnabledByDefault()).thenReturn(true);
1796 when(plugin.isDeleteable()).thenReturn(true);
1797 when(plugin.isUninstallable()).thenReturn(true);
1798 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
1799 when(plugin.compareTo(any(Plugin.class))).thenReturn(-1);
1800 when (pluginLoader.loadAllPlugins(any(ModuleDescriptorFactory.class))).thenReturn(asList(plugin));
1801
1802 manager = newDefaultPluginManager(pluginLoader);
1803 manager.init();
1804
1805 PluginModuleDisabledListener listener = new PluginModuleDisabledListener();
1806 pluginEventManager.register(listener);
1807 Collection<ModuleDescriptor<?>> mods = new ArrayList<ModuleDescriptor<?>>();
1808 MockModuleDescriptor<String> moduleDescriptor = new MockModuleDescriptor<String>(plugin, "foo", "foo");
1809 mods.add(moduleDescriptor);
1810 when(plugin.getModuleDescriptors()).thenReturn(mods);
1811 when(plugin.getModuleDescriptor("foo")).thenReturn((ModuleDescriptor)moduleDescriptor);
1812 pluginEventManager.broadcast(new PluginModuleAvailableEvent(moduleDescriptor));
1813 assertTrue(getPluginAccessor().isPluginModuleEnabled("dynPlugin:foo"));
1814 assertFalse(listener.called);
1815 pluginEventManager.broadcast(new PluginModuleUnavailableEvent(moduleDescriptor));
1816 assertTrue(listener.called);
1817 }
1818
1819 @Test
1820 public void testPluginContainerUnavailable()
1821 {
1822 PluginLoader pluginLoader = mock(PluginLoader.class);
1823 when(pluginLoader.supportsRemoval()).thenReturn(true);
1824 Plugin plugin = mock(Plugin.class);
1825 when(plugin.getKey()).thenReturn("dynPlugin");
1826 when(plugin.isEnabledByDefault()).thenReturn(true);
1827 when(plugin.isDeleteable()).thenReturn(true);
1828 when(plugin.isUninstallable()).thenReturn(true);
1829 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
1830 when(plugin.compareTo(any(Plugin.class))).thenReturn(-1);
1831 Collection<ModuleDescriptor<?>> mods = new ArrayList<ModuleDescriptor<?>>();
1832 MockModuleDescriptor<String> moduleDescriptor = new MockModuleDescriptor<String>(plugin, "foo", "foo");
1833 mods.add(moduleDescriptor);
1834 when(plugin.getModuleDescriptors()).thenReturn(mods);
1835 when(plugin.getModuleDescriptor("foo")).thenReturn((ModuleDescriptor)moduleDescriptor);
1836 when (pluginLoader.loadAllPlugins(any(ModuleDescriptorFactory.class))).thenReturn(asList(plugin));
1837
1838 manager = newDefaultPluginManager(pluginLoader);
1839 manager.init();
1840
1841 PluginDisabledListener listener = new PluginDisabledListener();
1842 PluginModuleDisabledListener moduleDisabledListener = new PluginModuleDisabledListener();
1843 pluginEventManager.register(listener);
1844 pluginEventManager.register(moduleDisabledListener);
1845 when(plugin.getPluginState()).thenReturn(PluginState.DISABLED);
1846 pluginEventManager.broadcast(new PluginContainerUnavailableEvent("dynPlugin"));
1847
1848 assertFalse(getPluginAccessor().isPluginEnabled("dynPlugin"));
1849 assertFalse(listener.called);
1850 assertFalse(moduleDisabledListener.called);
1851 }
1852
1853 @Test
1854 public void testUninstallPluginWithDependencies() throws PluginException, IOException
1855 {
1856 PluginLoader pluginLoader = mock(PluginLoader.class);
1857 when(pluginLoader.supportsRemoval()).thenReturn(true);
1858 Plugin child = mock(Plugin.class);
1859 when(child.getKey()).thenReturn("child");
1860 when(child.isEnabledByDefault()).thenReturn(true);
1861 when(child.getPluginState()).thenReturn(PluginState.ENABLED);
1862 when(child.getRequiredPlugins()).thenReturn(singleton("parent"));
1863 when(child.compareTo(any(Plugin.class))).thenReturn(-1);
1864 Plugin parent = mock(Plugin.class);
1865 when(parent.getKey()).thenReturn("parent");
1866 when(parent.isEnabledByDefault()).thenReturn(true);
1867 when(parent.isDeleteable()).thenReturn(true);
1868 when(parent.isUninstallable()).thenReturn(true);
1869 when(parent.getPluginState()).thenReturn(PluginState.ENABLED);
1870 when(parent.compareTo(any(Plugin.class))).thenReturn(-1);
1871 when (pluginLoader.loadAllPlugins(any(ModuleDescriptorFactory.class))).thenReturn(asList(child, parent));
1872
1873 manager = newDefaultPluginManager(pluginLoader);
1874 manager.init();
1875
1876 manager.uninstall(parent);
1877 verify(parent).enable();
1878 verify(parent).disable();
1879 verify(pluginLoader).removePlugin(parent);
1880
1881 verify(child).enable();
1882 verify(child).disable();
1883 }
1884
1885 @Test
1886 public void testUninstallPluginWithMultiLevelDependencies() throws PluginException, IOException
1887 {
1888 PluginLoader pluginLoader = mock(PluginLoader.class);
1889 when(pluginLoader.supportsRemoval()).thenReturn(true);
1890
1891 Plugin child = mock(Plugin.class);
1892 when(child.getKey()).thenReturn("child");
1893 when(child.isEnabledByDefault()).thenReturn(true);
1894 when(child.getPluginState()).thenReturn(PluginState.ENABLED);
1895 when(child.getRequiredPlugins()).thenReturn(singleton("parent"));
1896 when(child.compareTo(any(Plugin.class))).thenReturn(-1);
1897
1898 Plugin parent = mock(Plugin.class);
1899 when(parent.getKey()).thenReturn("parent");
1900 when(parent.isEnabledByDefault()).thenReturn(true);
1901 when(parent.getPluginState()).thenReturn(PluginState.ENABLED);
1902 when(parent.getRequiredPlugins()).thenReturn(singleton("grandparent"));
1903 when(parent.compareTo(any(Plugin.class))).thenReturn(-1);
1904
1905 Plugin grandparent = mock(Plugin.class);
1906 when(grandparent.getKey()).thenReturn("grandparent");
1907 when(grandparent.isEnabledByDefault()).thenReturn(true);
1908 when(grandparent.isDeleteable()).thenReturn(true);
1909 when(grandparent.isUninstallable()).thenReturn(true);
1910 when(grandparent.getPluginState()).thenReturn(PluginState.ENABLED);
1911 when(grandparent.compareTo(any(Plugin.class))).thenReturn(-1);
1912 when(pluginLoader.loadAllPlugins(any(ModuleDescriptorFactory.class))).thenReturn(asList(child, parent, grandparent));
1913 manager = newDefaultPluginManager(pluginLoader);
1914 manager.init();
1915
1916 manager.uninstall(grandparent);
1917 verify(grandparent).enable();
1918 verify(grandparent).disable();
1919 verify(pluginLoader).removePlugin(grandparent);
1920
1921 verify(parent).enable();
1922 verify(parent).disable();
1923 verify(child).enable();
1924 verify(child).disable();
1925 }
1926
1927 @Test
1928 public void testCircularDependencyWouldNotCauseInfiniteLoop() throws PluginException, IOException
1929 {
1930 PluginLoader pluginLoader = mock(PluginLoader.class);
1931 when(pluginLoader.supportsRemoval()).thenReturn(true);
1932
1933 Plugin p1 = mock(Plugin.class);
1934 when(p1.getKey()).thenReturn("p1");
1935 when(p1.isEnabledByDefault()).thenReturn(true);
1936 when(p1.getPluginState()).thenReturn(PluginState.ENABLED);
1937 when(p1.getRequiredPlugins()).thenReturn(ImmutableSet.of("p2", "parent"));
1938 when(p1.compareTo(any(Plugin.class))).thenReturn(-1);
1939
1940
1941 Plugin p2 = mock(Plugin.class);
1942 when(p2.getKey()).thenReturn("p2");
1943 when(p2.isEnabledByDefault()).thenReturn(true);
1944 when(p2.getPluginState()).thenReturn(PluginState.ENABLED);
1945 when(p2.getRequiredPlugins()).thenReturn(singleton("p1"));
1946 when(p2.compareTo(any(Plugin.class))).thenReturn(-1);
1947
1948 Plugin parent = mock(Plugin.class);
1949 when(parent.getKey()).thenReturn("parent");
1950 when(parent.isEnabledByDefault()).thenReturn(true);
1951 when(parent.isDeleteable()).thenReturn(true);
1952 when(parent.isUninstallable()).thenReturn(true);
1953 when(parent.getPluginState()).thenReturn(PluginState.ENABLED);
1954 when(parent.compareTo(any(Plugin.class))).thenReturn(-1);
1955 when (pluginLoader.loadAllPlugins(any(ModuleDescriptorFactory.class))).thenReturn(asList(p1, p2, parent));
1956 manager = newDefaultPluginManager(pluginLoader);
1957 manager.init();
1958
1959 manager.uninstall(parent);
1960 verify(parent, times(1)).enable();
1961 verify(parent, times(1)).disable();
1962 verify(pluginLoader).removePlugin(parent);
1963
1964 verify(p1, times(1)).enable();
1965 verify(p1, times(1)).disable();
1966 verify(p2, times(1)).enable();
1967 verify(p2, times(1)).disable();
1968 }
1969
1970 @Test
1971 public void testThreeCycleDependencyWouldNotCauseInfiniteLoop() throws PluginException, IOException
1972 {
1973 PluginLoader pluginLoader = mock(PluginLoader.class);
1974 when(pluginLoader.supportsRemoval()).thenReturn(true);
1975
1976 Plugin p1 = mock(Plugin.class);
1977 when(p1.getKey()).thenReturn("p1");
1978 when(p1.isEnabledByDefault()).thenReturn(true);
1979 when(p1.getPluginState()).thenReturn(PluginState.ENABLED);
1980 when(p1.getRequiredPlugins()).thenReturn(ImmutableSet.of("p2", "parent"));
1981 when(p1.compareTo(any(Plugin.class))).thenReturn(-1);
1982
1983
1984 Plugin p2 = mock(Plugin.class);
1985 when(p2.getKey()).thenReturn("p2");
1986 when(p2.isEnabledByDefault()).thenReturn(true);
1987 when(p2.getPluginState()).thenReturn(PluginState.ENABLED);
1988 when(p2.getRequiredPlugins()).thenReturn(singleton("p3"));
1989 when(p2.compareTo(any(Plugin.class))).thenReturn(-1);
1990
1991 Plugin p3 = mock(Plugin.class);
1992 when(p3.getKey()).thenReturn("p3");
1993 when(p3.isEnabledByDefault()).thenReturn(true);
1994 when(p3.getPluginState()).thenReturn(PluginState.ENABLED);
1995 when(p3.getRequiredPlugins()).thenReturn(singleton("p1"));
1996 when(p3.compareTo(any(Plugin.class))).thenReturn(-1);
1997
1998 Plugin parent = mock(Plugin.class);
1999 when(parent.getKey()).thenReturn("parent");
2000 when(parent.isEnabledByDefault()).thenReturn(true);
2001 when(parent.isDeleteable()).thenReturn(true);
2002 when(parent.isUninstallable()).thenReturn(true);
2003 when(parent.getPluginState()).thenReturn(PluginState.ENABLED);
2004 when(parent.compareTo(any(Plugin.class))).thenReturn(-1);
2005 when (pluginLoader.loadAllPlugins(any(ModuleDescriptorFactory.class))).thenReturn(asList(p1, p2, p3, parent));
2006 manager = newDefaultPluginManager(pluginLoader);
2007 manager.init();
2008
2009 manager.uninstall(parent);
2010 verify(parent, times(1)).enable();
2011 verify(parent, times(1)).disable();
2012 verify(pluginLoader).removePlugin(parent);
2013
2014 verify(p1, times(1)).enable();
2015 verify(p1, times(1)).disable();
2016 verify(p2, times(1)).enable();
2017 verify(p2, times(1)).disable();
2018 verify(p3, times(1)).enable();
2019 verify(p3, times(1)).disable();
2020 }
2021
2022 @Test
2023 public void testNonRemovablePlugins() throws PluginParseException
2024 {
2025 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
2026 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
2027
2028 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
2029 manager.init();
2030
2031 final Plugin plugin = getPluginAccessor().getPlugin("test.atlassian.plugin");
2032 assertFalse(plugin.isUninstallable());
2033 assertNotNull(plugin.getResourceAsStream("test-atlassian-plugin.xml"));
2034
2035 try
2036 {
2037 manager.uninstall(plugin);
2038 fail("Where was the exception?");
2039 }
2040 catch (final PluginException p)
2041 {
2042 }
2043 }
2044
2045 @Test
2046 public void testNonDeletablePlugins() throws PluginException, IOException
2047 {
2048 createFillAndCleanTempPluginDirectory();
2049
2050 final DefaultPluginManager manager = makeClassLoadingPluginManager();
2051 assertThat(manager.getPlugins(), hasSize(2));
2052
2053
2054 final Plugin pluginToRemove = new AbstractDelegatingPlugin(manager.getPlugin("test.atlassian.plugin.classloaded"))
2055 {
2056 public boolean isDeleteable()
2057 {
2058 return false;
2059 }
2060 };
2061
2062
2063 final MockAnimalModuleDescriptor moduleDescriptor = (MockAnimalModuleDescriptor) manager.getPluginModule("test.atlassian.plugin.classloaded:paddington");
2064 assertFalse(moduleDescriptor.disabled);
2065
2066 manager.uninstall(pluginToRemove);
2067
2068 assertTrue("Module must have had disable() called before being removed", moduleDescriptor.disabled);
2069 assertThat(manager.getPlugins(), hasSize(1));
2070 assertNull(manager.getPlugin("test.atlassian.plugin.classloaded"));
2071 assertEquals(2, pluginsTestDir.listFiles().length);
2072 }
2073
2074
2075 @Test
2076 public void testComparePluginNewer()
2077 {
2078
2079 final Plugin p1 = createPluginWithVersion("1.1");
2080 final Plugin p2 = createPluginWithVersion("1.0");
2081 assertTrue(p1.compareTo(p2) == 1);
2082
2083 p1.getPluginInformation().setVersion("1.10");
2084 p2.getPluginInformation().setVersion("1.2");
2085 assertTrue(p1.compareTo(p2) == 1);
2086
2087 p1.getPluginInformation().setVersion("1.2");
2088 p2.getPluginInformation().setVersion("1.01");
2089 assertTrue(p1.compareTo(p2) == 1);
2090
2091 p1.getPluginInformation().setVersion("1.0.1");
2092 p2.getPluginInformation().setVersion("1.0");
2093 assertTrue(p1.compareTo(p2) == 1);
2094
2095 p1.getPluginInformation().setVersion("1.2");
2096 p2.getPluginInformation().setVersion("1.1.1");
2097 assertTrue(p1.compareTo(p2) == 1);
2098 }
2099
2100 @Test
2101 public void testComparePluginOlder()
2102 {
2103 final Plugin p1 = createPluginWithVersion("1.0");
2104 final Plugin p2 = createPluginWithVersion("1.1");
2105 assertTrue(p1.compareTo(p2) == -1);
2106
2107 p1.getPluginInformation().setVersion("1.2");
2108 p2.getPluginInformation().setVersion("1.10");
2109 assertTrue(p1.compareTo(p2) == -1);
2110
2111 p1.getPluginInformation().setVersion("1.01");
2112 p2.getPluginInformation().setVersion("1.2");
2113 assertTrue(p1.compareTo(p2) == -1);
2114
2115 p1.getPluginInformation().setVersion("1.0");
2116 p2.getPluginInformation().setVersion("1.0.1");
2117 assertTrue(p1.compareTo(p2) == -1);
2118
2119 p1.getPluginInformation().setVersion("1.1.1");
2120 p2.getPluginInformation().setVersion("1.2");
2121 assertTrue(p1.compareTo(p2) == -1);
2122 }
2123
2124 @Test
2125 public void testComparePluginEqual()
2126 {
2127 final Plugin p1 = createPluginWithVersion("1.0");
2128 final Plugin p2 = createPluginWithVersion("1.0");
2129 assertTrue(p1.compareTo(p2) == 0);
2130
2131 p1.getPluginInformation().setVersion("1.1.0.0");
2132 p2.getPluginInformation().setVersion("1.1");
2133 assertTrue(p1.compareTo(p2) == 0);
2134
2135 p1.getPluginInformation().setVersion(" 1 . 1 ");
2136 p2.getPluginInformation().setVersion("1.1");
2137 assertTrue(p1.compareTo(p2) == 0);
2138 }
2139
2140
2141 @Test
2142 public void testComparePluginNoVersion()
2143 {
2144 final Plugin p1 = createPluginWithVersion("1.0");
2145 final Plugin p2 = createPluginWithVersion("#$%");
2146 assertEquals(1, p1.compareTo(p2));
2147
2148 p1.getPluginInformation().setVersion("#$%");
2149 p2.getPluginInformation().setVersion("1.0");
2150 assertEquals(-1, p1.compareTo(p2));
2151 }
2152
2153 @Test
2154 public void testComparePluginBadPlugin()
2155 {
2156 final Plugin p1 = createPluginWithVersion("1.0");
2157 final Plugin p2 = createPluginWithVersion("1.0");
2158
2159
2160 p2.setKey("bad.key");
2161 assertTrue(p1.compareTo(p2) != 0);
2162 }
2163
2164 @Test
2165 public void testInvalidationOfDynamicResourceCache() throws IOException, PluginException
2166 {
2167 createFillAndCleanTempPluginDirectory();
2168
2169 final DefaultPluginManager manager = makeClassLoadingPluginManager();
2170
2171 checkResources(manager, true, true);
2172 manager.disablePlugin("test.atlassian.plugin.classloaded");
2173 checkResources(manager, false, false);
2174 manager.enablePlugin("test.atlassian.plugin.classloaded");
2175 checkResources(manager, true, true);
2176 manager.uninstall(manager.getPlugin("test.atlassian.plugin.classloaded"));
2177 checkResources(manager, false, false);
2178
2179 FileUtils.copyDirectory(pluginsDirectory, pluginsTestDir);
2180 manager.scanForNewPlugins();
2181 checkResources(manager, true, true);
2182
2183
2184
2185 }
2186
2187 @Test
2188 public void testValidatePlugin() throws PluginParseException
2189 {
2190 final DynamicPluginLoader mockLoader = mock(DynamicPluginLoader.class);
2191 when(mockLoader.isDynamicPluginLoader()).thenReturn(true);
2192
2193 manager = new DefaultPluginManager(pluginStateStore, ImmutableList.<PluginLoader>of(mockLoader), moduleDescriptorFactory, new DefaultPluginEventManager());
2194
2195 final PluginArtifact mockPluginJar = mock(PluginArtifact.class);
2196 final PluginArtifact pluginArtifact = mockPluginJar;
2197 when(mockLoader.canLoad(pluginArtifact)).thenReturn("foo");
2198
2199 final String key = manager.validatePlugin(pluginArtifact);
2200 assertEquals("foo", key);
2201 verify(mockLoader).canLoad(pluginArtifact);
2202 }
2203
2204 @Test
2205 public void testValidatePluginWithNoDynamicLoaders() throws PluginParseException
2206 {
2207 final PluginLoader loader = mock(PluginLoader.class);
2208 final DefaultPluginManager manager = new DefaultPluginManager(pluginStateStore, ImmutableList.<PluginLoader>of(loader), moduleDescriptorFactory, new DefaultPluginEventManager());
2209
2210 final PluginArtifact pluginArtifact = mock(PluginArtifact.class);
2211 try
2212 {
2213 manager.validatePlugin(pluginArtifact);
2214 fail("Should have thrown exception");
2215 }
2216 catch (final IllegalStateException ex)
2217 {
2218
2219 }
2220 }
2221
2222 @Test
2223 public void testInvalidationOfDynamicClassCache() throws IOException, PluginException
2224 {
2225 createFillAndCleanTempPluginDirectory();
2226
2227 final DefaultPluginManager manager = makeClassLoadingPluginManager();
2228
2229 checkClasses(manager, true);
2230 manager.disablePlugin("test.atlassian.plugin.classloaded");
2231 checkClasses(manager, false);
2232 manager.enablePlugin("test.atlassian.plugin.classloaded");
2233 checkClasses(manager, true);
2234 manager.uninstall(manager.getPlugin("test.atlassian.plugin.classloaded"));
2235 checkClasses(manager, false);
2236
2237 FileUtils.copyDirectory(pluginsDirectory, pluginsTestDir);
2238 manager.scanForNewPlugins();
2239 checkClasses(manager, true);
2240 }
2241
2242 @Test
2243 public void testInstallPlugin() throws Exception
2244 {
2245 final PluginPersistentStateStore mockPluginStateStore = mock(PluginPersistentStateStore.class);
2246 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
2247 final DynamicPluginLoader mockPluginLoader = mock(DynamicPluginLoader.class);
2248 when(mockPluginLoader.isDynamicPluginLoader()).thenReturn(true);
2249
2250 final DescriptorParserFactory mockDescriptorParserFactory = mock(DescriptorParserFactory.class);
2251 final DescriptorParser mockDescriptorParser = mock(DescriptorParser.class);
2252 final PluginArtifact pluginArtifact = mock(PluginArtifact.class);
2253 final PluginInstaller mockRepository = mock(PluginInstaller.class);
2254 final Plugin plugin = mock(Plugin.class);
2255
2256
2257 final DefaultPluginManager pluginManager = new DefaultPluginManager(mockPluginStateStore,
2258 Collections.<PluginLoader>singletonList(mockPluginLoader), moduleDescriptorFactory, pluginEventManager);
2259
2260 when(mockPluginStateStore.load()).thenReturn(new DefaultPluginPersistentState());
2261 when(mockPluginStateStore.load()).thenReturn(new DefaultPluginPersistentState());
2262 when(mockPluginStateStore.load()).thenReturn(new DefaultPluginPersistentState());
2263 when(mockDescriptorParser.getKey()).thenReturn("test");
2264 when(mockPluginLoader.loadAllPlugins(moduleDescriptorFactory)).thenReturn((Iterable) Collections.emptyList());
2265 when(mockPluginLoader.supportsAddition()).thenReturn(true);
2266 when(mockPluginLoader.loadFoundPlugins(moduleDescriptorFactory)).thenReturn(Collections.singletonList(plugin));
2267 when(mockPluginLoader.canLoad(pluginArtifact)).thenReturn("test");
2268 when(plugin.getKey()).thenReturn("test");
2269 when(plugin.getModuleDescriptors()).thenReturn((Collection) new ArrayList<Object>());
2270 when(plugin.getModuleDescriptors()).thenReturn((Collection) new ArrayList<Object>());
2271 when(plugin.isEnabledByDefault()).thenReturn(true);
2272
2273 when(plugin.isEnabledByDefault()).thenReturn(true);
2274 when(plugin.isEnabled()).thenReturn(true);
2275 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
2276 when(plugin.hasAllPermissions()).thenReturn(true);
2277 when(plugin.getActivePermissions()).thenReturn(ImmutableSet.of(Permissions.ALL_PERMISSIONS));
2278
2279 pluginManager.setPluginInstaller(mockRepository);
2280 pluginManager.init();
2281 final PassListener enabledListener = new PassListener(PluginEnabledEvent.class);
2282 pluginEventManager.register(enabledListener);
2283 pluginManager.installPlugin(pluginArtifact);
2284
2285 assertEquals(plugin, pluginManager.getPlugin("test"));
2286 assertTrue(pluginManager.isPluginEnabled("test"));
2287
2288
2289
2290
2291
2292
2293
2294
2295 enabledListener.assertCalled();
2296 }
2297
2298 @Test
2299 public void testInstallPluginsWithOne()
2300 {
2301 DynamicPluginLoader loader = mock(DynamicPluginLoader.class);
2302 when(loader.isDynamicPluginLoader()).thenReturn(true);
2303
2304 ModuleDescriptorFactory descriptorFactory = mock(ModuleDescriptorFactory.class);
2305 PluginEventManager eventManager = mock(PluginEventManager.class);
2306 PluginInstaller installer = mock(PluginInstaller.class);
2307 DefaultPluginManager pm = new DefaultPluginManager(new MemoryPluginPersistentStateStore(), Collections.<PluginLoader>singletonList(loader), descriptorFactory, eventManager);
2308 pm.setPluginInstaller(installer);
2309 PluginArtifact artifact = mock(PluginArtifact.class);
2310 Plugin plugin = mock(Plugin.class);
2311 when(loader.canLoad(artifact)).thenReturn("foo");
2312 when(loader.loadFoundPlugins(descriptorFactory)).thenReturn(Arrays.asList(plugin));
2313
2314 pm.installPlugins(artifact);
2315
2316 verify(loader).canLoad(artifact);
2317 verify(installer).installPlugin("foo", artifact);
2318 }
2319
2320 @Test
2321 public void testInstallPluginsWithTwo()
2322 {
2323 DynamicPluginLoader loader = mock(DynamicPluginLoader.class);
2324 when(loader.isDynamicPluginLoader()).thenReturn(true);
2325
2326 ModuleDescriptorFactory descriptorFactory = mock(ModuleDescriptorFactory.class);
2327 PluginEventManager eventManager = mock(PluginEventManager.class);
2328 PluginInstaller installer = mock(PluginInstaller.class);
2329 DefaultPluginManager pm = new DefaultPluginManager(new MemoryPluginPersistentStateStore(), Collections.<PluginLoader>singletonList(loader), descriptorFactory, eventManager);
2330 pm.setPluginInstaller(installer);
2331 PluginArtifact artifactA = mock(PluginArtifact.class);
2332 Plugin pluginA = mock(Plugin.class);
2333 when(loader.canLoad(artifactA)).thenReturn("a");
2334 PluginArtifact artifactB = mock(PluginArtifact.class);
2335 Plugin pluginB = mock(Plugin.class);
2336 when(loader.canLoad(artifactB)).thenReturn("b");
2337
2338 when(loader.loadFoundPlugins(descriptorFactory)).thenReturn(Arrays.asList(pluginA, pluginB));
2339
2340 pm.installPlugins(artifactA, artifactB);
2341
2342 verify(loader).canLoad(artifactA);
2343 verify(loader).canLoad(artifactB);
2344 verify(installer).installPlugin("a", artifactA);
2345 verify(installer).installPlugin("b", artifactB);
2346 }
2347
2348 @Test
2349 public void testInstallPluginsWithTwoButOneFailsValidation()
2350 {
2351 DynamicPluginLoader loader = mock(DynamicPluginLoader.class);
2352 when(loader.isDynamicPluginLoader()).thenReturn(true);
2353
2354 ModuleDescriptorFactory descriptorFactory = mock(ModuleDescriptorFactory.class);
2355 PluginEventManager eventManager = mock(PluginEventManager.class);
2356 PluginInstaller installer = mock(PluginInstaller.class);
2357 DefaultPluginManager pm = new DefaultPluginManager(new MemoryPluginPersistentStateStore(), Collections.<PluginLoader>singletonList(loader), descriptorFactory, eventManager);
2358 pm.setPluginInstaller(installer);
2359 PluginArtifact artifactA = mock(PluginArtifact.class);
2360 Plugin pluginA = mock(Plugin.class);
2361 when(loader.canLoad(artifactA)).thenReturn("a");
2362 PluginArtifact artifactB = mock(PluginArtifact.class);
2363 Plugin pluginB = mock(Plugin.class);
2364 when(loader.canLoad(artifactB)).thenReturn(null);
2365
2366 when(loader.loadFoundPlugins(descriptorFactory)).thenReturn(Arrays.asList(pluginA, pluginB));
2367
2368 try
2369 {
2370 pm.installPlugins(artifactA, artifactB);
2371 fail("Should have not installed plugins");
2372 }
2373 catch (PluginParseException ex)
2374 {
2375
2376 }
2377
2378 verify(loader).canLoad(artifactA);
2379 verify(loader).canLoad(artifactB);
2380 verify(installer, never()).installPlugin("a", artifactA);
2381 verify(installer, never()).installPlugin("b", artifactB);
2382 }
2383
2384 @Test
2385 public void testInstallPluginsWithTwoButOneFailsValidationWithException()
2386 {
2387 DynamicPluginLoader loader = mock(DynamicPluginLoader.class);
2388 when(loader.isDynamicPluginLoader()).thenReturn(true);
2389
2390 ModuleDescriptorFactory descriptorFactory = mock(ModuleDescriptorFactory.class);
2391 PluginEventManager eventManager = mock(PluginEventManager.class);
2392 PluginInstaller installer = mock(PluginInstaller.class);
2393 DefaultPluginManager pm = new DefaultPluginManager(new MemoryPluginPersistentStateStore(), Collections.<PluginLoader>singletonList(loader), descriptorFactory, eventManager);
2394 pm.setPluginInstaller(installer);
2395 PluginArtifact artifactA = mock(PluginArtifact.class);
2396 Plugin pluginA = mock(Plugin.class);
2397 when(loader.canLoad(artifactA)).thenReturn("a");
2398 PluginArtifact artifactB = mock(PluginArtifact.class);
2399 Plugin pluginB = mock(Plugin.class);
2400 doThrow(new PluginParseException()).when(loader).canLoad(artifactB);
2401
2402 when(loader.loadFoundPlugins(descriptorFactory)).thenReturn(Arrays.asList(pluginA, pluginB));
2403
2404 try
2405 {
2406 pm.installPlugins(artifactA, artifactB);
2407 fail("Should have not installed plugins");
2408 }
2409 catch (PluginParseException ex)
2410 {
2411
2412 }
2413
2414 verify(loader).canLoad(artifactA);
2415 verify(loader).canLoad(artifactB);
2416 verify(installer, never()).installPlugin("a", artifactA);
2417 verify(installer, never()).installPlugin("b", artifactB);
2418 }
2419
2420 Plugin mockStaticPlugin(final String pluginKey, final ModuleDescriptor<?>... descriptors)
2421 {
2422 return new StaticPlugin()
2423 {
2424 {
2425 setPluginInformation(new PluginInformation());
2426 setEnabledByDefault(true);
2427 setKey(pluginKey);
2428 }
2429
2430 @Override
2431 public Collection<ModuleDescriptor<?>> getModuleDescriptors()
2432 {
2433 return Arrays.<ModuleDescriptor<?>>asList(descriptors);
2434 }
2435
2436 @Override
2437 public ModuleDescriptor<Object> getModuleDescriptor(final String moduleKey)
2438 {
2439 for (ModuleDescriptor desc : descriptors)
2440 {
2441 if (desc.getKey().equals(moduleKey))
2442 return desc;
2443 }
2444 return null;
2445 }
2446 };
2447 }
2448
2449 @Test
2450 public void testInstallTwoPluginsButOneFailsToEnableAModuleAndThenFailsToDisableAModule()
2451 {
2452 final PluginLoader mockPluginLoader = mock(PluginLoader.class);
2453 final ModuleDescriptor<Object> failEnableModuleDescriptor = mockFailingModuleDescriptor("foo:bar", FailureMode.FAIL_TO_ENABLE);
2454 final ModuleDescriptor<Object> failDisableModuleDescriptor = mockFailingModuleDescriptor("foo:buzz", FailureMode.FAIL_TO_DISABLE);
2455
2456 Plugin badPlugin = mockStaticPlugin("foo", failDisableModuleDescriptor, failEnableModuleDescriptor);
2457
2458 final AbstractModuleDescriptor<?> goodModuleDescriptor = mock(AbstractModuleDescriptor.class);
2459 when(goodModuleDescriptor.getKey()).thenReturn("baz");
2460 when(goodModuleDescriptor.getCompleteKey()).thenReturn("good:baz");
2461 when(goodModuleDescriptor.isEnabledByDefault()).thenReturn(true);
2462 Plugin goodPlugin = mockStaticPlugin("good", goodModuleDescriptor);
2463
2464 when(mockPluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Lists.newArrayList(badPlugin, goodPlugin));
2465
2466 manager = newDefaultPluginManager(mockPluginLoader);
2467 manager.init();
2468
2469 assertThat(getPluginAccessor().getPlugins(), hasSize(2));
2470 assertThat(getPluginAccessor().getEnabledPlugins(), hasSize(1));
2471 verify(goodModuleDescriptor).enabled();
2472 }
2473
2474 private <T> void checkResources(final PluginAccessor manager, final boolean canGetGlobal, final boolean canGetModule) throws IOException
2475 {
2476 InputStream is = manager.getDynamicResourceAsStream("icon.gif");
2477 assertEquals(canGetGlobal, is != null);
2478 IOUtils.closeQuietly(is);
2479 is = manager.getDynamicResourceAsStream("bear/paddington.vm");
2480 assertEquals(canGetModule, is != null);
2481 IOUtils.closeQuietly(is);
2482 }
2483
2484 private <T> void checkClasses(final PluginAccessor manager, final boolean canGet)
2485 {
2486 try
2487 {
2488 manager.getDynamicPluginClass("com.atlassian.plugin.mock.MockPaddington");
2489 if (!canGet)
2490 {
2491 fail("Class in plugin was successfully loaded");
2492 }
2493 }
2494 catch (final ClassNotFoundException e)
2495 {
2496 if (canGet)
2497 {
2498 fail(e.getMessage());
2499 }
2500 }
2501 }
2502
2503 @Test
2504 public void testAddPluginsThatThrowExceptionOnEnabled() throws Exception
2505 {
2506 final Plugin plugin = new CannotEnablePlugin();
2507
2508 manager = newDefaultPluginManager();
2509 manager.addPlugins(null, Arrays.asList(plugin));
2510
2511 assertFalse(plugin.getPluginState() == PluginState.ENABLED);
2512 }
2513
2514 @Test
2515 public void testUninstallPluginClearsState() throws IOException
2516 {
2517 createFillAndCleanTempPluginDirectory();
2518
2519 final DefaultPluginManager manager = makeClassLoadingPluginManager();
2520
2521 checkClasses(manager, true);
2522 final Plugin plugin = manager.getPlugin("test.atlassian.plugin.classloaded");
2523
2524 final ModuleDescriptor<?> module = plugin.getModuleDescriptor("paddington");
2525 assertTrue(manager.isPluginModuleEnabled(module.getCompleteKey()));
2526 manager.disablePluginModule(module.getCompleteKey());
2527 assertFalse(manager.isPluginModuleEnabled(module.getCompleteKey()));
2528 manager.uninstall(plugin);
2529 assertFalse(manager.isPluginModuleEnabled(module.getCompleteKey()));
2530 assertTrue(pluginStateStore.load().getPluginStateMap(plugin).isEmpty());
2531 }
2532
2533 @Test
2534 public void testCannotInitTwice() throws PluginParseException
2535 {
2536 manager = newDefaultPluginManager();
2537 manager.init();
2538 try
2539 {
2540 manager.init();
2541 fail("IllegalStateException expected");
2542 }
2543 catch (final IllegalStateException expected)
2544 {
2545 }
2546 }
2547
2548 @Test
2549 public void testCannotShutdownTwice() throws PluginParseException
2550 {
2551 manager = newDefaultPluginManager();
2552 manager.init();
2553 manager.shutdown();
2554 try
2555 {
2556 manager.shutdown();
2557 fail("IllegalStateException expected");
2558 }
2559 catch (final IllegalStateException expected)
2560 {
2561 }
2562 }
2563
2564 @Test
2565 public void testGetPluginWithNullKey()
2566 {
2567 manager = newDefaultPluginManager();
2568 manager.init();
2569 try
2570 {
2571 getPluginAccessor().getPlugin(null);
2572 fail();
2573 }
2574 catch (IllegalArgumentException ex)
2575 {
2576
2577 }
2578 }
2579
2580 @Test
2581 public void testShutdownHandlesException()
2582 {
2583 final ThingsAreWrongListener listener = new ThingsAreWrongListener();
2584 pluginEventManager.register(listener);
2585
2586 manager = newDefaultPluginManager();
2587 manager.init();
2588 try
2589 {
2590
2591 manager.shutdown();
2592 }
2593 catch (Exception e)
2594 {
2595 fail("Should not have thrown an exception!");
2596 }
2597 assertTrue(listener.isCalled());
2598 }
2599
2600 private void writeToFile(String file, String line) throws IOException, URISyntaxException
2601 {
2602 final String resourceName = ClasspathFilePluginMetadata.class.getPackage().getName().replace(".", "/") + "/" + file;
2603 URL resource = getClass().getClassLoader().getResource(resourceName);
2604
2605 FileOutputStream fout = new FileOutputStream(new File(resource.toURI()), false);
2606 fout.write(line.getBytes(), 0, line.length());
2607 fout.close();
2608
2609 }
2610
2611 @Test
2612 public void testExceptionOnRequiredPluginNotEnabling() throws Exception
2613 {
2614 try
2615 {
2616 writeToFile("application-required-modules.txt", "foo.required:bar");
2617 writeToFile("application-required-plugins.txt", "foo.required");
2618
2619 final PluginLoader mockPluginLoader = mock(PluginLoader.class);
2620 final ModuleDescriptor<Object> badModuleDescriptor = mockFailingModuleDescriptor("foo.required:bar", FailureMode.FAIL_TO_ENABLE);
2621
2622 final AbstractModuleDescriptor goodModuleDescriptor = mock(AbstractModuleDescriptor.class);
2623 when(goodModuleDescriptor.getKey()).thenReturn("baz");
2624 when(goodModuleDescriptor.getCompleteKey()).thenReturn("foo.required:baz");
2625
2626 Plugin plugin = mockStaticPlugin("foo.required", goodModuleDescriptor, badModuleDescriptor);
2627
2628 when(mockPluginLoader.loadAllPlugins(any(ModuleDescriptorFactory.class))).thenReturn(Collections.singletonList(plugin));
2629
2630 manager = newDefaultPluginManager(mockPluginLoader);
2631 try
2632 {
2633 manager.init();
2634 }
2635 catch(PluginException e)
2636 {
2637
2638 assertEquals("Unable to validate required plugins or modules", e.getMessage());
2639 return;
2640 }
2641 fail("A PluginException is expected when trying to initialize the plugins system with required plugins that do not load.");
2642 }
2643 finally
2644 {
2645
2646 writeToFile("application-required-modules.txt", "");
2647 writeToFile("application-required-plugins.txt", "");
2648 }
2649 }
2650
2651 public static class ThingsAreWrongListener
2652 {
2653 private volatile boolean called = false;
2654
2655 @PluginEventListener
2656 public void onFrameworkShutdown(final PluginFrameworkShutdownEvent event)
2657 {
2658 called = true;
2659 throw new NullPointerException("AAAH!");
2660 }
2661
2662 public boolean isCalled()
2663 {
2664 return called;
2665 }
2666 }
2667
2668 public Plugin createPluginWithVersion(final String version)
2669 {
2670 final Plugin p = new StaticPlugin();
2671 p.setKey("test.default.plugin");
2672 final PluginInformation pInfo = p.getPluginInformation();
2673 pInfo.setVersion(version);
2674 return p;
2675 }
2676
2677
2678
2679
2680
2681 private static class SinglePluginLoaderWithRemoval extends SinglePluginLoader
2682 {
2683 public SinglePluginLoaderWithRemoval(final String resource)
2684 {
2685 super(resource);
2686 }
2687
2688 public boolean supportsRemoval()
2689 {
2690
2691 return true;
2692 }
2693
2694 public void removePlugin(final Plugin plugin) throws PluginException
2695 {
2696 plugins = Collections.emptyList();
2697 }
2698
2699 protected StaticPlugin getNewPlugin()
2700 {
2701 return new StaticPlugin()
2702 {
2703 public boolean isUninstallable()
2704 {
2705 return true;
2706 }
2707 };
2708 }
2709 }
2710
2711 class NothingModuleDescriptor extends MockUnusedModuleDescriptor
2712 {
2713 }
2714
2715 @RequiresRestart
2716 public static class RequiresRestartModuleDescriptor extends MockUnusedModuleDescriptor
2717 {
2718 }
2719
2720
2721 public static class RequiresRestartSubclassModuleDescriptor extends RequiresRestartModuleDescriptor
2722 {
2723 }
2724
2725 private class MultiplePluginLoader implements PluginLoader
2726 {
2727 private final String[] descriptorPaths;
2728
2729 public MultiplePluginLoader(final String... descriptorPaths)
2730 {
2731 this.descriptorPaths = descriptorPaths;
2732 }
2733
2734 public Iterable<Plugin> loadAllPlugins(final ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
2735 {
2736 final ImmutableList.Builder<Plugin> result = ImmutableList.builder();
2737 for (final String path : descriptorPaths)
2738 {
2739 final SinglePluginLoader loader = new SinglePluginLoader(path);
2740 result.addAll(loader.loadAllPlugins(moduleDescriptorFactory));
2741 }
2742 return result.build();
2743 }
2744
2745 public boolean supportsAddition()
2746 {
2747 return false;
2748 }
2749
2750 public boolean supportsRemoval()
2751 {
2752 return false;
2753 }
2754
2755 public Iterable<Plugin> loadFoundPlugins(final ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
2756 {
2757 throw new UnsupportedOperationException("This PluginLoader does not support addition.");
2758 }
2759
2760 public void removePlugin(final Plugin plugin) throws PluginException
2761 {
2762 throw new UnsupportedOperationException("This PluginLoader does not support addition.");
2763 }
2764
2765 @Override
2766 public boolean isDynamicPluginLoader()
2767 {
2768 return false;
2769 }
2770 }
2771
2772 private static class DynamicSinglePluginLoader extends SinglePluginLoader implements PluginLoader, DynamicPluginLoader
2773 {
2774 private final AtomicBoolean canLoad = new AtomicBoolean(false);
2775
2776 private final String key;
2777
2778 public DynamicSinglePluginLoader(final String key, final String resource)
2779 {
2780 super(resource);
2781 this.key = key;
2782 }
2783
2784 @Override
2785 public boolean isDynamicPluginLoader()
2786 {
2787 return true;
2788 }
2789
2790 public String canLoad(final PluginArtifact pluginArtifact) throws PluginParseException
2791 {
2792 return canLoad.get() ? key : null;
2793 }
2794
2795 public boolean supportsAddition()
2796 {
2797 return true;
2798 }
2799
2800 @Override
2801 public Iterable<Plugin> loadAllPlugins(ModuleDescriptorFactory moduleDescriptorFactory)
2802 {
2803 if (canLoad.get())
2804 {
2805 return super.loadAllPlugins(moduleDescriptorFactory);
2806 }
2807 else
2808 {
2809 return ImmutableList.of();
2810 }
2811 }
2812
2813 @Override
2814 public Iterable<Plugin> loadFoundPlugins(final ModuleDescriptorFactory moduleDescriptorFactory)
2815 {
2816 if (canLoad.get())
2817 {
2818 return super.loadAllPlugins(moduleDescriptorFactory);
2819 }
2820 else
2821 {
2822 return ImmutableList.of();
2823 }
2824 }
2825 }
2826
2827 private static class CannotEnablePlugin extends StaticPlugin
2828 {
2829 public CannotEnablePlugin()
2830 {
2831 setKey("foo");
2832 }
2833
2834 @Override
2835 protected PluginState enableInternal()
2836 {
2837 throw new RuntimeException("boo");
2838 }
2839
2840 public void disabled()
2841 {
2842 }
2843 }
2844
2845 public static class PluginModuleEnabledListener
2846 {
2847 public volatile boolean called;
2848 @PluginEventListener
2849 public void onEnable(PluginModuleEnabledEvent event)
2850 {
2851 called = true;
2852 }
2853 }
2854
2855 public static class PluginModuleDisabledListener
2856 {
2857 public volatile boolean called;
2858 @PluginEventListener
2859 public void onDisable(PluginModuleDisabledEvent event)
2860 {
2861 called = true;
2862 }
2863 }
2864
2865 public static class PluginDisabledListener
2866 {
2867 public volatile boolean called;
2868 @PluginEventListener
2869 public void onDisable(PluginDisabledEvent event)
2870 {
2871 called = true;
2872 }
2873 }
2874 }