View Javadoc

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