View Javadoc

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