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