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