View Javadoc

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