View Javadoc

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