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