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