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