View Javadoc

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