View Javadoc

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