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.Map;
15 import java.util.concurrent.atomic.AtomicBoolean;
16
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.ModuleCompleteKey;
22 import com.atlassian.plugin.ModuleDescriptor;
23 import com.atlassian.plugin.ModuleDescriptorFactory;
24 import com.atlassian.plugin.Permissions;
25 import com.atlassian.plugin.Plugin;
26 import com.atlassian.plugin.PluginAccessor;
27 import com.atlassian.plugin.PluginArtifact;
28 import com.atlassian.plugin.PluginException;
29 import com.atlassian.plugin.PluginInformation;
30 import com.atlassian.plugin.PluginInstaller;
31 import com.atlassian.plugin.PluginParseException;
32 import com.atlassian.plugin.PluginRestartState;
33 import com.atlassian.plugin.PluginState;
34 import com.atlassian.plugin.SplitStartupPluginSystemLifecycle;
35 import com.atlassian.plugin.StateAware;
36 import com.atlassian.plugin.descriptors.AbstractModuleDescriptor;
37 import com.atlassian.plugin.descriptors.MockUnusedModuleDescriptor;
38 import com.atlassian.plugin.descriptors.RequiresRestart;
39 import com.atlassian.plugin.event.NotificationException;
40 import com.atlassian.plugin.event.PluginEventListener;
41 import com.atlassian.plugin.event.PluginEventManager;
42 import com.atlassian.plugin.event.events.BeforePluginDisabledEvent;
43 import com.atlassian.plugin.event.events.BeforePluginModuleDisabledEvent;
44 import com.atlassian.plugin.event.events.PluginContainerUnavailableEvent;
45 import com.atlassian.plugin.event.events.PluginDisabledEvent;
46 import com.atlassian.plugin.event.events.PluginEnabledEvent;
47 import com.atlassian.plugin.event.events.PluginFrameworkDelayedEvent;
48 import com.atlassian.plugin.event.events.PluginFrameworkResumingEvent;
49 import com.atlassian.plugin.event.events.PluginFrameworkShutdownEvent;
50 import com.atlassian.plugin.event.events.PluginFrameworkShuttingDownEvent;
51 import com.atlassian.plugin.event.events.PluginFrameworkStartedEvent;
52 import com.atlassian.plugin.event.events.PluginFrameworkStartingEvent;
53 import com.atlassian.plugin.event.events.PluginModuleAvailableEvent;
54 import com.atlassian.plugin.event.events.PluginModuleDisabledEvent;
55 import com.atlassian.plugin.event.events.PluginModuleEnabledEvent;
56 import com.atlassian.plugin.event.events.PluginModuleUnavailableEvent;
57 import com.atlassian.plugin.event.events.PluginUninstalledEvent;
58 import com.atlassian.plugin.event.events.PluginUpgradedEvent;
59 import com.atlassian.plugin.event.impl.DefaultPluginEventManager;
60 import com.atlassian.plugin.event.listeners.FailListener;
61 import com.atlassian.plugin.event.listeners.PassListener;
62 import com.atlassian.plugin.exception.PluginExceptionInterception;
63 import com.atlassian.plugin.factories.LegacyDynamicPluginFactory;
64 import com.atlassian.plugin.factories.PluginFactory;
65 import com.atlassian.plugin.factories.XmlDynamicPluginFactory;
66 import com.atlassian.plugin.hostcontainer.DefaultHostContainer;
67 import com.atlassian.plugin.impl.AbstractDelegatingPlugin;
68 import com.atlassian.plugin.impl.StaticPlugin;
69 import com.atlassian.plugin.impl.UnloadablePlugin;
70 import com.atlassian.plugin.loaders.DirectoryPluginLoader;
71 import com.atlassian.plugin.loaders.DiscardablePluginLoader;
72 import com.atlassian.plugin.loaders.DynamicPluginLoader;
73 import com.atlassian.plugin.loaders.PluginLoader;
74 import com.atlassian.plugin.loaders.SinglePluginLoader;
75 import com.atlassian.plugin.loaders.classloading.DirectoryPluginLoaderUtils;
76 import com.atlassian.plugin.manager.store.DelegatingPluginPersistentStateStore;
77 import com.atlassian.plugin.manager.store.LoadOnlyPluginPersistentStateStore;
78 import com.atlassian.plugin.manager.store.MemoryPluginPersistentStateStore;
79 import com.atlassian.plugin.metadata.ClasspathFilePluginMetadata;
80 import com.atlassian.plugin.mock.MockAnimal;
81 import com.atlassian.plugin.mock.MockAnimalModuleDescriptor;
82 import com.atlassian.plugin.mock.MockBear;
83 import com.atlassian.plugin.mock.MockMineral;
84 import com.atlassian.plugin.mock.MockMineralModuleDescriptor;
85 import com.atlassian.plugin.mock.MockThing;
86 import com.atlassian.plugin.mock.MockVegetableModuleDescriptor;
87 import com.atlassian.plugin.mock.MockVegetableSubclassModuleDescriptor;
88 import com.atlassian.plugin.module.ModuleFactory;
89 import com.atlassian.plugin.parsers.DescriptorParser;
90 import com.atlassian.plugin.parsers.DescriptorParserFactory;
91 import com.atlassian.plugin.predicate.ModuleDescriptorPredicate;
92 import com.atlassian.plugin.predicate.PluginPredicate;
93 import com.atlassian.plugin.repositories.FilePluginInstaller;
94 import com.atlassian.plugin.test.CapturedLogging;
95 import com.atlassian.plugin.test.PluginJarBuilder;
96
97 import com.google.common.base.Function;
98 import com.google.common.base.Predicate;
99 import com.google.common.base.Predicates;
100 import com.google.common.collect.ImmutableList;
101 import com.google.common.collect.ImmutableMap;
102 import com.google.common.collect.ImmutableSet;
103 import com.google.common.collect.Iterables;
104 import com.google.common.collect.Lists;
105
106 import org.apache.commons.io.FileUtils;
107 import org.apache.commons.io.IOUtils;
108 import org.hamcrest.Description;
109 import org.hamcrest.Matcher;
110 import org.hamcrest.TypeSafeMatcher;
111 import org.junit.After;
112 import org.junit.Before;
113 import org.junit.Rule;
114 import org.junit.Test;
115 import org.junit.contrib.java.lang.system.RestoreSystemProperties;
116 import org.junit.rules.ExpectedException;
117 import org.mockito.ArgumentCaptor;
118 import org.mockito.invocation.InvocationOnMock;
119 import org.mockito.stubbing.Answer;
120
121 import static com.atlassian.plugin.loaders.classloading.DirectoryPluginLoaderUtils.PADDINGTON_JAR;
122 import static com.atlassian.plugin.manager.DefaultPluginManager.getLateStartupEnableRetryProperty;
123 import static com.atlassian.plugin.manager.DefaultPluginManager.getStartupOverrideFileProperty;
124 import static com.atlassian.plugin.test.CapturedLogging.didLogWarn;
125 import static com.atlassian.plugin.test.PluginTestUtils.createTempDirectory;
126 import static com.google.common.collect.ImmutableList.copyOf;
127 import static com.google.common.collect.Iterables.filter;
128 import static com.google.common.collect.Iterables.get;
129 import static com.google.common.collect.Iterables.getLast;
130 import static com.google.common.collect.Iterables.indexOf;
131 import static java.util.Collections.singleton;
132 import static org.apache.commons.io.FileUtils.deleteQuietly;
133 import static org.apache.commons.io.FileUtils.writeLines;
134 import static org.hamcrest.MatcherAssert.assertThat;
135 import static org.hamcrest.Matchers.contains;
136 import static org.hamcrest.Matchers.containsInAnyOrder;
137 import static org.hamcrest.Matchers.empty;
138 import static org.hamcrest.Matchers.greaterThan;
139 import static org.hamcrest.Matchers.hasSize;
140 import static org.hamcrest.Matchers.instanceOf;
141 import static org.hamcrest.Matchers.is;
142 import static org.hamcrest.Matchers.lessThan;
143 import static org.hamcrest.Matchers.not;
144 import static org.hamcrest.Matchers.nullValue;
145 import static org.junit.Assert.assertEquals;
146 import static org.junit.Assert.assertFalse;
147 import static org.junit.Assert.assertNotNull;
148 import static org.junit.Assert.assertNull;
149 import static org.junit.Assert.assertTrue;
150 import static org.junit.Assert.fail;
151 import static org.mockito.Matchers.any;
152 import static org.mockito.Matchers.anyString;
153 import static org.mockito.Matchers.isA;
154 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
155 import static org.mockito.Mockito.RETURNS_MOCKS;
156 import static org.mockito.Mockito.doAnswer;
157 import static org.mockito.Mockito.doNothing;
158 import static org.mockito.Mockito.doThrow;
159 import static org.mockito.Mockito.mock;
160 import static org.mockito.Mockito.never;
161 import static org.mockito.Mockito.reset;
162 import static org.mockito.Mockito.times;
163 import static org.mockito.Mockito.verify;
164 import static org.mockito.Mockito.when;
165 import static org.mockito.Mockito.withSettings;
166
167
168
169
170
171
172
173
174
175 public class TestDefaultPluginManager
176 {
177 @Rule
178 public CapturedLogging capturedLogging = new CapturedLogging(DefaultPluginManager.class);
179
180 @Rule
181 public ExpectedException expectedException = ExpectedException.none();
182
183 @Rule
184 public RestoreSystemProperties restoreSystemProperties = new RestoreSystemProperties(
185 getStartupOverrideFileProperty(),
186 getLateStartupEnableRetryProperty());
187
188
189
190
191 protected DefaultPluginManager manager;
192
193 private PluginPersistentStateStore pluginStateStore;
194 private DefaultModuleDescriptorFactory moduleDescriptorFactory;
195
196 private DirectoryPluginLoader directoryPluginLoader;
197 protected PluginEventManager pluginEventManager;
198 private File pluginsDirectory;
199 private File pluginsTestDir;
200 private File temporaryDirectory;
201 private File startupOverrideFile;
202
203 private void createFillAndCleanTempPluginDirectory() throws IOException
204 {
205 final DirectoryPluginLoaderUtils.ScannerDirectories directories = DirectoryPluginLoaderUtils.createFillAndCleanTempPluginDirectory();
206 pluginsDirectory = directories.pluginsDirectory;
207 pluginsTestDir = directories.pluginsTestDir;
208 }
209
210 @Before
211 public void setUp() throws Exception
212 {
213 pluginEventManager = new DefaultPluginEventManager();
214
215 pluginStateStore = new MemoryPluginPersistentStateStore();
216 moduleDescriptorFactory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
217 temporaryDirectory = createTempDirectory(TestDefaultPluginManager.class);
218 startupOverrideFile = new File(temporaryDirectory, "startupOverride.properties");
219 }
220
221 @After
222 public void tearDown() throws Exception
223 {
224 manager = null;
225 moduleDescriptorFactory = null;
226 pluginStateStore = null;
227
228 if (directoryPluginLoader != null)
229 {
230 directoryPluginLoader = null;
231 }
232 deleteQuietly(temporaryDirectory);
233 }
234
235 protected DefaultPluginManager newDefaultPluginManager(PluginLoader... pluginLoaders)
236 {
237 manager = new DefaultPluginManager(pluginStateStore, copyOf(pluginLoaders), moduleDescriptorFactory, pluginEventManager, true);
238 return manager;
239 }
240
241 protected PluginAccessor getPluginAccessor()
242 {
243 return manager;
244 }
245
246 @Test
247 public void testRetrievePlugins() throws PluginParseException
248 {
249 manager = newDefaultPluginManager(
250 new SinglePluginLoader("test-atlassian-plugin.xml"),
251 new SinglePluginLoader("test-disabled-plugin.xml"));
252
253 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
254 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
255 manager.init();
256
257 assertThat(manager.getPlugins(), hasSize(2));
258 assertThat(manager.getEnabledPlugins(), hasSize(1));
259 manager.enablePlugin("test.disabled.plugin");
260 assertThat(manager.getEnabledPlugins(), hasSize(2));
261 }
262
263 private enum FailureMode
264 {
265 FAIL_TO_ENABLE,
266 FAIL_TO_DISABLE
267 }
268
269 private ModuleDescriptor<Object> mockFailingModuleDescriptor(final String completeKey, final FailureMode... failureModes)
270 {
271 return new AbstractModuleDescriptor<Object>(ModuleFactory.LEGACY_MODULE_FACTORY)
272 {
273 @Override
274 public String getKey()
275 {
276 return completeKey.substring(completeKey.lastIndexOf(":") + 1, completeKey.length());
277 }
278
279 @Override
280 public String getCompleteKey()
281 {
282 return completeKey;
283 }
284
285 @Override
286 public void enabled()
287 {
288 if (Lists.newArrayList(failureModes).contains(FailureMode.FAIL_TO_ENABLE))
289 throw new IllegalArgumentException("Cannot enable");
290 }
291
292 @Override
293 public void disabled()
294 {
295 if (Lists.newArrayList(failureModes).contains(FailureMode.FAIL_TO_DISABLE))
296 throw new IllegalArgumentException("Cannot disable");
297 }
298
299 @Override
300 public Object getModule()
301 {
302 return null;
303 }
304 };
305 }
306
307 @Test
308 public void testEnableModuleFailed() throws PluginParseException
309 {
310 final PluginLoader mockPluginLoader = mock(PluginLoader.class);
311 final ModuleDescriptor<Object> badModuleDescriptor = mockFailingModuleDescriptor("foo:bar", FailureMode.FAIL_TO_ENABLE);
312
313 final AbstractModuleDescriptor goodModuleDescriptor = mock(AbstractModuleDescriptor.class);
314 when(goodModuleDescriptor.getKey()).thenReturn("baz");
315 when(goodModuleDescriptor.getCompleteKey()).thenReturn("foo:baz");
316 when(goodModuleDescriptor.isEnabledByDefault()).thenReturn(true);
317
318 Plugin plugin = mockStaticPlugin("foo", goodModuleDescriptor, badModuleDescriptor);
319
320 when(mockPluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Collections.singletonList(plugin));
321
322 pluginEventManager.register(new FailListener(PluginEnabledEvent.class));
323
324 MyModuleDisabledListener listener = new MyModuleDisabledListener(goodModuleDescriptor);
325 pluginEventManager.register(listener);
326
327 manager = newDefaultPluginManager(mockPluginLoader);
328 manager.init();
329
330 assertThat(getPluginAccessor().getPlugins(), hasSize(1));
331 assertThat(getPluginAccessor().getEnabledPlugins(), hasSize(0));
332 plugin = getPluginAccessor().getPlugin("foo");
333 assertFalse(plugin.getPluginState() == PluginState.ENABLED);
334 assertTrue(plugin instanceof UnloadablePlugin);
335 assertTrue(listener.isCalled());
336 }
337
338 public static class MyModuleDisabledListener
339 {
340 private final ModuleDescriptor goodModuleDescriptor;
341 private volatile boolean disableCalled = false;
342
343 public MyModuleDisabledListener(ModuleDescriptor goodModuleDescriptor)
344 {
345 this.goodModuleDescriptor = goodModuleDescriptor;
346 }
347
348 @PluginEventListener
349 public void onDisable(PluginModuleDisabledEvent evt)
350 {
351 if (evt.getModule().equals(goodModuleDescriptor))
352 {
353 disableCalled = true;
354 }
355 }
356
357 public boolean isCalled()
358 {
359 return disableCalled;
360 }
361 }
362
363 @Test
364 public void testEnabledModuleOutOfSyncWithPlugin() throws PluginParseException
365 {
366 final PluginLoader mockPluginLoader = mock(PluginLoader.class);
367 Plugin plugin = new StaticPlugin();
368 plugin.setKey("foo");
369 plugin.setEnabledByDefault(true);
370 plugin.setPluginInformation(new PluginInformation());
371
372 when(mockPluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Collections.singletonList(plugin));
373
374 manager = newDefaultPluginManager(mockPluginLoader);
375 manager.init();
376
377 assertThat(getPluginAccessor().getPlugins(), hasSize(1));
378 assertThat(getPluginAccessor().getEnabledPlugins(), hasSize(1));
379 plugin = getPluginAccessor().getPlugin("foo");
380 assertTrue(plugin.getPluginState() == PluginState.ENABLED);
381 assertTrue(getPluginAccessor().isPluginEnabled("foo"));
382 plugin.disable();
383 assertFalse(plugin.getPluginState() == PluginState.ENABLED);
384 assertFalse(getPluginAccessor().isPluginEnabled("foo"));
385 }
386
387 @Test
388 public void testDisablePluginModuleWithCannotDisableAnnotation()
389 {
390 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
391 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
392 moduleDescriptorFactory.addModuleDescriptor("bullshit", MockUnusedModuleDescriptor.class);
393 moduleDescriptorFactory.addModuleDescriptor("vegetable", MockVegetableModuleDescriptor.class);
394
395 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
396 manager.init();
397
398 final String pluginKey = "test.atlassian.plugin";
399 final String disablableModuleKey = pluginKey + ":bear";
400 final String moduleKey = pluginKey + ":veg";
401
402
403 manager.disablePluginModule(disablableModuleKey);
404 assertNull(getPluginAccessor().getEnabledPluginModule(disablableModuleKey));
405
406
407 manager.disablePluginModule(moduleKey);
408 assertNotNull(getPluginAccessor().getEnabledPluginModule(moduleKey));
409 }
410
411 @Test
412 public void testDisablePluginModuleWithCannotDisableAnnotationInSuperclass()
413 {
414 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
415 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
416 moduleDescriptorFactory.addModuleDescriptor("bullshit", MockUnusedModuleDescriptor.class);
417 moduleDescriptorFactory.addModuleDescriptor("vegetable", MockVegetableModuleDescriptor.class);
418 moduleDescriptorFactory.addModuleDescriptor("vegetableSubclass", MockVegetableSubclassModuleDescriptor.class);
419
420 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
421 manager.init();
422
423 final String pluginKey = "test.atlassian.plugin";
424 final String disablableModuleKey = pluginKey + ":bear";
425 final String moduleKey = pluginKey + ":vegSubclass";
426
427
428 manager.disablePluginModule(disablableModuleKey);
429 assertNull(getPluginAccessor().getEnabledPluginModule(disablableModuleKey));
430
431
432 manager.disablePluginModule(moduleKey);
433 assertNotNull(getPluginAccessor().getEnabledPluginModule(moduleKey));
434 }
435
436 @Test
437 public void testEnabledDisabledRetrieval() throws PluginParseException
438 {
439 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
440 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
441 moduleDescriptorFactory.addModuleDescriptor("bullshit", MockUnusedModuleDescriptor.class);
442 moduleDescriptorFactory.addModuleDescriptor("vegetable", MockVegetableModuleDescriptor.class);
443
444 final PassListener enabledListener = new PassListener(PluginEnabledEvent.class);
445 final PassListener disabledListener = new PassListener(PluginDisabledEvent.class);
446 pluginEventManager.register(enabledListener);
447 pluginEventManager.register(disabledListener);
448
449 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
450 manager.init();
451
452
453 assertNull(getPluginAccessor().getPlugin("bull:shit"));
454 assertNull(getPluginAccessor().getEnabledPlugin("bull:shit"));
455 assertNull(getPluginAccessor().getPluginModule("bull:shit"));
456 assertNull(getPluginAccessor().getEnabledPluginModule("bull:shit"));
457 assertTrue(getPluginAccessor().getEnabledModuleDescriptorsByClass(NothingModuleDescriptor.class).isEmpty());
458 assertTrue(getPluginAccessor().getEnabledModuleDescriptorsByType("bullshit").isEmpty());
459
460 final String pluginKey = "test.atlassian.plugin";
461 final String moduleKey = pluginKey + ":bear";
462
463
464 assertNotNull(getPluginAccessor().getPlugin(pluginKey));
465 assertNotNull(getPluginAccessor().getEnabledPlugin(pluginKey));
466 assertNotNull(getPluginAccessor().getPluginModule(moduleKey));
467 assertNotNull(getPluginAccessor().getEnabledPluginModule(moduleKey));
468 assertNull(getPluginAccessor().getEnabledPluginModule(pluginKey + ":shit"));
469 assertFalse(getPluginAccessor().getEnabledModuleDescriptorsByClass(MockAnimalModuleDescriptor.class).isEmpty());
470 assertFalse(getPluginAccessor().getEnabledModuleDescriptorsByType("animal").isEmpty());
471 assertFalse(getPluginAccessor().getEnabledModulesByClass(MockBear.class).isEmpty());
472 assertEquals(new MockBear(), getPluginAccessor().getEnabledModulesByClass(MockBear.class).get(0));
473 enabledListener.assertCalled();
474
475
476 manager.disablePlugin(pluginKey);
477 assertNotNull(getPluginAccessor().getPlugin(pluginKey));
478 assertNull(getPluginAccessor().getEnabledPlugin(pluginKey));
479 assertNotNull(getPluginAccessor().getPluginModule(moduleKey));
480 assertNull(getPluginAccessor().getEnabledPluginModule(moduleKey));
481 assertTrue(getPluginAccessor().getEnabledModulesByClass(com.atlassian.plugin.mock.MockBear.class).isEmpty());
482 assertTrue(getPluginAccessor().getEnabledModuleDescriptorsByClass(MockAnimalModuleDescriptor.class).isEmpty());
483 assertTrue(getPluginAccessor().getEnabledModuleDescriptorsByType("animal").isEmpty());
484 disabledListener.assertCalled();
485
486
487 manager.enablePlugin(pluginKey);
488 assertNotNull(getPluginAccessor().getPlugin(pluginKey));
489 assertNotNull(getPluginAccessor().getEnabledPlugin(pluginKey));
490 assertNotNull(getPluginAccessor().getPluginModule(moduleKey));
491 assertNotNull(getPluginAccessor().getEnabledPluginModule(moduleKey));
492 assertFalse(getPluginAccessor().getEnabledModulesByClass(com.atlassian.plugin.mock.MockBear.class).isEmpty());
493 assertFalse(getPluginAccessor().getEnabledModuleDescriptorsByClass(MockAnimalModuleDescriptor.class).isEmpty());
494 assertFalse(getPluginAccessor().getEnabledModuleDescriptorsByType("animal").isEmpty());
495 enabledListener.assertCalled();
496
497
498 pluginEventManager.register(new FailListener(PluginEnabledEvent.class));
499 manager.disablePluginModule(moduleKey);
500 assertNotNull(getPluginAccessor().getPlugin(pluginKey));
501 assertNotNull(getPluginAccessor().getEnabledPlugin(pluginKey));
502 assertNotNull(getPluginAccessor().getPluginModule(moduleKey));
503 assertNull(getPluginAccessor().getEnabledPluginModule(moduleKey));
504 assertTrue(getPluginAccessor().getEnabledModulesByClass(com.atlassian.plugin.mock.MockBear.class).isEmpty());
505 assertTrue(getPluginAccessor().getEnabledModuleDescriptorsByClass(MockAnimalModuleDescriptor.class).isEmpty());
506 assertTrue(getPluginAccessor().getEnabledModuleDescriptorsByType("animal").isEmpty());
507
508
509 pluginEventManager.register(new FailListener(PluginDisabledEvent.class));
510 manager.enablePluginModule(moduleKey);
511 assertNotNull(getPluginAccessor().getPlugin(pluginKey));
512 assertNotNull(getPluginAccessor().getEnabledPlugin(pluginKey));
513 assertNotNull(getPluginAccessor().getPluginModule(moduleKey));
514 assertNotNull(getPluginAccessor().getEnabledPluginModule(moduleKey));
515 assertFalse(getPluginAccessor().getEnabledModulesByClass(com.atlassian.plugin.mock.MockBear.class).isEmpty());
516 assertFalse(getPluginAccessor().getEnabledModuleDescriptorsByClass(MockAnimalModuleDescriptor.class).isEmpty());
517 assertFalse(getPluginAccessor().getEnabledModuleDescriptorsByType("animal").isEmpty());
518 }
519
520 @Test
521 public void testDuplicatePluginKeysAreIgnored() throws PluginParseException
522 {
523 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
524
525 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"), new SinglePluginLoader("test-atlassian-plugin.xml"));
526 manager.init();
527 assertThat(getPluginAccessor().getEnabledPlugins(), hasSize(1));
528 }
529
530 @Test
531 public void testDuplicateSnapshotVersionsAreNotLoaded() throws PluginParseException
532 {
533 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
534
535 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-snapshot-plugin.xml"), new SinglePluginLoader("test-atlassian-snapshot-plugin.xml"));
536 manager.init();
537 assertThat(getPluginAccessor().getEnabledPlugins(), hasSize(1));
538 }
539
540 @Test
541 public void testChangedSnapshotVersionIsLoaded() throws PluginParseException
542 {
543 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
544
545 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-snapshot-plugin.xml"), new SinglePluginLoader("test-atlassian-snapshot-plugin-changed-same-version.xml"));
546 manager.init();
547 assertThat(getPluginAccessor().getEnabledPlugins(), hasSize(1));
548 final Plugin plugin = getPluginAccessor().getPlugin("test.atlassian.plugin");
549 assertEquals("1.1-SNAPSHOT", plugin.getPluginInformation().getVersion());
550 assertEquals("This plugin descriptor has been changed!", plugin.getPluginInformation().getDescription());
551 }
552
553 @Test
554 public void testLoadOlderDuplicatePlugin()
555 {
556 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
557 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
558
559 manager = newDefaultPluginManager(new MultiplePluginLoader("test-atlassian-plugin-newer.xml"), new MultiplePluginLoader("test-atlassian-plugin.xml", "test-another-plugin.xml"));
560 manager.init();
561 assertThat(getPluginAccessor().getEnabledPlugins(), hasSize(2));
562 }
563
564 @Test
565 public void testLoadOlderDuplicatePluginDoesNotTryToEnableIt()
566 {
567 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
568 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
569
570 final Plugin plugin = new StaticPlugin()
571 {
572 @Override
573 protected PluginState enableInternal()
574 {
575 fail("enable() must never be called on a earlier version of plugin when later version is installed");
576 return null;
577 }
578
579 @Override
580 public void disableInternal()
581 {
582 fail("disable() must never be called on a earlier version of plugin when later version is installed");
583 }
584 };
585 plugin.setKey("test.atlassian.plugin");
586 plugin.getPluginInformation().setVersion("1.0");
587
588 PluginLoader pluginLoader = new MultiplePluginLoader("test-atlassian-plugin-newer.xml");
589 manager = newDefaultPluginManager(pluginLoader);
590 manager.init();
591 manager.addPlugins(pluginLoader, Collections.singletonList(plugin));
592 }
593
594 @Test
595 public void testLoadNewerDuplicatePlugin()
596 {
597 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
598 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
599
600 manager = newDefaultPluginManager(
601 new SinglePluginLoader("test-atlassian-plugin.xml"),
602 new SinglePluginLoader("test-atlassian-plugin-newer.xml"));
603 manager.init();
604 assertThat(getPluginAccessor().getEnabledPlugins(), hasSize(1));
605 final Plugin plugin = getPluginAccessor().getPlugin("test.atlassian.plugin");
606 assertEquals("1.1", plugin.getPluginInformation().getVersion());
607 }
608
609 @Test
610 public void testLoadNewerDuplicateDynamicPluginPreservesPluginState() throws PluginParseException
611 {
612 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
613 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
614
615 manager = newDefaultPluginManager(new SinglePluginLoaderWithRemoval("test-atlassian-plugin.xml"));
616 manager.init();
617
618 pluginStateStore.save(PluginPersistentState.Builder.create(pluginStateStore.load()).setEnabled(manager.getPlugin("test.atlassian.plugin"),
619 false).toState());
620
621 assertFalse(getPluginAccessor().isPluginEnabled("test.atlassian.plugin"));
622 manager.shutdown();
623
624 manager = newDefaultPluginManager(new SinglePluginLoaderWithRemoval("test-atlassian-plugin-newer.xml"));
625 manager.init();
626
627 final Plugin plugin = getPluginAccessor().getPlugin("test.atlassian.plugin");
628 assertEquals("1.1", plugin.getPluginInformation().getVersion());
629 assertFalse(getPluginAccessor().isPluginEnabled("test.atlassian.plugin"));
630 }
631
632 @Test
633 public void testLoadNewerDuplicateDynamicPluginPreservesModuleState() throws PluginParseException
634 {
635 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
636 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
637
638 manager = newDefaultPluginManager(new SinglePluginLoaderWithRemoval("test-atlassian-plugin.xml"));
639 manager.init();
640
641 pluginStateStore.save(PluginPersistentState.Builder.create(pluginStateStore.load()).setEnabled(
642 getPluginAccessor().getPluginModule("test.atlassian.plugin:bear"), false).toState());
643
644 manager.shutdown();
645
646 manager = newDefaultPluginManager(new SinglePluginLoaderWithRemoval("test-atlassian-plugin-newer.xml"));
647 manager.init();
648
649 final Plugin plugin = getPluginAccessor().getPlugin("test.atlassian.plugin");
650 assertEquals("1.1", plugin.getPluginInformation().getVersion());
651 assertFalse(getPluginAccessor().isPluginModuleEnabled("test.atlassian.plugin:bear"));
652 assertTrue(getPluginAccessor().isPluginModuleEnabled("test.atlassian.plugin:gold"));
653 }
654
655 @Test
656 public void testLoadChangedDynamicPluginWithSameVersionNumberDoesNotReplaceExisting() throws PluginParseException
657 {
658 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
659 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
660
661 manager = newDefaultPluginManager(
662 new SinglePluginLoaderWithRemoval("test-atlassian-plugin.xml"),
663 new SinglePluginLoaderWithRemoval("test-atlassian-plugin-changed-same-version.xml"));
664 manager.init();
665
666 final Plugin plugin = getPluginAccessor().getPlugin("test.atlassian.plugin");
667 assertEquals("Test Plugin", plugin.getName());
668 }
669
670 @Test
671 public void testGetPluginsWithPluginMatchingPluginPredicate() throws Exception
672 {
673 final Plugin plugin = mockTestPlugin(Collections.emptyList());
674
675 final PluginPredicate mockPluginPredicate = mock(PluginPredicate.class);
676 when(mockPluginPredicate.matches(plugin)).thenReturn(true);
677
678 manager = newDefaultPluginManager();
679 manager.addPlugins(null, Collections.singletonList(plugin));
680 final Collection<Plugin> plugins = getPluginAccessor().getPlugins(mockPluginPredicate);
681
682 assertThat(plugins, hasSize(1));
683 assertTrue(plugins.contains(plugin));
684 verify(mockPluginPredicate).matches(any(Plugin.class));
685 }
686
687 @Test
688 public void testGetPluginsWithPluginNotMatchingPluginPredicate() throws Exception
689 {
690 final Plugin plugin = mockTestPlugin(Collections.emptyList());
691
692 final PluginPredicate mockPluginPredicate = mock(PluginPredicate.class);
693
694 manager = newDefaultPluginManager();
695 manager.addPlugins(null, Collections.singletonList(plugin));
696 final Collection<Plugin> plugins = getPluginAccessor().getPlugins(mockPluginPredicate);
697
698 assertThat(plugins, hasSize(0));
699 }
700
701 @Test
702 public void testGetPluginModulesWithModuleMatchingPredicate() throws Exception
703 {
704 final MockThing module = new MockThing()
705 {
706 };
707 @SuppressWarnings("unchecked")
708 final ModuleDescriptor<MockThing> moduleDescriptor = mock(ModuleDescriptor.class);
709 when(moduleDescriptor.getModule()).thenReturn(module);
710 when(moduleDescriptor.getCompleteKey()).thenReturn("some-plugin-key:module");
711 when(moduleDescriptor.isEnabledByDefault()).thenReturn(true);
712
713 final Plugin plugin = mockTestPlugin(Collections.singleton(moduleDescriptor));
714 when(plugin.getModuleDescriptor("module")).thenReturn((ModuleDescriptor) moduleDescriptor);
715
716 final ModuleDescriptorPredicate mockModulePredicate = mock(ModuleDescriptorPredicate.class);
717 when(mockModulePredicate.matches(moduleDescriptor)).thenReturn(true);
718
719 manager = newDefaultPluginManager();
720 manager.addPlugins(null, Collections.singletonList(plugin));
721 @SuppressWarnings("unchecked")
722 final ModuleDescriptorPredicate<MockThing> predicate = (ModuleDescriptorPredicate<MockThing>) mockModulePredicate;
723 final Collection<MockThing> modules = getPluginAccessor().getModules(predicate);
724
725 assertThat(modules, hasSize(1));
726 assertTrue(modules.contains(module));
727
728 verify(mockModulePredicate).matches(moduleDescriptor);
729 }
730
731 @Test
732 public void testGetPluginModulesWithGetModuleThrowingException() throws Exception
733 {
734 final Plugin badPlugin = new StaticPlugin();
735 badPlugin.setKey("bad");
736 final MockModuleDescriptor<Object> badDescriptor = new MockModuleDescriptor<Object>(badPlugin, "bad", new Object())
737 {
738 @Override
739 public Object getModule()
740 {
741 throw new RuntimeException();
742 }
743 };
744 badPlugin.addModuleDescriptor(badDescriptor);
745
746 final Plugin goodPlugin = new StaticPlugin();
747 goodPlugin.setKey("good");
748 final MockModuleDescriptor<Object> goodDescriptor = new MockModuleDescriptor<Object>(goodPlugin, "good", new Object());
749 goodPlugin.addModuleDescriptor(goodDescriptor);
750
751 manager = newDefaultPluginManager();
752 manager.addPlugins(null, Arrays.asList(goodPlugin, badPlugin));
753 manager.enablePlugin("bad");
754 manager.enablePlugin("good");
755
756 assertTrue(getPluginAccessor().isPluginEnabled("bad"));
757 assertTrue(getPluginAccessor().isPluginEnabled("good"));
758 final Collection<Object> modules = getPluginAccessor().getEnabledModulesByClass(Object.class);
759
760 assertThat(modules, hasSize(1));
761 assertFalse(getPluginAccessor().isPluginEnabled("bad"));
762 assertTrue(getPluginAccessor().isPluginEnabled("good"));
763 }
764
765 @Test
766 public void testGetPluginModulesWith2GetModulesThrowingExceptionOnlyNotifiesOnce() throws Exception
767 {
768 final Plugin badPlugin = new StaticPlugin();
769 badPlugin.setKey("bad");
770 final MockModuleDescriptor<Object> badDescriptor = new MockModuleDescriptor<Object>(badPlugin, "bad", new Object())
771 {
772 @Override
773 public Object getModule()
774 {
775 throw new RuntimeException();
776 }
777 };
778 badPlugin.addModuleDescriptor(badDescriptor);
779 final MockModuleDescriptor<Object> badDescriptor2 = new MockModuleDescriptor<Object>(badPlugin, "bad2", new Object())
780 {
781 @Override
782 public Object getModule()
783 {
784 throw new RuntimeException();
785 }
786 };
787 badPlugin.addModuleDescriptor(badDescriptor2);
788
789 final Plugin goodPlugin = new StaticPlugin();
790 goodPlugin.setKey("good");
791 final MockModuleDescriptor<Object> goodDescriptor = new MockModuleDescriptor<Object>(goodPlugin, "good", new Object());
792 goodPlugin.addModuleDescriptor(goodDescriptor);
793 DisabledPluginCounter counter = new DisabledPluginCounter();
794 pluginEventManager.register(counter);
795
796 manager = newDefaultPluginManager();
797 manager.addPlugins(null, Arrays.asList(goodPlugin, badPlugin));
798 manager.enablePlugin("bad");
799 manager.enablePlugin("good");
800
801 assertTrue(getPluginAccessor().isPluginEnabled("bad"));
802 assertTrue(getPluginAccessor().isPluginEnabled("good"));
803 final Collection<Object> modules = getPluginAccessor().getEnabledModulesByClass(Object.class);
804
805 assertThat(modules, hasSize(1));
806 assertFalse(getPluginAccessor().isPluginEnabled("bad"));
807 assertTrue(getPluginAccessor().isPluginEnabled("good"));
808 assertEquals(1, counter.disableCount);
809 }
810
811 public static class DisabledPluginCounter
812 {
813 int disableCount = 0;
814 @PluginEventListener
815 public void consume(PluginDisabledEvent element)
816 {
817 disableCount++;
818 }
819 }
820
821 @Test
822 public void testGetPluginModulesWithModuleNotMatchingPredicate() throws Exception
823 {
824 @SuppressWarnings("unchecked")
825 final ModuleDescriptor<MockThing> moduleDescriptor = mock(ModuleDescriptor.class);
826 when(moduleDescriptor.getCompleteKey()).thenReturn("some-plugin-key:module");
827 when(moduleDescriptor.isEnabledByDefault()).thenReturn(true);
828
829 final Plugin plugin = mockTestPlugin(Collections.singleton(moduleDescriptor));
830 when(plugin.getModuleDescriptor("module")).thenReturn((ModuleDescriptor) moduleDescriptor);
831
832 @SuppressWarnings("unchecked")
833 final ModuleDescriptorPredicate<MockThing> predicate = mock(ModuleDescriptorPredicate.class);
834
835 manager = newDefaultPluginManager();
836 manager.addPlugins(null, Collections.singletonList(plugin));
837 final Collection<MockThing> modules = getPluginAccessor().getModules(predicate);
838
839 assertThat(modules, hasSize(0));
840
841 verify(predicate).matches(moduleDescriptor);
842 }
843
844 @Test
845 public void testGetPluginModuleDescriptorWithModuleMatchingPredicate() throws Exception
846 {
847 @SuppressWarnings("unchecked")
848 final ModuleDescriptor<MockThing> moduleDescriptor = mock(ModuleDescriptor.class);
849 when(moduleDescriptor.getCompleteKey()).thenReturn("some-plugin-key:module");
850 when(moduleDescriptor.isEnabledByDefault()).thenReturn(true);
851
852 final Plugin plugin = mockTestPlugin(Collections.singleton(moduleDescriptor));
853 when(plugin.getModuleDescriptor("module")).thenReturn((ModuleDescriptor) moduleDescriptor);
854
855 @SuppressWarnings("unchecked")
856 final ModuleDescriptorPredicate<MockThing> predicate = mock(ModuleDescriptorPredicate.class);
857 when(predicate.matches(moduleDescriptor)).thenReturn(true);
858
859 manager = newDefaultPluginManager();
860 manager.addPlugins(null, Collections.singletonList(plugin));
861 final Collection<ModuleDescriptor<MockThing>> modules = getPluginAccessor().getModuleDescriptors(predicate);
862
863 assertThat(modules, hasSize(1));
864 assertTrue(modules.contains(moduleDescriptor));
865
866 verify(predicate).matches(moduleDescriptor);
867 }
868
869 @Test
870 public void testGetPluginModuleDescriptorsWithModuleNotMatchingPredicate() throws Exception
871 {
872 @SuppressWarnings("unchecked")
873 final ModuleDescriptor<MockThing> moduleDescriptor = mock(ModuleDescriptor.class);
874 when(moduleDescriptor.getCompleteKey()).thenReturn("some-plugin-key:module");
875 when(moduleDescriptor.isEnabledByDefault()).thenReturn(true);
876
877 final Plugin plugin = mockTestPlugin(Collections.singleton(moduleDescriptor));
878 when(plugin.getModuleDescriptor("module")).thenReturn((ModuleDescriptor) moduleDescriptor);
879
880 @SuppressWarnings("unchecked")
881 final ModuleDescriptorPredicate<MockThing> predicate = mock(ModuleDescriptorPredicate.class);
882
883 manager = newDefaultPluginManager();
884 manager.addPlugins(null, Collections.singletonList(plugin));
885 final Collection<MockThing> modules = getPluginAccessor().getModules(predicate);
886
887 assertThat(modules, hasSize(0));
888
889 verify(predicate).matches(moduleDescriptor);
890 }
891
892 private Plugin mockTestPlugin(Collection moduleDescriptors)
893 {
894 final Plugin mockPlugin = mock(Plugin.class);
895 when(mockPlugin.getKey()).thenReturn("some-plugin-key");
896 when(mockPlugin.isEnabledByDefault()).thenReturn(true);
897 when(mockPlugin.isEnabled()).thenReturn(true);
898 when(mockPlugin.getPluginState()).thenReturn(PluginState.ENABLED);
899 when(mockPlugin.getModuleDescriptors()).thenReturn(moduleDescriptors);
900 return mockPlugin;
901 }
902
903 @Test
904 public void testGetPluginAndModules() throws PluginParseException
905 {
906 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
907 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
908
909 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
910 manager.init();
911
912 final Plugin plugin = manager.getPlugin("test.atlassian.plugin");
913 assertNotNull(plugin);
914 assertEquals("Test Plugin", plugin.getName());
915
916 final ModuleDescriptor<?> bear = plugin.getModuleDescriptor("bear");
917 assertEquals(bear, getPluginAccessor().getPluginModule("test.atlassian.plugin:bear"));
918 }
919
920 @Test
921 public void testGetModuleByModuleClassOneFound() throws PluginParseException
922 {
923 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
924 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
925
926 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
927 manager.init();
928
929 final List<MockAnimalModuleDescriptor> animalDescriptors = getPluginAccessor().getEnabledModuleDescriptorsByClass(MockAnimalModuleDescriptor.class);
930 assertNotNull(animalDescriptors);
931 assertThat(animalDescriptors, hasSize(1));
932 final ModuleDescriptor<MockAnimal> moduleDescriptor = animalDescriptors.iterator().next();
933 assertEquals("Bear Animal", moduleDescriptor.getName());
934
935 final List<MockMineralModuleDescriptor> mineralDescriptors = getPluginAccessor().getEnabledModuleDescriptorsByClass(MockMineralModuleDescriptor.class);
936 assertNotNull(mineralDescriptors);
937 assertThat(mineralDescriptors, hasSize(1));
938 final ModuleDescriptor<MockMineral> mineralDescriptor = mineralDescriptors.iterator().next();
939 assertEquals("Bar", mineralDescriptor.getName());
940 }
941
942 @Test
943 public void testGetModuleByModuleClassAndDescriptor() throws PluginParseException
944 {
945 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
946 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
947
948 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
949 manager.init();
950
951 final Collection<MockBear> bearModules = getPluginAccessor().getEnabledModulesByClassAndDescriptor(
952 new Class[]{MockAnimalModuleDescriptor.class, MockMineralModuleDescriptor.class}, MockBear.class);
953 assertNotNull(bearModules);
954 assertThat(bearModules, hasSize(1));
955 assertTrue(bearModules.iterator().next() instanceof MockBear);
956
957 final Collection<MockBear> noModules = getPluginAccessor().getEnabledModulesByClassAndDescriptor(new Class[]{}, MockBear.class);
958 assertNotNull(noModules);
959 assertThat(noModules, hasSize(0));
960
961 final Collection<MockThing> mockThings = getPluginAccessor().getEnabledModulesByClassAndDescriptor(
962 new Class[]{MockAnimalModuleDescriptor.class, MockMineralModuleDescriptor.class}, MockThing.class);
963 assertNotNull(mockThings);
964 assertThat(mockThings, hasSize(2));
965 assertTrue(mockThings.iterator().next() instanceof MockThing);
966 assertTrue(mockThings.iterator().next() instanceof MockThing);
967
968 final Collection<MockThing> mockThingsFromMineral = getPluginAccessor().getEnabledModulesByClassAndDescriptor(
969 new Class[]{MockMineralModuleDescriptor.class}, MockThing.class);
970 assertNotNull(mockThingsFromMineral);
971 assertThat(mockThingsFromMineral, hasSize(1));
972 final Object o = mockThingsFromMineral.iterator().next();
973 assertTrue(o instanceof MockMineral);
974 }
975
976 @Test
977 public void testGetModuleByModuleClassNoneFound() throws PluginParseException
978 {
979 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
980 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
981
982 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
983 manager.init();
984
985 class MockSilver implements MockMineral
986 {
987 public int getWeight()
988 {
989 return 3;
990 }
991 }
992
993 final Collection<MockSilver> descriptors = getPluginAccessor().getEnabledModulesByClass(MockSilver.class);
994 assertNotNull(descriptors);
995 assertTrue(descriptors.isEmpty());
996 }
997
998 @Test
999 public void testGetModuleDescriptorsByType() throws PluginParseException
1000 {
1001 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
1002 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
1003
1004 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
1005 manager.init();
1006
1007 Collection<ModuleDescriptor<MockThing>> descriptors = getPluginAccessor().getEnabledModuleDescriptorsByType("animal");
1008 assertNotNull(descriptors);
1009 assertThat(descriptors, hasSize(1));
1010 ModuleDescriptor<MockThing> moduleDescriptor = descriptors.iterator().next();
1011 assertEquals("Bear Animal", moduleDescriptor.getName());
1012
1013 descriptors = getPluginAccessor().getEnabledModuleDescriptorsByType("mineral");
1014 assertNotNull(descriptors);
1015 assertThat(descriptors, hasSize(1));
1016 moduleDescriptor = descriptors.iterator().next();
1017 assertEquals("Bar", moduleDescriptor.getName());
1018
1019 try
1020 {
1021 getPluginAccessor().getEnabledModuleDescriptorsByType("foobar");
1022 }
1023 catch (final IllegalArgumentException e)
1024 {
1025 fail("Shouldn't have thrown exception.");
1026 }
1027 }
1028
1029 @Test
1030 public void testRetrievingDynamicResources() throws PluginParseException, IOException
1031 {
1032 createFillAndCleanTempPluginDirectory();
1033
1034 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1035
1036 final InputStream is = manager.getPluginResourceAsStream("test.atlassian.plugin.classloaded", "atlassian-plugin.xml");
1037 assertNotNull(is);
1038 IOUtils.closeQuietly(is);
1039 }
1040
1041 @Test
1042 public void testGetDynamicPluginClass() throws IOException, PluginParseException
1043 {
1044 createFillAndCleanTempPluginDirectory();
1045
1046 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1047 try
1048 {
1049 manager.getDynamicPluginClass("com.atlassian.plugin.mock.MockPooh");
1050 }
1051 catch (final ClassNotFoundException e)
1052 {
1053 fail(e.getMessage());
1054 }
1055 }
1056
1057 @Test
1058 public void testGetEnabledPluginsDoesNotReturnEnablingPlugins() throws Exception
1059 {
1060 final PluginLoader mockPluginLoader = mock(PluginLoader.class);
1061
1062 final Plugin firstPlugin = new StaticPlugin();
1063 firstPlugin.setKey("first");
1064 firstPlugin.setEnabledByDefault(false);
1065 firstPlugin.setPluginInformation(new PluginInformation());
1066
1067 manager = newDefaultPluginManager();
1068 manager.enablePluginState(firstPlugin, pluginStateStore);
1069
1070 final Plugin secondPlugin = new StaticPlugin()
1071 {
1072 public PluginState enableInternal()
1073 {
1074 try
1075 {
1076
1077 assertThat(manager.getPlugins(), hasSize(2));
1078 assertThat("First plugin should not be enabled", manager.getEnabledPlugins(), hasSize(0));
1079 }
1080 catch (Exception e)
1081 {
1082 throw new RuntimeException(e);
1083 }
1084 return PluginState.ENABLED;
1085 }
1086
1087 public void disableInternal()
1088 {
1089
1090 }
1091 };
1092 secondPlugin.setKey("second");
1093 secondPlugin.setEnabledByDefault(false);
1094 secondPlugin.setPluginInformation(new PluginInformation());
1095 manager.enablePluginState(secondPlugin, pluginStateStore);
1096
1097 when(mockPluginLoader.loadAllPlugins(any(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(firstPlugin, secondPlugin));
1098
1099 manager = newDefaultPluginManager(mockPluginLoader);
1100 manager.init();
1101 }
1102
1103 @Test
1104 public void testFindingNewPlugins() throws PluginParseException, IOException
1105 {
1106 createFillAndCleanTempPluginDirectory();
1107
1108
1109 final File paddington = new File(pluginsTestDir, PADDINGTON_JAR);
1110 paddington.delete();
1111
1112 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1113
1114 assertThat(manager.getPlugins(), hasSize(1));
1115 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded2"));
1116
1117
1118 FileUtils.copyDirectory(pluginsDirectory, pluginsTestDir);
1119
1120 final int foundFirstScan = manager.scanForNewPlugins();
1121 assertThat(foundFirstScan, is(1));
1122 assertThat(manager.getPlugins(), hasSize(2));
1123 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded2"));
1124 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded"));
1125
1126 final int foundSecondScan = manager.scanForNewPlugins();
1127 assertThat(foundSecondScan, is(0));
1128 assertThat(manager.getPlugins(), hasSize(2));
1129 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded2"));
1130 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded"));
1131 }
1132
1133 @Test
1134 public void testFindingNewPluginsNotLoadingRestartRequiredDescriptors() throws PluginParseException, IOException
1135 {
1136 createFillAndCleanTempPluginDirectory();
1137
1138 final DynamicSinglePluginLoader dynamicSinglePluginLoader = new DynamicSinglePluginLoader("test.atlassian.plugin", "test-requiresRestart-plugin.xml");
1139 manager = makeClassLoadingPluginManager(dynamicSinglePluginLoader);
1140
1141 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1142
1143 assertThat(manager.getPlugins(), hasSize(2));
1144 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded2"));
1145
1146
1147 dynamicSinglePluginLoader.canLoad.set(true);
1148
1149 manager.scanForNewPlugins();
1150 assertThat(manager.getPlugins(), hasSize(3));
1151 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded2"));
1152 assertNotNull(manager.getPlugin("test.atlassian.plugin"));
1153
1154 final Plugin plugin = manager.getPlugin("test.atlassian.plugin");
1155 assertTrue(plugin instanceof UnloadablePlugin);
1156 assertTrue(((UnloadablePlugin)plugin).getErrorText().contains("foo"));
1157
1158 assertEquals(PluginRestartState.INSTALL, manager.getPluginRestartState("test.atlassian.plugin"));
1159 }
1160
1161
1162
1163
1164 @Test
1165 public void testFindingUpgradePluginsNotLoadingRestartRequiredDescriptors() throws PluginParseException, IOException
1166 {
1167 createFillAndCleanTempPluginDirectory();
1168
1169 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1170
1171 final DynamicSinglePluginLoader dynamicSinglePluginLoader = new DynamicSinglePluginLoader("test.atlassian.plugin.classloaded2", "test-requiresRestartWithUpgrade-plugin.xml");
1172 manager = makeClassLoadingPluginManager(dynamicSinglePluginLoader);
1173
1174 assertThat(manager.getPlugins(), hasSize(2));
1175 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded2"));
1176 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1177
1178
1179 dynamicSinglePluginLoader.canLoad.set(true);
1180
1181 manager.scanForNewPlugins();
1182 assertThat(manager.getPlugins(), hasSize(2));
1183 assertNotNull(manager.getPlugin("test.atlassian.plugin.classloaded2"));
1184 assertEquals(PluginRestartState.UPGRADE, manager.getPluginRestartState("test.atlassian.plugin.classloaded2"));
1185 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1186 }
1187
1188 @Test
1189 public void testInstallPluginThatRequiresRestart() throws PluginParseException, IOException, InterruptedException
1190 {
1191 createFillAndCleanTempPluginDirectory();
1192 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1193 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1194 assertThat(manager.getPlugins(), hasSize(2));
1195
1196 new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1197 "<atlassian-plugin name='Test 2' i18n-name-key='test.name' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1198 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1199 manager.scanForNewPlugins();
1200
1201 assertThat(manager.getPlugins(), hasSize(3));
1202 Plugin plugin = manager.getPlugin("test.restartrequired");
1203 assertNotNull(plugin);
1204 assertEquals("Test 2", plugin.getName());
1205 assertEquals("test.name", plugin.getI18nNameKey());
1206 assertEquals(1, plugin.getPluginsVersion());
1207 assertEquals("1.0", plugin.getPluginInformation().getVersion());
1208 assertFalse(manager.isPluginEnabled("test.restartrequired"));
1209 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1210 assertEquals(PluginRestartState.INSTALL, manager.getPluginRestartState("test.restartrequired"));
1211
1212 manager.shutdown();
1213 manager.init();
1214
1215 assertThat(manager.getPlugins(), hasSize(3));
1216 assertNotNull(plugin);
1217 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1218 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1219 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1220 }
1221
1222 @Test
1223 public void testInstallPluginThatRequiresRestartThenRevert() throws PluginParseException, IOException, InterruptedException
1224 {
1225 createFillAndCleanTempPluginDirectory();
1226 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1227 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1228 manager.setPluginInstaller(new FilePluginInstaller(pluginsTestDir));
1229 assertThat(manager.getPlugins(), hasSize(2));
1230
1231 File pluginJar = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1232 "<atlassian-plugin name='Test 2' i18n-name-key='test.name' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1233 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build();
1234 manager.installPlugin(new JarPluginArtifact(pluginJar));
1235
1236 assertThat(manager.getPlugins(), hasSize(3));
1237 Plugin plugin = manager.getPlugin("test.restartrequired");
1238 assertNotNull(plugin);
1239 assertEquals("Test 2", plugin.getName());
1240 assertEquals("test.name", plugin.getI18nNameKey());
1241 assertEquals(1, plugin.getPluginsVersion());
1242 assertEquals("1.0", plugin.getPluginInformation().getVersion());
1243 assertFalse(manager.isPluginEnabled("test.restartrequired"));
1244 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1245 assertEquals(PluginRestartState.INSTALL, manager.getPluginRestartState("test.restartrequired"));
1246
1247 manager.revertRestartRequiredChange("test.restartrequired");
1248 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1249
1250 manager.shutdown();
1251 manager.init();
1252
1253 assertThat(manager.getPlugins(), hasSize(2));
1254 assertFalse(manager.isPluginEnabled("test.restartrequired"));
1255 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1256 }
1257
1258 @Test
1259 public void testUpgradePluginThatRequiresRestart() throws PluginParseException, IOException, InterruptedException
1260 {
1261 createFillAndCleanTempPluginDirectory();
1262 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1263
1264 final File origFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1265 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1266 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1267
1268 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1269
1270 assertThat(manager.getPlugins(), hasSize(3));
1271 assertNotNull(manager.getPlugin("test.restartrequired"));
1272 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1273 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1274 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1275
1276
1277 Thread.sleep(1000);
1278 final File updateFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1279 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>2.0</version>",
1280 " </plugin-info>", " <requiresRestart key='foo' />", " <requiresRestart key='bar' />", "</atlassian-plugin>").build();
1281
1282 origFile.delete();
1283 FileUtils.moveFile(updateFile, origFile);
1284
1285 manager.scanForNewPlugins();
1286 assertThat(manager.getPlugins(), hasSize(3));
1287 assertNotNull(manager.getPlugin("test.restartrequired"));
1288 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1289 assertEquals(PluginRestartState.UPGRADE, manager.getPluginRestartState("test.restartrequired"));
1290 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1291
1292 manager.shutdown();
1293 manager.init();
1294
1295 assertThat(manager.getPlugins(), hasSize(3));
1296 assertNotNull(manager.getPlugin("test.restartrequired"));
1297 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1298 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(2));
1299 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1300 }
1301
1302 @Test
1303 public void testUpgradePluginThatRequiresRestartThenReverted() throws PluginParseException, IOException, InterruptedException
1304 {
1305 createFillAndCleanTempPluginDirectory();
1306 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1307
1308 new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1309 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>",
1310 " <plugin-info>",
1311 " <version>1.0</version>",
1312 " </plugin-info>",
1313 " <requiresRestart key='foo' />",
1314 "</atlassian-plugin>")
1315 .build(pluginsTestDir);
1316
1317 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1318 manager.setPluginInstaller(new FilePluginInstaller(pluginsTestDir));
1319
1320 assertThat(manager.getPlugins(), hasSize(3));
1321 assertNotNull(manager.getPlugin("test.restartrequired"));
1322 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1323 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1324 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1325
1326
1327 Thread.sleep(1000);
1328 final File updateFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1329 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>2.0</version>",
1330 " </plugin-info>", " <requiresRestart key='foo' />", " <requiresRestart key='bar' />", "</atlassian-plugin>").build();
1331
1332 manager.installPlugin(new JarPluginArtifact(updateFile));
1333
1334 assertThat(manager.getPlugins(), hasSize(3));
1335 assertNotNull(manager.getPlugin("test.restartrequired"));
1336 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1337 assertEquals(PluginRestartState.UPGRADE, manager.getPluginRestartState("test.restartrequired"));
1338 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1339
1340 manager.revertRestartRequiredChange("test.restartrequired");
1341 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1342
1343 manager.shutdown();
1344 manager.init();
1345
1346 assertThat(manager.getPlugins(), hasSize(3));
1347 assertNotNull(manager.getPlugin("test.restartrequired"));
1348 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1349 assertEquals("1.0", manager.getPlugin("test.restartrequired").getPluginInformation().getVersion());
1350 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1351 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1352 }
1353
1354 @Test
1355 public void testUpgradePluginThatRequiresRestartThenRevertedRevertsToOriginalPlugin() throws PluginParseException, IOException, InterruptedException
1356 {
1357 createFillAndCleanTempPluginDirectory();
1358 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1359
1360
1361 new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1362 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1363 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1364
1365 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1366 manager.setPluginInstaller(new FilePluginInstaller(pluginsTestDir));
1367
1368 assertThat(manager.getPlugins(), hasSize(3));
1369 assertNotNull(manager.getPlugin("test.restartrequired"));
1370 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1371 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1372 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1373
1374
1375 Thread.sleep(1000);
1376 final File updateFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1377 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>2.0</version>",
1378 " </plugin-info>", " <requiresRestart key='foo' />", " <requiresRestart key='bar' />", "</atlassian-plugin>").build();
1379
1380
1381 manager.installPlugin(new JarPluginArtifact(updateFile));
1382
1383 assertThat(manager.getPlugins(), hasSize(3));
1384 assertNotNull(manager.getPlugin("test.restartrequired"));
1385 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1386 assertEquals(PluginRestartState.UPGRADE, manager.getPluginRestartState("test.restartrequired"));
1387 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1388
1389 Thread.sleep(1000);
1390 final File updateFile2 = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1391 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>3.0</version>",
1392 " </plugin-info>", " <requiresRestart key='foo' />", " <requiresRestart key='bar' />", "</atlassian-plugin>").build();
1393
1394
1395 manager.installPlugin(new JarPluginArtifact(updateFile2));
1396
1397 assertThat(manager.getPlugins(), hasSize(3));
1398 assertNotNull(manager.getPlugin("test.restartrequired"));
1399 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1400 assertEquals(PluginRestartState.UPGRADE, manager.getPluginRestartState("test.restartrequired"));
1401 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1402
1403
1404 manager.revertRestartRequiredChange("test.restartrequired");
1405 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1406 assertEquals("1.0", manager.getPlugin("test.restartrequired").getPluginInformation().getVersion());
1407
1408 manager.shutdown();
1409 manager.init();
1410
1411 assertThat(manager.getPlugins(), hasSize(3));
1412 assertNotNull(manager.getPlugin("test.restartrequired"));
1413 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1414 assertEquals("1.0", manager.getPlugin("test.restartrequired").getPluginInformation().getVersion());
1415 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1416 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1417 }
1418
1419 @Test
1420 public void testUpgradePluginThatRequiresRestartMultipleTimeStaysUpgraded() throws PluginParseException, IOException, InterruptedException
1421 {
1422 createFillAndCleanTempPluginDirectory();
1423 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1424
1425 final File origFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1426 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1427 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1428
1429 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1430 manager.setPluginInstaller(new FilePluginInstaller(pluginsTestDir));
1431
1432 assertThat(manager.getPlugins(), hasSize(3));
1433 assertNotNull(manager.getPlugin("test.restartrequired"));
1434 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1435 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1436 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1437
1438
1439 Thread.sleep(1000);
1440 final File updateFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1441 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>2.0</version>",
1442 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build();
1443
1444 manager.installPlugin(new JarPluginArtifact(updateFile));
1445
1446 assertThat(manager.getPlugins(), hasSize(3));
1447 assertNotNull(manager.getPlugin("test.restartrequired"));
1448 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1449 assertEquals(PluginRestartState.UPGRADE, manager.getPluginRestartState("test.restartrequired"));
1450 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1451
1452 Thread.sleep(1000);
1453 final File updateFile2 = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1454 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>3.0</version>",
1455 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build();
1456
1457 manager.installPlugin(new JarPluginArtifact(updateFile2));
1458
1459 assertThat(manager.getPlugins(), hasSize(3));
1460 assertNotNull(manager.getPlugin("test.restartrequired"));
1461 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1462 assertEquals(PluginRestartState.UPGRADE, manager.getPluginRestartState("test.restartrequired"));
1463 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1464
1465 manager.shutdown();
1466 manager.init();
1467
1468 assertThat(manager.getPlugins(), hasSize(3));
1469 assertNotNull(manager.getPlugin("test.restartrequired"));
1470 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1471 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1472 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1473 }
1474
1475 @Test
1476 public void testUpgradePluginThatPreviouslyRequiredRestart() throws PluginParseException, IOException, InterruptedException
1477 {
1478 createFillAndCleanTempPluginDirectory();
1479 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1480
1481 final File origFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1482 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1483 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1484
1485 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1486
1487 assertThat(manager.getPlugins(), hasSize(3));
1488 assertNotNull(manager.getPlugin("test.restartrequired"));
1489 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1490 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1491 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1492
1493
1494 Thread.sleep(1000);
1495 final File updateFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1496 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>2.0</version>",
1497 " </plugin-info>", "</atlassian-plugin>").build(pluginsTestDir);
1498
1499 origFile.delete();
1500 FileUtils.moveFile(updateFile, origFile);
1501
1502 manager.scanForNewPlugins();
1503 assertThat(manager.getPlugins(), hasSize(3));
1504 assertNotNull(manager.getPlugin("test.restartrequired"));
1505 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1506 assertEquals(PluginRestartState.UPGRADE, manager.getPluginRestartState("test.restartrequired"));
1507 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1508
1509 manager.shutdown();
1510 manager.init();
1511
1512 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1513 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1514 }
1515
1516 @Test
1517 public void testInstallPluginThatPreviouslyRequiredRestart() throws PluginParseException, IOException, InterruptedException
1518 {
1519 createFillAndCleanTempPluginDirectory();
1520 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1521
1522 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1523
1524 assertThat(manager.getPlugins(), hasSize(2));
1525 assertNull(manager.getPlugin("test.restartrequired"));
1526
1527 final File origFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1528 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1529 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1530
1531 manager.scanForNewPlugins();
1532 assertThat(manager.getPlugins(), hasSize(3));
1533 assertNotNull(manager.getPlugin("test.restartrequired"));
1534 assertFalse(manager.isPluginEnabled("test.restartrequired"));
1535 assertEquals(PluginRestartState.INSTALL, manager.getPluginRestartState("test.restartrequired"));
1536
1537
1538 Thread.sleep(1000);
1539 final File updateFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1540 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>2.0</version>",
1541 " </plugin-info>", "</atlassian-plugin>").build(pluginsTestDir);
1542
1543 origFile.delete();
1544 FileUtils.moveFile(updateFile, origFile);
1545
1546 manager.scanForNewPlugins();
1547 assertThat(manager.getPlugins(), hasSize(3));
1548 assertNotNull(manager.getPlugin("test.restartrequired"));
1549 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1550 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1551 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1552
1553 manager.shutdown();
1554 manager.init();
1555
1556 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1557 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1558 }
1559
1560 @Test
1561 public void testInstallPluginMoreThanOnceStaysAsInstall() throws PluginParseException, IOException, InterruptedException
1562 {
1563 createFillAndCleanTempPluginDirectory();
1564 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1565
1566 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1567
1568 assertThat(manager.getPlugins(), hasSize(2));
1569 assertNull(manager.getPlugin("test.restartrequired"));
1570 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1571
1572 final File origFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1573 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1574 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1575
1576 manager.scanForNewPlugins();
1577 assertThat(manager.getPlugins(), hasSize(3));
1578 assertNotNull(manager.getPlugin("test.restartrequired"));
1579 assertFalse(manager.isPluginEnabled("test.restartrequired"));
1580 assertEquals(PluginRestartState.INSTALL, manager.getPluginRestartState("test.restartrequired"));
1581
1582
1583 Thread.sleep(1000);
1584 final File updateFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1585 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>2.0</version>",
1586 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1587
1588 origFile.delete();
1589 FileUtils.moveFile(updateFile, origFile);
1590
1591 manager.scanForNewPlugins();
1592 assertThat(manager.getPlugins(), hasSize(3));
1593 assertNotNull(manager.getPlugin("test.restartrequired"));
1594 assertFalse(manager.isPluginEnabled("test.restartrequired"));
1595 assertEquals(PluginRestartState.INSTALL, manager.getPluginRestartState("test.restartrequired"));
1596
1597 manager.shutdown();
1598 manager.init();
1599
1600 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1601 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1602 }
1603
1604 @Test
1605 public void testRemovePluginThatRequiresRestart() throws PluginParseException, IOException
1606 {
1607 createFillAndCleanTempPluginDirectory();
1608 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1609
1610 final File pluginFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1611 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1612 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1613
1614 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1615
1616 assertThat(manager.getPlugins(), hasSize(3));
1617 assertNotNull(manager.getPlugin("test.restartrequired"));
1618 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1619 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1620 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1621
1622 manager.uninstall(manager.getPlugin("test.restartrequired"));
1623
1624 assertThat(manager.getPlugins(), hasSize(3));
1625 assertNotNull(manager.getPlugin("test.restartrequired"));
1626 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1627 assertEquals(PluginRestartState.REMOVE, manager.getPluginRestartState("test.restartrequired"));
1628 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1629
1630 manager.shutdown();
1631 manager.init();
1632
1633 assertFalse(pluginFile.exists());
1634 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(0));
1635 assertThat(manager.getPlugins(), hasSize(2));
1636 }
1637
1638 @Test
1639 public void testRemovePluginThatRequiresRestartThenReverted() throws PluginParseException, IOException
1640 {
1641 createFillAndCleanTempPluginDirectory();
1642 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1643
1644 final File pluginFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1645 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1646 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1647
1648 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1649
1650 assertThat(manager.getPlugins(), hasSize(3));
1651 assertNotNull(manager.getPlugin("test.restartrequired"));
1652 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1653 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1654 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1655
1656 manager.uninstall(manager.getPlugin("test.restartrequired"));
1657
1658 assertThat(manager.getPlugins(), hasSize(3));
1659 assertNotNull(manager.getPlugin("test.restartrequired"));
1660 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1661 assertEquals(PluginRestartState.REMOVE, manager.getPluginRestartState("test.restartrequired"));
1662 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1663
1664 manager.revertRestartRequiredChange("test.restartrequired");
1665 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1666
1667 manager.shutdown();
1668 manager.init();
1669
1670 assertThat(manager.getPlugins(), hasSize(3));
1671 assertNotNull(manager.getPlugin("test.restartrequired"));
1672 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1673 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1674 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1675 }
1676
1677 @Test
1678 public void testRemovePluginThatRequiresRestartViaSubclass() throws PluginParseException, IOException
1679 {
1680 createFillAndCleanTempPluginDirectory();
1681 moduleDescriptorFactory.addModuleDescriptor("requiresRestartSubclass", RequiresRestartSubclassModuleDescriptor.class);
1682
1683 final File pluginFile = new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1684 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1685 " </plugin-info>", " <requiresRestartSubclass key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1686
1687 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1688
1689 assertThat(manager.getPlugins(), hasSize(3));
1690 assertNotNull(manager.getPlugin("test.restartrequired"));
1691 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1692 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartSubclassModuleDescriptor.class), hasSize(1));
1693 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1694
1695 manager.uninstall(manager.getPlugin("test.restartrequired"));
1696
1697 assertThat(manager.getPlugins(), hasSize(3));
1698 assertNotNull(manager.getPlugin("test.restartrequired"));
1699 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1700 assertEquals(PluginRestartState.REMOVE, manager.getPluginRestartState("test.restartrequired"));
1701 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartSubclassModuleDescriptor.class), hasSize(1));
1702
1703 manager.shutdown();
1704 manager.init();
1705
1706 assertFalse(pluginFile.exists());
1707 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartSubclassModuleDescriptor.class), hasSize(0));
1708 assertThat(manager.getPlugins(), hasSize(2));
1709 }
1710
1711 @Test
1712 public void testDisableEnableOfPluginThatRequiresRestart() throws PluginParseException, IOException
1713 {
1714 createFillAndCleanTempPluginDirectory();
1715 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1716
1717 new PluginJarBuilder().addFormattedResource("atlassian-plugin.xml",
1718 "<atlassian-plugin name='Test 2' key='test.restartrequired' pluginsVersion='1'>", " <plugin-info>", " <version>1.0</version>",
1719 " </plugin-info>", " <requiresRestart key='foo' />", "</atlassian-plugin>").build(pluginsTestDir);
1720
1721 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1722
1723 assertThat(manager.getPlugins(), hasSize(3));
1724 assertNotNull(manager.getPlugin("test.restartrequired"));
1725 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1726 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1727 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1728
1729 manager.disablePlugin("test.restartrequired");
1730 assertFalse(manager.isPluginEnabled("test.restartrequired"));
1731 manager.enablePlugins("test.restartrequired");
1732
1733 assertThat(manager.getPlugins(), hasSize(3));
1734 assertNotNull(manager.getPlugin("test.restartrequired"));
1735 assertTrue(manager.isPluginEnabled("test.restartrequired"));
1736 assertEquals(PluginRestartState.NONE, manager.getPluginRestartState("test.restartrequired"));
1737 assertThat(manager.getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1738 }
1739
1740 @Test
1741 public void testCannotRemovePluginFromStaticLoader() throws PluginParseException, IOException
1742 {
1743 createFillAndCleanTempPluginDirectory();
1744 moduleDescriptorFactory.addModuleDescriptor("requiresRestart", RequiresRestartModuleDescriptor.class);
1745
1746 directoryPluginLoader = new DirectoryPluginLoader(
1747 pluginsTestDir,
1748 ImmutableList.<PluginFactory>of(new LegacyDynamicPluginFactory(PluginAccessor.Descriptor.FILENAME), new XmlDynamicPluginFactory(new MockApplication().setKey("key"))),
1749 pluginEventManager);
1750
1751 manager = newDefaultPluginManager(directoryPluginLoader, new SinglePluginLoader("test-requiresRestart-plugin.xml"));
1752 manager.init();
1753
1754 assertThat(getPluginAccessor().getPlugins(), hasSize(3));
1755 assertNotNull(getPluginAccessor().getPlugin("test.atlassian.plugin"));
1756 assertTrue(getPluginAccessor().isPluginEnabled("test.atlassian.plugin"));
1757 assertThat(getPluginAccessor().getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1758 assertEquals(PluginRestartState.NONE, getPluginAccessor().getPluginRestartState("test.atlassian.plugin"));
1759
1760 try
1761 {
1762 manager.uninstall(getPluginAccessor().getPlugin("test.atlassian.plugin"));
1763 fail();
1764 }
1765 catch (final PluginException ex)
1766 {
1767
1768 }
1769
1770 assertThat(getPluginAccessor().getPlugins(), hasSize(3));
1771 assertNotNull(getPluginAccessor().getPlugin("test.atlassian.plugin"));
1772 assertTrue(getPluginAccessor().isPluginEnabled("test.atlassian.plugin"));
1773 assertEquals(PluginRestartState.NONE, getPluginAccessor().getPluginRestartState("test.atlassian.plugin"));
1774 assertThat(getPluginAccessor().getEnabledModuleDescriptorsByClass(RequiresRestartModuleDescriptor.class), hasSize(1));
1775 }
1776
1777 private DefaultPluginManager makeClassLoadingPluginManager(PluginLoader... pluginLoaders) throws PluginParseException
1778 {
1779 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
1780
1781 directoryPluginLoader = new DirectoryPluginLoader(pluginsTestDir, ImmutableList.<PluginFactory>of(
1782 new LegacyDynamicPluginFactory(PluginAccessor.Descriptor.FILENAME),
1783 new XmlDynamicPluginFactory(new MockApplication().setKey("key"))), pluginEventManager);
1784
1785 final DefaultPluginManager manager = newDefaultPluginManager(Iterables.toArray(ImmutableList.<PluginLoader>builder().add(directoryPluginLoader).addAll(copyOf(pluginLoaders)).build(), PluginLoader.class));
1786
1787 manager.init();
1788 return manager;
1789 }
1790
1791 @Test
1792 public void testRemovingPlugins() throws PluginException, IOException
1793 {
1794 createFillAndCleanTempPluginDirectory();
1795
1796 final DefaultPluginManager manager = makeClassLoadingPluginManager();
1797 assertThat(manager.getPlugins(), hasSize(2));
1798 final MockAnimalModuleDescriptor moduleDescriptor = (MockAnimalModuleDescriptor) manager.getPluginModule("test.atlassian.plugin.classloaded:paddington");
1799 assertFalse(moduleDescriptor.disabled);
1800 final PassListener disabledListener = new PassListener(PluginDisabledEvent.class);
1801 pluginEventManager.register(disabledListener);
1802 final Plugin plugin = manager.getPlugin("test.atlassian.plugin.classloaded");
1803 manager.uninstall(plugin);
1804 assertTrue("Module must have had disable() called before being removed", moduleDescriptor.disabled);
1805
1806
1807 assertTrue(pluginStateStore.load().getPluginStateMap(plugin).isEmpty());
1808
1809 assertThat(manager.getPlugins(), hasSize(1));
1810
1811 assertNull(manager.getPlugin("test.atlassian.plugin.classloaded"));
1812 assertEquals(1, pluginsTestDir.listFiles().length);
1813 disabledListener.assertCalled();
1814 }
1815
1816 @Test
1817 public void testPluginModuleAvailableAfterInstallation()
1818 {
1819 Plugin plugin = mock(Plugin.class);
1820 when(plugin.getKey()).thenReturn("dynPlugin");
1821 when(plugin.isEnabledByDefault()).thenReturn(true);
1822 when(plugin.isDeleteable()).thenReturn(true);
1823 when(plugin.isUninstallable()).thenReturn(true);
1824 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
1825 when(plugin.compareTo(any(Plugin.class))).thenReturn(-1);
1826
1827 PluginLoader pluginLoader = mockPluginLoaderForPlugins(plugin);
1828 when(pluginLoader.supportsRemoval()).thenReturn(true);
1829
1830 manager = newDefaultPluginManager(pluginLoader);
1831 manager.init();
1832
1833 PluginModuleEnabledListener listener = new PluginModuleEnabledListener();
1834 pluginEventManager.register(listener);
1835 Collection<ModuleDescriptor<?>> mods = new ArrayList<ModuleDescriptor<?>>();
1836 MockModuleDescriptor<String> moduleDescriptor = new MockModuleDescriptor<String>(plugin, "foo", "foo");
1837 mods.add(moduleDescriptor);
1838 when(plugin.getModuleDescriptors()).thenReturn(mods);
1839 when(plugin.getModuleDescriptor("foo")).thenReturn((ModuleDescriptor)moduleDescriptor);
1840 pluginEventManager.broadcast(new PluginModuleAvailableEvent(moduleDescriptor));
1841
1842 assertTrue(getPluginAccessor().isPluginModuleEnabled("dynPlugin:foo"));
1843 assertTrue(listener.called);
1844 }
1845
1846 @Test
1847 public void testPluginModuleAvailableAfterInstallationButConfiguredToBeDisabled()
1848 {
1849 Plugin plugin = mock(Plugin.class);
1850 when(plugin.getKey()).thenReturn("dynPlugin");
1851 when(plugin.isEnabledByDefault()).thenReturn(true);
1852 when(plugin.isDeleteable()).thenReturn(true);
1853 when(plugin.isUninstallable()).thenReturn(true);
1854 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
1855 when(plugin.compareTo(any(Plugin.class))).thenReturn(-1);
1856
1857 PluginLoader pluginLoader = mockPluginLoaderForPlugins(plugin);
1858 when(pluginLoader.supportsRemoval()).thenReturn(true);
1859
1860 manager = newDefaultPluginManager(pluginLoader);
1861 manager.init();
1862
1863 MockModuleDescriptor<String> moduleDescriptor = new MockModuleDescriptor<String>(plugin, "foo", "foo");
1864
1865 manager.disablePluginModuleState(moduleDescriptor, manager.getStore());
1866
1867 PluginModuleEnabledListener listener = new PluginModuleEnabledListener();
1868 pluginEventManager.register(listener);
1869 Collection<ModuleDescriptor<?>> mods = new ArrayList<ModuleDescriptor<?>>();
1870 mods.add(moduleDescriptor);
1871
1872 when(plugin.getModuleDescriptors()).thenReturn(mods);
1873 when(plugin.getModuleDescriptor("foo")).thenReturn((ModuleDescriptor)moduleDescriptor);
1874 pluginEventManager.broadcast(new PluginModuleAvailableEvent(moduleDescriptor));
1875
1876 assertFalse(getPluginAccessor().isPluginModuleEnabled("dynPlugin:foo"));
1877 assertFalse(listener.called);
1878 }
1879
1880 @Test
1881 public void testPluginModuleUnavailableAfterInstallation()
1882 {
1883 Plugin plugin = mock(Plugin.class);
1884 when(plugin.getKey()).thenReturn("dynPlugin");
1885 when(plugin.isEnabledByDefault()).thenReturn(true);
1886 when(plugin.isDeleteable()).thenReturn(true);
1887 when(plugin.isUninstallable()).thenReturn(true);
1888 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
1889 when(plugin.compareTo(any(Plugin.class))).thenReturn(-1);
1890
1891 PluginLoader pluginLoader = mockPluginLoaderForPlugins(plugin);
1892 when(pluginLoader.supportsRemoval()).thenReturn(true);
1893
1894 manager = newDefaultPluginManager(pluginLoader);
1895 manager.init();
1896
1897 PluginModuleDisabledListener listener = new PluginModuleDisabledListener();
1898 pluginEventManager.register(listener);
1899 Collection<ModuleDescriptor<?>> mods = new ArrayList<ModuleDescriptor<?>>();
1900 MockModuleDescriptor<String> moduleDescriptor = new MockModuleDescriptor<String>(plugin, "foo", "foo");
1901 mods.add(moduleDescriptor);
1902 when(plugin.getModuleDescriptors()).thenReturn(mods);
1903 when(plugin.getModuleDescriptor("foo")).thenReturn((ModuleDescriptor)moduleDescriptor);
1904 pluginEventManager.broadcast(new PluginModuleAvailableEvent(moduleDescriptor));
1905 assertTrue(getPluginAccessor().isPluginModuleEnabled("dynPlugin:foo"));
1906 assertFalse(listener.called);
1907 pluginEventManager.broadcast(new PluginModuleUnavailableEvent(moduleDescriptor));
1908 assertTrue(listener.called);
1909 }
1910
1911 @Test
1912 public void testPluginContainerUnavailable()
1913 {
1914 Plugin plugin = mock(Plugin.class);
1915 when(plugin.getKey()).thenReturn("dynPlugin");
1916 when(plugin.isEnabledByDefault()).thenReturn(true);
1917 when(plugin.isDeleteable()).thenReturn(true);
1918 when(plugin.isUninstallable()).thenReturn(true);
1919 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
1920 when(plugin.compareTo(any(Plugin.class))).thenReturn(-1);
1921 Collection<ModuleDescriptor<?>> mods = new ArrayList<ModuleDescriptor<?>>();
1922 MockModuleDescriptor<String> moduleDescriptor = new MockModuleDescriptor<String>(plugin, "foo", "foo");
1923 mods.add(moduleDescriptor);
1924 when(plugin.getModuleDescriptors()).thenReturn(mods);
1925 when(plugin.getModuleDescriptor("foo")).thenReturn((ModuleDescriptor)moduleDescriptor);
1926
1927 PluginLoader pluginLoader = mockPluginLoaderForPlugins(plugin);
1928 when(pluginLoader.supportsRemoval()).thenReturn(true);
1929
1930 manager = newDefaultPluginManager(pluginLoader);
1931 manager.init();
1932
1933 PluginDisabledListener listener = new PluginDisabledListener();
1934 PluginModuleDisabledListener moduleDisabledListener = new PluginModuleDisabledListener();
1935 pluginEventManager.register(listener);
1936 pluginEventManager.register(moduleDisabledListener);
1937 when(plugin.getPluginState()).thenReturn(PluginState.DISABLED);
1938 pluginEventManager.broadcast(new PluginContainerUnavailableEvent("dynPlugin"));
1939
1940 assertFalse(getPluginAccessor().isPluginEnabled("dynPlugin"));
1941 assertFalse(listener.called);
1942 assertFalse(moduleDisabledListener.called);
1943 }
1944
1945 @Test
1946 public void testUninstallPluginWithDependencies() throws PluginException, IOException
1947 {
1948 Plugin child = mock(Plugin.class);
1949 when(child.getKey()).thenReturn("child");
1950 when(child.isEnabledByDefault()).thenReturn(true);
1951 when(child.getPluginState()).thenReturn(PluginState.ENABLED);
1952 when(child.getRequiredPlugins()).thenReturn(singleton("parent"));
1953 when(child.compareTo(any(Plugin.class))).thenReturn(-1);
1954 Plugin parent = mock(Plugin.class);
1955 when(parent.getKey()).thenReturn("parent");
1956 when(parent.isEnabledByDefault()).thenReturn(true);
1957 when(parent.isDeleteable()).thenReturn(true);
1958 when(parent.isUninstallable()).thenReturn(true);
1959 when(parent.getPluginState()).thenReturn(PluginState.ENABLED);
1960 when(parent.compareTo(any(Plugin.class))).thenReturn(-1);
1961
1962 PluginLoader pluginLoader = mockPluginLoaderForPlugins(child, parent);
1963 when(pluginLoader.supportsRemoval()).thenReturn(true);
1964
1965 manager = newDefaultPluginManager(pluginLoader);
1966 manager.init();
1967
1968 manager.uninstall(parent);
1969 verify(parent).enable();
1970 verify(parent).disable();
1971 verify(pluginLoader).removePlugin(parent);
1972
1973 verify(child).enable();
1974 verify(child).disable();
1975 }
1976
1977 @Test
1978 public void testUninstallPluginWithMultiLevelDependencies() throws PluginException, IOException
1979 {
1980
1981 Plugin child = mock(Plugin.class);
1982 when(child.getKey()).thenReturn("child");
1983 when(child.isEnabledByDefault()).thenReturn(true);
1984 when(child.getPluginState()).thenReturn(PluginState.ENABLED);
1985 when(child.getRequiredPlugins()).thenReturn(singleton("parent"));
1986 when(child.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.getPluginState()).thenReturn(PluginState.ENABLED);
1992 when(parent.getRequiredPlugins()).thenReturn(singleton("grandparent"));
1993 when(parent.compareTo(any(Plugin.class))).thenReturn(-1);
1994
1995 Plugin grandparent = mock(Plugin.class);
1996 when(grandparent.getKey()).thenReturn("grandparent");
1997 when(grandparent.isEnabledByDefault()).thenReturn(true);
1998 when(grandparent.isDeleteable()).thenReturn(true);
1999 when(grandparent.isUninstallable()).thenReturn(true);
2000 when(grandparent.getPluginState()).thenReturn(PluginState.ENABLED);
2001 when(grandparent.compareTo(any(Plugin.class))).thenReturn(-1);
2002
2003 PluginLoader pluginLoader = mockPluginLoaderForPlugins(child, parent, grandparent);
2004 when(pluginLoader.supportsRemoval()).thenReturn(true);
2005
2006 manager = newDefaultPluginManager(pluginLoader);
2007 manager.init();
2008
2009 manager.uninstall(grandparent);
2010 verify(grandparent).enable();
2011 verify(grandparent).disable();
2012 verify(pluginLoader).removePlugin(grandparent);
2013
2014 verify(parent).enable();
2015 verify(parent).disable();
2016 verify(child).enable();
2017 verify(child).disable();
2018 }
2019
2020 @Test
2021 public void testCircularDependencyWouldNotCauseInfiniteLoop() throws PluginException, IOException
2022 {
2023
2024 Plugin p1 = mock(Plugin.class);
2025 when(p1.getKey()).thenReturn("p1");
2026 when(p1.isEnabledByDefault()).thenReturn(true);
2027 when(p1.getPluginState()).thenReturn(PluginState.ENABLED);
2028 when(p1.getRequiredPlugins()).thenReturn(ImmutableSet.of("p2", "parent"));
2029 when(p1.compareTo(any(Plugin.class))).thenReturn(-1);
2030
2031
2032 Plugin p2 = mock(Plugin.class);
2033 when(p2.getKey()).thenReturn("p2");
2034 when(p2.isEnabledByDefault()).thenReturn(true);
2035 when(p2.getPluginState()).thenReturn(PluginState.ENABLED);
2036 when(p2.getRequiredPlugins()).thenReturn(singleton("p1"));
2037 when(p2.compareTo(any(Plugin.class))).thenReturn(-1);
2038
2039 Plugin parent = mock(Plugin.class);
2040 when(parent.getKey()).thenReturn("parent");
2041 when(parent.isEnabledByDefault()).thenReturn(true);
2042 when(parent.isDeleteable()).thenReturn(true);
2043 when(parent.isUninstallable()).thenReturn(true);
2044 when(parent.getPluginState()).thenReturn(PluginState.ENABLED);
2045 when(parent.compareTo(any(Plugin.class))).thenReturn(-1);
2046
2047 PluginLoader pluginLoader = mockPluginLoaderForPlugins(p1, p2, parent);
2048 when(pluginLoader.supportsRemoval()).thenReturn(true);
2049
2050 manager = newDefaultPluginManager(pluginLoader);
2051 manager.init();
2052
2053 manager.uninstall(parent);
2054 verify(parent, times(1)).enable();
2055 verify(parent, times(1)).disable();
2056 verify(pluginLoader).removePlugin(parent);
2057
2058 verify(p1, times(1)).enable();
2059 verify(p1, times(1)).disable();
2060 verify(p2, times(1)).enable();
2061 verify(p2, times(1)).disable();
2062 }
2063
2064 @Test
2065 public void testThreeCycleDependencyWouldNotCauseInfiniteLoop() throws PluginException, IOException
2066 {
2067 Plugin p1 = mock(Plugin.class);
2068 when(p1.getKey()).thenReturn("p1");
2069 when(p1.isEnabledByDefault()).thenReturn(true);
2070 when(p1.getPluginState()).thenReturn(PluginState.ENABLED);
2071 when(p1.getRequiredPlugins()).thenReturn(ImmutableSet.of("p2", "parent"));
2072 when(p1.compareTo(any(Plugin.class))).thenReturn(-1);
2073
2074
2075 Plugin p2 = mock(Plugin.class);
2076 when(p2.getKey()).thenReturn("p2");
2077 when(p2.isEnabledByDefault()).thenReturn(true);
2078 when(p2.getPluginState()).thenReturn(PluginState.ENABLED);
2079 when(p2.getRequiredPlugins()).thenReturn(singleton("p3"));
2080 when(p2.compareTo(any(Plugin.class))).thenReturn(-1);
2081
2082 Plugin p3 = mock(Plugin.class);
2083 when(p3.getKey()).thenReturn("p3");
2084 when(p3.isEnabledByDefault()).thenReturn(true);
2085 when(p3.getPluginState()).thenReturn(PluginState.ENABLED);
2086 when(p3.getRequiredPlugins()).thenReturn(singleton("p1"));
2087 when(p3.compareTo(any(Plugin.class))).thenReturn(-1);
2088
2089 Plugin parent = mock(Plugin.class);
2090 when(parent.getKey()).thenReturn("parent");
2091 when(parent.isEnabledByDefault()).thenReturn(true);
2092 when(parent.isDeleteable()).thenReturn(true);
2093 when(parent.isUninstallable()).thenReturn(true);
2094 when(parent.getPluginState()).thenReturn(PluginState.ENABLED);
2095 when(parent.compareTo(any(Plugin.class))).thenReturn(-1);
2096
2097 PluginLoader pluginLoader = mockPluginLoaderForPlugins(p1, p2, p3, parent);
2098 when(pluginLoader.supportsRemoval()).thenReturn(true);
2099
2100 manager = newDefaultPluginManager(pluginLoader);
2101 manager.init();
2102
2103 manager.uninstall(parent);
2104 verify(parent, times(1)).enable();
2105 verify(parent, times(1)).disable();
2106 verify(pluginLoader).removePlugin(parent);
2107
2108 verify(p1, times(1)).enable();
2109 verify(p1, times(1)).disable();
2110 verify(p2, times(1)).enable();
2111 verify(p2, times(1)).disable();
2112 verify(p3, times(1)).enable();
2113 verify(p3, times(1)).disable();
2114 }
2115
2116 @Test
2117 public void testNonRemovablePlugins() throws PluginParseException
2118 {
2119 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
2120 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
2121
2122 manager = newDefaultPluginManager(new SinglePluginLoader("test-atlassian-plugin.xml"));
2123 manager.init();
2124
2125 final Plugin plugin = getPluginAccessor().getPlugin("test.atlassian.plugin");
2126 assertFalse(plugin.isUninstallable());
2127 assertNotNull(plugin.getResourceAsStream("test-atlassian-plugin.xml"));
2128
2129 try
2130 {
2131 manager.uninstall(plugin);
2132 fail("Where was the exception?");
2133 }
2134 catch (final PluginException p)
2135 {
2136 }
2137 }
2138
2139 @Test
2140 public void testNonDeletablePlugins() throws PluginException, IOException
2141 {
2142 createFillAndCleanTempPluginDirectory();
2143
2144 final DefaultPluginManager manager = makeClassLoadingPluginManager();
2145 assertThat(manager.getPlugins(), hasSize(2));
2146
2147
2148 final Plugin pluginToRemove = new AbstractDelegatingPlugin(manager.getPlugin("test.atlassian.plugin.classloaded"))
2149 {
2150 public boolean isDeleteable()
2151 {
2152 return false;
2153 }
2154 };
2155
2156
2157 final MockAnimalModuleDescriptor moduleDescriptor = (MockAnimalModuleDescriptor) manager.getPluginModule("test.atlassian.plugin.classloaded:paddington");
2158 assertFalse(moduleDescriptor.disabled);
2159
2160 manager.uninstall(pluginToRemove);
2161
2162 assertTrue("Module must have had disable() called before being removed", moduleDescriptor.disabled);
2163 assertThat(manager.getPlugins(), hasSize(1));
2164 assertNull(manager.getPlugin("test.atlassian.plugin.classloaded"));
2165 assertEquals(2, pluginsTestDir.listFiles().length);
2166 }
2167
2168
2169 @Test
2170 public void testComparePluginNewer()
2171 {
2172
2173 final Plugin p1 = createPluginWithVersion("1.1");
2174 final Plugin p2 = createPluginWithVersion("1.0");
2175 assertTrue(p1.compareTo(p2) == 1);
2176
2177 p1.getPluginInformation().setVersion("1.10");
2178 p2.getPluginInformation().setVersion("1.2");
2179 assertTrue(p1.compareTo(p2) == 1);
2180
2181 p1.getPluginInformation().setVersion("1.2");
2182 p2.getPluginInformation().setVersion("1.01");
2183 assertTrue(p1.compareTo(p2) == 1);
2184
2185 p1.getPluginInformation().setVersion("1.0.1");
2186 p2.getPluginInformation().setVersion("1.0");
2187 assertTrue(p1.compareTo(p2) == 1);
2188
2189 p1.getPluginInformation().setVersion("1.2");
2190 p2.getPluginInformation().setVersion("1.1.1");
2191 assertTrue(p1.compareTo(p2) == 1);
2192 }
2193
2194 @Test
2195 public void testComparePluginOlder()
2196 {
2197 final Plugin p1 = createPluginWithVersion("1.0");
2198 final Plugin p2 = createPluginWithVersion("1.1");
2199 assertTrue(p1.compareTo(p2) == -1);
2200
2201 p1.getPluginInformation().setVersion("1.2");
2202 p2.getPluginInformation().setVersion("1.10");
2203 assertTrue(p1.compareTo(p2) == -1);
2204
2205 p1.getPluginInformation().setVersion("1.01");
2206 p2.getPluginInformation().setVersion("1.2");
2207 assertTrue(p1.compareTo(p2) == -1);
2208
2209 p1.getPluginInformation().setVersion("1.0");
2210 p2.getPluginInformation().setVersion("1.0.1");
2211 assertTrue(p1.compareTo(p2) == -1);
2212
2213 p1.getPluginInformation().setVersion("1.1.1");
2214 p2.getPluginInformation().setVersion("1.2");
2215 assertTrue(p1.compareTo(p2) == -1);
2216 }
2217
2218 @Test
2219 public void testComparePluginEqual()
2220 {
2221 final Plugin p1 = createPluginWithVersion("1.0");
2222 final Plugin p2 = createPluginWithVersion("1.0");
2223 assertTrue(p1.compareTo(p2) == 0);
2224
2225 p1.getPluginInformation().setVersion("1.1.0.0");
2226 p2.getPluginInformation().setVersion("1.1");
2227 assertTrue(p1.compareTo(p2) == 0);
2228
2229 p1.getPluginInformation().setVersion(" 1 . 1 ");
2230 p2.getPluginInformation().setVersion("1.1");
2231 assertTrue(p1.compareTo(p2) == 0);
2232 }
2233
2234
2235 @Test
2236 public void testComparePluginNoVersion()
2237 {
2238 final Plugin p1 = createPluginWithVersion("1.0");
2239 final Plugin p2 = createPluginWithVersion("#$%");
2240 assertEquals(1, p1.compareTo(p2));
2241
2242 p1.getPluginInformation().setVersion("#$%");
2243 p2.getPluginInformation().setVersion("1.0");
2244 assertEquals(-1, p1.compareTo(p2));
2245 }
2246
2247 @Test
2248 public void testComparePluginBadPlugin()
2249 {
2250 final Plugin p1 = createPluginWithVersion("1.0");
2251 final Plugin p2 = createPluginWithVersion("1.0");
2252
2253
2254 p2.setKey("bad.key");
2255 assertTrue(p1.compareTo(p2) != 0);
2256 }
2257
2258 @Test
2259 public void testInvalidationOfDynamicResourceCache() throws IOException, PluginException
2260 {
2261 createFillAndCleanTempPluginDirectory();
2262
2263 final DefaultPluginManager manager = makeClassLoadingPluginManager();
2264
2265 checkResources(manager, true, true);
2266 manager.disablePlugin("test.atlassian.plugin.classloaded");
2267 checkResources(manager, false, false);
2268 manager.enablePlugin("test.atlassian.plugin.classloaded");
2269 checkResources(manager, true, true);
2270 manager.uninstall(manager.getPlugin("test.atlassian.plugin.classloaded"));
2271 checkResources(manager, false, false);
2272
2273 FileUtils.copyDirectory(pluginsDirectory, pluginsTestDir);
2274 manager.scanForNewPlugins();
2275 checkResources(manager, true, true);
2276
2277
2278
2279 }
2280
2281 @Test
2282 public void testValidatePlugin() throws PluginParseException
2283 {
2284 final DynamicPluginLoader mockLoader = mock(DynamicPluginLoader.class);
2285 when(mockLoader.isDynamicPluginLoader()).thenReturn(true);
2286
2287 manager = new DefaultPluginManager(pluginStateStore, ImmutableList.<PluginLoader>of(mockLoader), moduleDescriptorFactory, new DefaultPluginEventManager());
2288
2289 final PluginArtifact mockPluginJar = mock(PluginArtifact.class);
2290 final PluginArtifact pluginArtifact = mockPluginJar;
2291 when(mockLoader.canLoad(pluginArtifact)).thenReturn("foo");
2292
2293 final String key = manager.validatePlugin(pluginArtifact);
2294 assertEquals("foo", key);
2295 verify(mockLoader).canLoad(pluginArtifact);
2296 }
2297
2298 @Test
2299 public void testValidatePluginWithNoDynamicLoaders() throws PluginParseException
2300 {
2301 final PluginLoader loader = mock(PluginLoader.class);
2302 final DefaultPluginManager manager = new DefaultPluginManager(pluginStateStore, ImmutableList.<PluginLoader>of(loader), moduleDescriptorFactory, new DefaultPluginEventManager());
2303
2304 final PluginArtifact pluginArtifact = mock(PluginArtifact.class);
2305 try
2306 {
2307 manager.validatePlugin(pluginArtifact);
2308 fail("Should have thrown exception");
2309 }
2310 catch (final IllegalStateException ex)
2311 {
2312
2313 }
2314 }
2315
2316 @Test
2317 public void testInvalidationOfDynamicClassCache() throws IOException, PluginException
2318 {
2319 createFillAndCleanTempPluginDirectory();
2320
2321 final DefaultPluginManager manager = makeClassLoadingPluginManager();
2322
2323 checkClasses(manager, true);
2324 manager.disablePlugin("test.atlassian.plugin.classloaded");
2325 checkClasses(manager, false);
2326 manager.enablePlugin("test.atlassian.plugin.classloaded");
2327 checkClasses(manager, true);
2328 manager.uninstall(manager.getPlugin("test.atlassian.plugin.classloaded"));
2329 checkClasses(manager, false);
2330
2331 FileUtils.copyDirectory(pluginsDirectory, pluginsTestDir);
2332 manager.scanForNewPlugins();
2333 checkClasses(manager, true);
2334 }
2335
2336 @Test
2337 public void testInstallPlugin() throws Exception
2338 {
2339 final PluginPersistentStateStore mockPluginStateStore = mock(PluginPersistentStateStore.class);
2340 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
2341 final DynamicPluginLoader mockPluginLoader = mock(DynamicPluginLoader.class);
2342 when(mockPluginLoader.isDynamicPluginLoader()).thenReturn(true);
2343
2344 final DescriptorParserFactory mockDescriptorParserFactory = mock(DescriptorParserFactory.class);
2345 final DescriptorParser mockDescriptorParser = mock(DescriptorParser.class);
2346 final PluginArtifact pluginArtifact = mock(PluginArtifact.class);
2347 final PluginInstaller mockRepository = mock(PluginInstaller.class);
2348 final Plugin plugin = mock(Plugin.class);
2349
2350
2351 final DefaultPluginManager pluginManager = new DefaultPluginManager(mockPluginStateStore,
2352 Collections.<PluginLoader>singletonList(mockPluginLoader), moduleDescriptorFactory, pluginEventManager);
2353
2354 when(mockPluginStateStore.load()).thenReturn(new DefaultPluginPersistentState());
2355 when(mockPluginStateStore.load()).thenReturn(new DefaultPluginPersistentState());
2356 when(mockPluginStateStore.load()).thenReturn(new DefaultPluginPersistentState());
2357 when(mockDescriptorParser.getKey()).thenReturn("test");
2358 when(mockPluginLoader.loadAllPlugins(moduleDescriptorFactory)).thenReturn((Iterable) Collections.emptyList());
2359 when(mockPluginLoader.supportsAddition()).thenReturn(true);
2360 when(mockPluginLoader.loadFoundPlugins(moduleDescriptorFactory)).thenReturn(Collections.singletonList(plugin));
2361 when(mockPluginLoader.canLoad(pluginArtifact)).thenReturn("test");
2362 when(plugin.getKey()).thenReturn("test");
2363 when(plugin.getModuleDescriptors()).thenReturn((Collection) new ArrayList<Object>());
2364 when(plugin.getModuleDescriptors()).thenReturn((Collection) new ArrayList<Object>());
2365 when(plugin.isEnabledByDefault()).thenReturn(true);
2366
2367 when(plugin.isEnabledByDefault()).thenReturn(true);
2368 when(plugin.isEnabled()).thenReturn(true);
2369 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
2370 when(plugin.hasAllPermissions()).thenReturn(true);
2371 when(plugin.getActivePermissions()).thenReturn(ImmutableSet.of(Permissions.ALL_PERMISSIONS));
2372
2373 pluginManager.setPluginInstaller(mockRepository);
2374 pluginManager.init();
2375 final PassListener enabledListener = new PassListener(PluginEnabledEvent.class);
2376 pluginEventManager.register(enabledListener);
2377 pluginManager.installPlugin(pluginArtifact);
2378
2379 assertEquals(plugin, pluginManager.getPlugin("test"));
2380 assertTrue(pluginManager.isPluginEnabled("test"));
2381
2382
2383
2384
2385
2386
2387
2388
2389 enabledListener.assertCalled();
2390 }
2391
2392 @Test
2393 public void testInstallPluginsWithOne()
2394 {
2395 DynamicPluginLoader loader = mock(DynamicPluginLoader.class);
2396 when(loader.isDynamicPluginLoader()).thenReturn(true);
2397
2398 ModuleDescriptorFactory descriptorFactory = mock(ModuleDescriptorFactory.class);
2399 PluginEventManager eventManager = mock(PluginEventManager.class);
2400 PluginInstaller installer = mock(PluginInstaller.class);
2401 DefaultPluginManager pm = new DefaultPluginManager(new MemoryPluginPersistentStateStore(), Collections.<PluginLoader>singletonList(loader), descriptorFactory, eventManager);
2402 pm.setPluginInstaller(installer);
2403 when(loader.loadAllPlugins(descriptorFactory)).thenReturn(Arrays.<Plugin>asList());
2404 PluginArtifact artifact = mock(PluginArtifact.class);
2405 Plugin plugin = mock(Plugin.class);
2406 when(loader.canLoad(artifact)).thenReturn("foo");
2407 when(loader.loadFoundPlugins(descriptorFactory)).thenReturn(Arrays.asList(plugin));
2408
2409 pm.init();
2410 assertThat(pm.getPlugins(), empty());
2411 pm.installPlugins(artifact);
2412
2413 verify(loader).canLoad(artifact);
2414 verify(installer).installPlugin("foo", artifact);
2415 }
2416
2417 @Test
2418 public void testInstallPluginsWithTwo()
2419 {
2420 DynamicPluginLoader loader = mock(DynamicPluginLoader.class);
2421 when(loader.isDynamicPluginLoader()).thenReturn(true);
2422
2423 ModuleDescriptorFactory descriptorFactory = mock(ModuleDescriptorFactory.class);
2424 PluginEventManager eventManager = mock(PluginEventManager.class);
2425 PluginInstaller installer = mock(PluginInstaller.class);
2426 DefaultPluginManager pm = new DefaultPluginManager(new MemoryPluginPersistentStateStore(), Collections.<PluginLoader>singletonList(loader), descriptorFactory, eventManager);
2427 pm.setPluginInstaller(installer);
2428 when(loader.loadAllPlugins(descriptorFactory)).thenReturn(Arrays.<Plugin>asList());
2429 PluginArtifact artifactA = mock(PluginArtifact.class);
2430 Plugin pluginA = mock(Plugin.class);
2431 when(loader.canLoad(artifactA)).thenReturn("a");
2432 PluginArtifact artifactB = mock(PluginArtifact.class);
2433 Plugin pluginB = mock(Plugin.class);
2434 when(loader.canLoad(artifactB)).thenReturn("b");
2435
2436 when(loader.loadFoundPlugins(descriptorFactory)).thenReturn(Arrays.asList(pluginA, pluginB));
2437
2438 pm.init();
2439 assertThat(pm.getPlugins(), empty());
2440 pm.installPlugins(artifactA, artifactB);
2441
2442 verify(loader).canLoad(artifactA);
2443 verify(loader).canLoad(artifactB);
2444 verify(installer).installPlugin("a", artifactA);
2445 verify(installer).installPlugin("b", artifactB);
2446 }
2447
2448 @Test
2449 public void testInstallPluginsWithTwoButOneFailsValidation()
2450 {
2451 DynamicPluginLoader loader = mock(DynamicPluginLoader.class);
2452 when(loader.isDynamicPluginLoader()).thenReturn(true);
2453
2454 ModuleDescriptorFactory descriptorFactory = mock(ModuleDescriptorFactory.class);
2455 PluginEventManager eventManager = mock(PluginEventManager.class);
2456 PluginInstaller installer = mock(PluginInstaller.class);
2457 DefaultPluginManager pm = new DefaultPluginManager(new MemoryPluginPersistentStateStore(), Collections.<PluginLoader>singletonList(loader), descriptorFactory, eventManager);
2458 pm.setPluginInstaller(installer);
2459 PluginArtifact artifactA = mock(PluginArtifact.class);
2460 Plugin pluginA = mock(Plugin.class);
2461 when(loader.canLoad(artifactA)).thenReturn("a");
2462 PluginArtifact artifactB = mock(PluginArtifact.class);
2463 Plugin pluginB = mock(Plugin.class);
2464 when(loader.canLoad(artifactB)).thenReturn(null);
2465
2466 when(loader.loadFoundPlugins(descriptorFactory)).thenReturn(Arrays.asList(pluginA, pluginB));
2467
2468 try
2469 {
2470 pm.installPlugins(artifactA, artifactB);
2471 fail("Should have not installed plugins");
2472 }
2473 catch (PluginParseException ex)
2474 {
2475
2476 }
2477
2478 verify(loader).canLoad(artifactA);
2479 verify(loader).canLoad(artifactB);
2480 verify(installer, never()).installPlugin("a", artifactA);
2481 verify(installer, never()).installPlugin("b", artifactB);
2482 }
2483
2484 @Test
2485 public void testInstallPluginsWithTwoButOneFailsValidationWithException()
2486 {
2487 DynamicPluginLoader loader = mock(DynamicPluginLoader.class);
2488 when(loader.isDynamicPluginLoader()).thenReturn(true);
2489
2490 ModuleDescriptorFactory descriptorFactory = mock(ModuleDescriptorFactory.class);
2491 PluginEventManager eventManager = mock(PluginEventManager.class);
2492 PluginInstaller installer = mock(PluginInstaller.class);
2493 DefaultPluginManager pm = new DefaultPluginManager(new MemoryPluginPersistentStateStore(), Collections.<PluginLoader>singletonList(loader), descriptorFactory, eventManager);
2494 pm.setPluginInstaller(installer);
2495 PluginArtifact artifactA = mock(PluginArtifact.class);
2496 Plugin pluginA = mock(Plugin.class);
2497 when(loader.canLoad(artifactA)).thenReturn("a");
2498 PluginArtifact artifactB = mock(PluginArtifact.class);
2499 Plugin pluginB = mock(Plugin.class);
2500 doThrow(new PluginParseException()).when(loader).canLoad(artifactB);
2501
2502 when(loader.loadFoundPlugins(descriptorFactory)).thenReturn(Arrays.asList(pluginA, pluginB));
2503
2504 try
2505 {
2506 pm.installPlugins(artifactA, artifactB);
2507 fail("Should have not installed plugins");
2508 }
2509 catch (PluginParseException ex)
2510 {
2511
2512 }
2513
2514 verify(loader).canLoad(artifactA);
2515 verify(loader).canLoad(artifactB);
2516 verify(installer, never()).installPlugin("a", artifactA);
2517 verify(installer, never()).installPlugin("b", artifactB);
2518 }
2519
2520 Plugin mockStaticPlugin(final String pluginKey, final ModuleDescriptor<?>... descriptors)
2521 {
2522 return new StaticPlugin()
2523 {
2524 {
2525 setPluginInformation(new PluginInformation());
2526 setEnabledByDefault(true);
2527 setKey(pluginKey);
2528 }
2529
2530 @Override
2531 public Collection<ModuleDescriptor<?>> getModuleDescriptors()
2532 {
2533 return Arrays.<ModuleDescriptor<?>>asList(descriptors);
2534 }
2535
2536 @Override
2537 public ModuleDescriptor<Object> getModuleDescriptor(final String moduleKey)
2538 {
2539 for (ModuleDescriptor desc : descriptors)
2540 {
2541 if (desc.getKey().equals(moduleKey))
2542 return desc;
2543 }
2544 return null;
2545 }
2546 };
2547 }
2548
2549 @Test
2550 public void testInstallTwoPluginsButOneFailsToEnableAModuleAndThenFailsToDisableAModule()
2551 {
2552 final PluginLoader mockPluginLoader = mock(PluginLoader.class);
2553 final ModuleDescriptor<Object> failEnableModuleDescriptor = mockFailingModuleDescriptor("foo:bar", FailureMode.FAIL_TO_ENABLE);
2554 final ModuleDescriptor<Object> failDisableModuleDescriptor = mockFailingModuleDescriptor("foo:buzz", FailureMode.FAIL_TO_DISABLE);
2555
2556 Plugin badPlugin = mockStaticPlugin("foo", failDisableModuleDescriptor, failEnableModuleDescriptor);
2557
2558 final AbstractModuleDescriptor<?> goodModuleDescriptor = mock(AbstractModuleDescriptor.class);
2559 when(goodModuleDescriptor.getKey()).thenReturn("baz");
2560 when(goodModuleDescriptor.getCompleteKey()).thenReturn("good:baz");
2561 when(goodModuleDescriptor.isEnabledByDefault()).thenReturn(true);
2562 Plugin goodPlugin = mockStaticPlugin("good", goodModuleDescriptor);
2563
2564 when(mockPluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Lists.newArrayList(badPlugin, goodPlugin));
2565
2566 manager = newDefaultPluginManager(mockPluginLoader);
2567 manager.init();
2568
2569 assertThat(getPluginAccessor().getPlugins(), hasSize(2));
2570 assertThat(getPluginAccessor().getEnabledPlugins(), hasSize(1));
2571 verify(goodModuleDescriptor).enabled();
2572 }
2573
2574 private <T> void checkResources(final PluginAccessor manager, final boolean canGetGlobal, final boolean canGetModule) throws IOException
2575 {
2576 InputStream is = manager.getDynamicResourceAsStream("icon.gif");
2577 assertEquals(canGetGlobal, is != null);
2578 IOUtils.closeQuietly(is);
2579 is = manager.getDynamicResourceAsStream("bear/paddington.vm");
2580 assertEquals(canGetModule, is != null);
2581 IOUtils.closeQuietly(is);
2582 }
2583
2584 private <T> void checkClasses(final PluginAccessor manager, final boolean canGet)
2585 {
2586 try
2587 {
2588 manager.getDynamicPluginClass("com.atlassian.plugin.mock.MockPaddington");
2589 if (!canGet)
2590 {
2591 fail("Class in plugin was successfully loaded");
2592 }
2593 }
2594 catch (final ClassNotFoundException e)
2595 {
2596 if (canGet)
2597 {
2598 fail(e.getMessage());
2599 }
2600 }
2601 }
2602
2603 @Test
2604 public void testAddPluginsThatThrowExceptionOnEnabled() throws Exception
2605 {
2606 final Plugin plugin = new CannotEnablePlugin();
2607
2608 manager = newDefaultPluginManager();
2609 manager.addPlugins(null, Arrays.asList(plugin));
2610
2611 assertFalse(plugin.getPluginState() == PluginState.ENABLED);
2612 }
2613
2614 @Test
2615 public void testUninstallPluginClearsState() throws IOException
2616 {
2617 createFillAndCleanTempPluginDirectory();
2618
2619 final DefaultPluginManager manager = makeClassLoadingPluginManager();
2620
2621 checkClasses(manager, true);
2622 final Plugin plugin = manager.getPlugin("test.atlassian.plugin.classloaded");
2623
2624 final ModuleDescriptor<?> module = plugin.getModuleDescriptor("paddington");
2625 assertTrue(manager.isPluginModuleEnabled(module.getCompleteKey()));
2626 manager.disablePluginModule(module.getCompleteKey());
2627 assertFalse(manager.isPluginModuleEnabled(module.getCompleteKey()));
2628 manager.uninstall(plugin);
2629 assertFalse(manager.isPluginModuleEnabled(module.getCompleteKey()));
2630 assertTrue(pluginStateStore.load().getPluginStateMap(plugin).isEmpty());
2631 }
2632
2633 @Test
2634 public void testCannotInitTwice() throws PluginParseException
2635 {
2636 manager = newDefaultPluginManager();
2637 manager.init();
2638 try
2639 {
2640 manager.init();
2641 fail("IllegalStateException expected");
2642 }
2643 catch (final IllegalStateException expected)
2644 {
2645 }
2646 }
2647
2648 @Test
2649 public void testCannotShutdownTwice() throws PluginParseException
2650 {
2651 manager = newDefaultPluginManager();
2652 manager.init();
2653 manager.shutdown();
2654 try
2655 {
2656 manager.shutdown();
2657 fail("IllegalStateException expected");
2658 }
2659 catch (final IllegalStateException expected)
2660 {
2661 }
2662 }
2663
2664 @Test
2665 public void testGetPluginWithNullKey()
2666 {
2667 manager = newDefaultPluginManager();
2668 manager.init();
2669 try
2670 {
2671 getPluginAccessor().getPlugin(null);
2672 fail();
2673 }
2674 catch (IllegalArgumentException ex)
2675 {
2676
2677 }
2678 }
2679
2680 @Test
2681 public void testShutdownHandlesException()
2682 {
2683 final ThingsAreWrongListener listener = new ThingsAreWrongListener();
2684 pluginEventManager.register(listener);
2685
2686 manager = newDefaultPluginManager();
2687 manager.init();
2688 try
2689 {
2690
2691 manager.shutdown();
2692 }
2693 catch (Exception e)
2694 {
2695 fail("Should not have thrown an exception!");
2696 }
2697 assertTrue(listener.isCalled());
2698 }
2699
2700 private void writeToFile(String file, String line) throws IOException, URISyntaxException
2701 {
2702 final String resourceName = ClasspathFilePluginMetadata.class.getPackage().getName().replace(".", "/") + "/" + file;
2703 URL resource = getClass().getClassLoader().getResource(resourceName);
2704
2705 FileOutputStream fout = new FileOutputStream(new File(resource.toURI()), false);
2706 fout.write(line.getBytes(), 0, line.length());
2707 fout.close();
2708
2709 }
2710
2711 @Test
2712 public void testExceptionOnRequiredPluginNotEnabling() throws Exception
2713 {
2714 try
2715 {
2716 writeToFile("application-required-modules.txt", "foo.required:bar");
2717 writeToFile("application-required-plugins.txt", "foo.required");
2718
2719 final PluginLoader mockPluginLoader = mock(PluginLoader.class);
2720 final ModuleDescriptor<Object> badModuleDescriptor = mockFailingModuleDescriptor("foo.required:bar", FailureMode.FAIL_TO_ENABLE);
2721
2722 final AbstractModuleDescriptor goodModuleDescriptor = mock(AbstractModuleDescriptor.class);
2723 when(goodModuleDescriptor.getKey()).thenReturn("baz");
2724 when(goodModuleDescriptor.getCompleteKey()).thenReturn("foo.required:baz");
2725
2726 Plugin plugin = mockStaticPlugin("foo.required", goodModuleDescriptor, badModuleDescriptor);
2727
2728 when(mockPluginLoader.loadAllPlugins(any(ModuleDescriptorFactory.class))).thenReturn(Collections.singletonList(plugin));
2729
2730 manager = newDefaultPluginManager(mockPluginLoader);
2731 try
2732 {
2733 manager.init();
2734 }
2735 catch(PluginException e)
2736 {
2737
2738 assertEquals("Unable to validate required plugins or modules", e.getMessage());
2739 return;
2740 }
2741 fail("A PluginException is expected when trying to initialize the plugins system with required plugins that do not load.");
2742 }
2743 finally
2744 {
2745
2746 writeToFile("application-required-modules.txt", "");
2747 writeToFile("application-required-plugins.txt", "");
2748 }
2749 }
2750
2751 @Test
2752 public void pluginReturnedByLoadAllPluginsButNotUsedIsDiscarded()
2753 {
2754 final String pluginKey = "pluginKey";
2755 final PluginPersistentStateStore pluginPersistentStateStore = mock(
2756 PluginPersistentStateStore.class, RETURNS_DEEP_STUBS);
2757 when(pluginPersistentStateStore.load().getPluginRestartState(pluginKey)).thenReturn(PluginRestartState.NONE);
2758
2759 DiscardablePluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
2760 Plugin pluginV1 = mock(Plugin.class, RETURNS_DEEP_STUBS);
2761 Plugin pluginV2 = mock(Plugin.class, RETURNS_DEEP_STUBS);
2762 when(pluginV1.getKey()).thenReturn(pluginKey);
2763 when(pluginV2.getKey()).thenReturn(pluginKey);
2764
2765 when(pluginV1.compareTo(pluginV2)).thenReturn(-1);
2766 when(pluginV2.compareTo(pluginV1)).thenReturn(1);
2767 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(pluginV1, pluginV2));
2768
2769 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
2770 pluginPersistentStateStore,
2771 Arrays.asList((PluginLoader)pluginLoader),
2772 mock(ModuleDescriptorFactory.class),
2773 mock(PluginEventManager.class),
2774 mock(PluginExceptionInterception.class)
2775 );
2776 defaultPluginManager.init();
2777 verify(pluginLoader).discardPlugin(pluginV1);
2778 }
2779
2780 @Test
2781 public void oldPluginReturnedByLoadFoundPluginsIsDiscarded()
2782 {
2783 final String pluginKey = "pluginKey";
2784 final PluginPersistentStateStore pluginPersistentStateStore = mock(
2785 PluginPersistentStateStore.class, RETURNS_DEEP_STUBS);
2786 when(pluginPersistentStateStore.load().getPluginRestartState(pluginKey)).thenReturn(PluginRestartState.NONE);
2787
2788 DiscardablePluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
2789 Plugin pluginV1 = mock(Plugin.class, RETURNS_DEEP_STUBS);
2790 Plugin pluginV2 = mock(Plugin.class, RETURNS_DEEP_STUBS);
2791 when(pluginV1.getKey()).thenReturn(pluginKey);
2792 when(pluginV2.getKey()).thenReturn(pluginKey);
2793
2794 when(pluginV1.compareTo(pluginV2)).thenReturn(-1);
2795 when(pluginV2.compareTo(pluginV1)).thenReturn(1);
2796 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(pluginV2));
2797 when(pluginLoader.supportsAddition()).thenReturn(true);
2798 when(pluginLoader.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(pluginV1));
2799
2800 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
2801 pluginPersistentStateStore,
2802 Arrays.asList((PluginLoader)pluginLoader),
2803 mock(ModuleDescriptorFactory.class),
2804 mock(PluginEventManager.class),
2805 mock(PluginExceptionInterception.class)
2806 );
2807 defaultPluginManager.init();
2808 final int found = defaultPluginManager.scanForNewPlugins();
2809 assertThat(found, is(1));
2810 verify(pluginLoader).discardPlugin(pluginV1);
2811 }
2812
2813 @Test
2814 public void uninstallingDeletableUninstallablePluginRemovesItFromLoader()
2815 {
2816 final Plugin plugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
2817 final PluginLoader pluginLoader = mock(PluginLoader.class);
2818 final DefaultPluginManager defaultPluginManager = setupUninstallTest(true, true, plugin, pluginLoader);
2819 defaultPluginManager.uninstall(plugin);
2820 verify(pluginLoader).removePlugin(plugin);
2821 }
2822
2823 @Test
2824 public void uninstallingNotDeletableUninstallablePluginRemovesItFromLoader()
2825 {
2826 final Plugin plugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
2827 final PluginLoader pluginLoader = mock(PluginLoader.class);
2828 final DefaultPluginManager defaultPluginManager = setupUninstallTest(false, true, plugin, pluginLoader);
2829 defaultPluginManager.uninstall(plugin);
2830 verify(pluginLoader).removePlugin(plugin);
2831 }
2832
2833 @Test
2834 public void uninstallingDeletableNotUninstallablePluginDoesNotRemoveItFromLoader()
2835 {
2836 final Plugin plugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
2837 final PluginLoader pluginLoader = mock(PluginLoader.class);
2838 final DefaultPluginManager defaultPluginManager = setupUninstallTest(true, false, plugin, pluginLoader);
2839
2840 doThrow(new AssertionError("Unexpected PluginLoader.removePlugin call")).when(pluginLoader).removePlugin(plugin);
2841 expectedException.expect(PluginException.class);
2842 expectedException.expectMessage(plugin.getKey());
2843 defaultPluginManager.uninstall(plugin);
2844 }
2845
2846 @Test
2847 public void uninstallingNotDeletableNotUninstallablePluginDoesNotRemoveItFromLoader()
2848 {
2849 final Plugin plugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
2850 final PluginLoader pluginLoader = mock(PluginLoader.class);
2851 final DefaultPluginManager defaultPluginManager = setupUninstallTest(false, false, plugin, pluginLoader);
2852
2853 doThrow(new AssertionError("Unexpected PluginLoader.removePlugin call")).when(pluginLoader).removePlugin(plugin);
2854 expectedException.expect(PluginException.class);
2855 expectedException.expectMessage(plugin.getKey());
2856 defaultPluginManager.uninstall(plugin);
2857 }
2858
2859 private DefaultPluginManager setupUninstallTest(
2860 final boolean isDeleteable,
2861 final boolean isUninstallable,
2862 final Plugin plugin,
2863 final PluginLoader pluginLoader)
2864 {
2865 final String pluginKey = "uninstall-test-plugin-key";
2866 when(plugin.getKey()).thenReturn(pluginKey);
2867 when(plugin.isDeleteable()).thenReturn(isDeleteable);
2868 when(plugin.isUninstallable()).thenReturn(isUninstallable);
2869 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(plugin));
2870 when(pluginLoader.supportsRemoval()).thenReturn(true);
2871
2872 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
2873 mock(PluginPersistentStateStore.class, RETURNS_DEEP_STUBS),
2874 Arrays.asList(pluginLoader),
2875 mock(ModuleDescriptorFactory.class),
2876 mock(PluginEventManager.class),
2877 mock(PluginExceptionInterception.class)
2878 );
2879 defaultPluginManager.init();
2880 return defaultPluginManager;
2881 }
2882
2883 @Test
2884 public void earlyLateStartupEvents()
2885 {
2886 final PluginPersistentStateStore pluginPersistentStateStore = mock(
2887 PluginPersistentStateStore.class, RETURNS_DEEP_STUBS);
2888
2889 final PluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
2890 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.<Plugin>asList());
2891 final List<PluginLoader> pluginLoaders = Arrays.asList(pluginLoader);
2892
2893 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
2894
2895 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
2896
2897 final SplitStartupPluginSystemLifecycle splitStartupPluginSystemLifecycle = new DefaultPluginManager(
2898 pluginPersistentStateStore, pluginLoaders, moduleDescriptorFactory, pluginEventManager);
2899
2900 splitStartupPluginSystemLifecycle.earlyStartup();
2901 final ArgumentCaptor<Object> earlyEvents = ArgumentCaptor.forClass(Object.class);
2902 verify(pluginEventManager, times(2)).broadcast(earlyEvents.capture());
2903 assertThat(earlyEvents.getAllValues(), contains(
2904 instanceOf(PluginFrameworkStartingEvent.class),instanceOf(PluginFrameworkDelayedEvent.class)));
2905
2906
2907 reset(pluginEventManager);
2908
2909 splitStartupPluginSystemLifecycle.lateStartup();
2910 final ArgumentCaptor<Object> laterEvents = ArgumentCaptor.forClass(Object.class);
2911 verify(pluginEventManager, times(2)).broadcast(laterEvents.capture());
2912 assertThat(laterEvents.getAllValues(), contains(
2913 instanceOf(PluginFrameworkResumingEvent.class), instanceOf(PluginFrameworkStartedEvent.class)));
2914 }
2915
2916 @Test
2917 public void delayedPluginsAreDelayed()
2918 {
2919 final String earlyKey = "earlyKey";
2920 final String laterKey = "laterKey";
2921
2922 final PluginPersistentStateStore pluginPersistentStateStore = mock(
2923 PluginPersistentStateStore.class, RETURNS_DEEP_STUBS);
2924 when(pluginPersistentStateStore.load().getPluginRestartState(earlyKey)).thenReturn(PluginRestartState.NONE);
2925 when(pluginPersistentStateStore.load().getPluginRestartState(laterKey)).thenReturn(PluginRestartState.NONE);
2926
2927 PluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
2928 Plugin earlyPlugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
2929 Plugin laterPlugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
2930 when(earlyPlugin.getKey()).thenReturn(earlyKey);
2931 when(laterPlugin.getKey()).thenReturn(laterKey);
2932 List<Plugin> bothPlugins = Arrays.asList(earlyPlugin, laterPlugin);
2933 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(bothPlugins);
2934 List<PluginLoader> pluginLoaders = Arrays.asList(pluginLoader);
2935
2936 ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
2937
2938 PluginEventManager pluginEventManager = mock(PluginEventManager.class);
2939
2940 PluginPredicate pluginPredicate = mock(PluginPredicate.class);
2941 when(pluginPredicate.matches(earlyPlugin)).thenReturn(false);
2942 when(pluginPredicate.matches(laterPlugin)).thenReturn(true);
2943
2944 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
2945 pluginPersistentStateStore, pluginLoaders, moduleDescriptorFactory, pluginEventManager, pluginPredicate);
2946
2947 defaultPluginManager.earlyStartup();
2948 assertThat(defaultPluginManager.getPlugin(earlyKey), is(earlyPlugin));
2949 assertThat(defaultPluginManager.getPlugin(laterKey), nullValue());
2950
2951 defaultPluginManager.lateStartup();
2952 assertThat(defaultPluginManager.getPlugin(earlyKey), is(earlyPlugin));
2953 assertThat(defaultPluginManager.getPlugin(laterKey), is(laterPlugin));
2954 }
2955
2956 @Test
2957 public void delayedPluginsCanBeDisabled()
2958 {
2959 final String earlyKey = "earlyKey";
2960 final String laterKey = "laterKey";
2961
2962 Wrapper wrapper = new Wrapper(earlyKey, laterKey).invoke(true);
2963 DefaultPluginManager defaultPluginManager = wrapper.getDefaultPluginManager();
2964 Plugin laterPlugin = wrapper.getLaterPlugin();
2965
2966 defaultPluginManager.earlyStartup();
2967 defaultPluginManager.disablePlugin(laterKey);
2968
2969 defaultPluginManager.lateStartup();
2970 verify(laterPlugin, never()).enable();
2971 }
2972
2973 @Test
2974 public void delayedPluginsCanBeEnabled()
2975 {
2976 final String earlyKey = "earlyKey";
2977 final String laterKey = "laterKey";
2978
2979 Wrapper wrapper = new Wrapper(earlyKey, laterKey).invoke(false);
2980 DefaultPluginManager defaultPluginManager = wrapper.getDefaultPluginManager();
2981 Plugin laterPlugin = wrapper.getLaterPlugin();
2982 defaultPluginManager.earlyStartup();
2983 defaultPluginManager.enablePlugin(laterKey);
2984
2985 defaultPluginManager.lateStartup();
2986 verify(laterPlugin).enable();
2987 }
2988
2989 @Test
2990 public void scanForNewPluginsNotAllowedBeforeLateStartup()
2991 {
2992 final PluginPersistentStateStore pluginPersistentStateStore = mock(
2993 PluginPersistentStateStore.class, RETURNS_DEEP_STUBS);
2994
2995 final PluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
2996 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.<Plugin>asList());
2997 final List<PluginLoader> pluginLoaders = Arrays.asList(pluginLoader);
2998
2999 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
3000
3001 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
3002
3003 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
3004 pluginPersistentStateStore, pluginLoaders, moduleDescriptorFactory, pluginEventManager);
3005
3006 defaultPluginManager.earlyStartup();
3007
3008 expectedException.expect(IllegalStateException.class);
3009 defaultPluginManager.scanForNewPlugins();
3010 }
3011
3012 @Test
3013 public void scanForNewPluginsDuringLateStartup()
3014 {
3015 final String pluginKey = "plugin-key";
3016 final Plugin plugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
3017 when(plugin.getKey()).thenReturn(pluginKey);
3018 when(plugin.isEnabledByDefault()).thenReturn(true);
3019 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
3020
3021 final PluginPersistentStateStore pluginPersistentStateStore = mock(
3022 PluginPersistentStateStore.class, RETURNS_DEEP_STUBS);
3023 when(pluginPersistentStateStore.load().isEnabled(plugin)).thenReturn(true);
3024
3025 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
3026
3027 final PluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
3028 when(pluginLoader.loadAllPlugins(moduleDescriptorFactory)).thenReturn(Arrays.asList(plugin));
3029 when(pluginLoader.loadFoundPlugins(moduleDescriptorFactory)).thenReturn(Arrays.<Plugin>asList());
3030 final List<PluginLoader> pluginLoaders = Arrays.asList(pluginLoader);
3031
3032 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
3033
3034 final PluginPredicate pluginPredicate = mock(PluginPredicate.class);
3035 when(pluginPredicate.matches(plugin)).thenReturn(true);
3036
3037 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
3038 pluginPersistentStateStore, pluginLoaders, moduleDescriptorFactory, pluginEventManager, pluginPredicate);
3039
3040
3041
3042 final Answer<Object> scanForNewPlugins = new Answer<Object>()
3043 {
3044 @Override
3045 public Object answer(final InvocationOnMock invocation) throws Throwable
3046 {
3047 defaultPluginManager.scanForNewPlugins();
3048 return null;
3049 }
3050 };
3051 doAnswer(scanForNewPlugins).when(pluginEventManager).broadcast(isA(PluginEnabledEvent.class));
3052
3053 defaultPluginManager.earlyStartup();
3054 defaultPluginManager.lateStartup();
3055 }
3056
3057
3058 @Test
3059 public void earlyStartupDoesNotSavePluginPersistentState()
3060 {
3061
3062 final String pluginKeyPrefix = "pluginWithRestartState_";
3063 final ImmutableList.Builder<Plugin> pluginListBuilder = ImmutableList.<Plugin>builder();
3064 final PluginPersistentState.Builder pluginPersistentStateBuilder = PluginPersistentState.Builder.create();
3065 for(final PluginRestartState pluginRestartState : PluginRestartState.values())
3066 {
3067 final String pluginKey = pluginKeyPrefix + pluginRestartState.toString();
3068 final Plugin plugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
3069 when(plugin.getKey()).thenReturn(pluginKey);
3070 pluginListBuilder.add(plugin);
3071 pluginPersistentStateBuilder.setPluginRestartState(pluginKey, pluginRestartState);
3072
3073 }
3074 final PluginPersistentStateStore pluginPersistentStateStore = mock(PluginPersistentStateStore.class);
3075 when(pluginPersistentStateStore.load()).thenReturn(pluginPersistentStateBuilder.toState());
3076 final PluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
3077 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(pluginListBuilder.build());
3078 final List<PluginLoader> pluginLoaders = ImmutableList.of(pluginLoader);
3079
3080 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
3081 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
3082 final PluginPredicate pluginPredicate = mock(PluginPredicate.class);
3083 when(pluginPredicate.matches(any(Plugin.class))).thenReturn(false);
3084
3085 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
3086 pluginPersistentStateStore, pluginLoaders, moduleDescriptorFactory, pluginEventManager, pluginPredicate);
3087
3088 defaultPluginManager.earlyStartup();
3089 verify(pluginPersistentStateStore, never()).save(any(PluginPersistentState.class));
3090 }
3091
3092 @Test
3093 public void lateStartupRemovesPluginsMarkedForRemoval()
3094 {
3095 final String earlyKey = "earlyKey";
3096 final String laterKey = "laterKey";
3097 final Plugin earlyPlugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
3098 final Plugin laterPlugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
3099 when(earlyPlugin.getKey()).thenReturn(earlyKey);
3100 when(laterPlugin.getKey()).thenReturn(laterKey);
3101
3102
3103 final PluginPersistentStateStore pluginPersistentStateStore = new MemoryPluginPersistentStateStore();
3104 pluginPersistentStateStore.save(PluginPersistentState.Builder.create()
3105 .setEnabled(earlyPlugin, !earlyPlugin.isEnabledByDefault())
3106 .setEnabled(laterPlugin, !laterPlugin.isEnabledByDefault())
3107 .setPluginRestartState(earlyKey, PluginRestartState.REMOVE)
3108 .setPluginRestartState(laterKey, PluginRestartState.REMOVE)
3109 .toState());
3110
3111 final PluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
3112 final List<Plugin> bothPlugins = Arrays.asList(earlyPlugin, laterPlugin);
3113 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(bothPlugins);
3114 final List<PluginLoader> pluginLoaders = Arrays.asList(pluginLoader);
3115
3116 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
3117
3118 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
3119
3120 final PluginPredicate pluginPredicate = mock(PluginPredicate.class);
3121 when(pluginPredicate.matches(earlyPlugin)).thenReturn(false);
3122 when(pluginPredicate.matches(laterPlugin)).thenReturn(true);
3123
3124 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
3125 pluginPersistentStateStore, pluginLoaders, moduleDescriptorFactory, pluginEventManager, pluginPredicate);
3126
3127 defaultPluginManager.earlyStartup();
3128 verify(pluginLoader, never()).removePlugin(any(Plugin.class));
3129
3130
3131 defaultPluginManager.lateStartup();
3132 verify(pluginLoader).removePlugin(earlyPlugin);
3133 verify(pluginLoader).removePlugin(laterPlugin);
3134
3135 final PluginPersistentState pluginPersistentState = pluginPersistentStateStore.load();
3136
3137 assertThat(pluginPersistentState.isEnabled(earlyPlugin), is(earlyPlugin.isEnabledByDefault()));
3138 assertThat(pluginPersistentState.isEnabled(laterPlugin), is(laterPlugin.isEnabledByDefault()));
3139
3140 assertThat(pluginPersistentState.getPluginRestartState(earlyKey), is(PluginRestartState.NONE));
3141 assertThat(pluginPersistentState.getPluginRestartState(laterKey), is(PluginRestartState.NONE));
3142
3143 assertThat(pluginPersistentState.getMap().size(), is(0));
3144 }
3145
3146 @Test
3147 public void exampleUsingPersistentStateDelegation()
3148 {
3149 final String earlyKey = "earlyKey";
3150 final String laterKey = "laterKey";
3151 final Plugin earlyPlugin = mock(Plugin.class, RETURNS_MOCKS);
3152 final Plugin laterPlugin = mock(Plugin.class, RETURNS_MOCKS);
3153 when(earlyPlugin.getKey()).thenReturn(earlyKey);
3154 when(laterPlugin.getKey()).thenReturn(laterKey);
3155 when(earlyPlugin.isEnabledByDefault()).thenReturn(true);
3156 when(laterPlugin.isEnabledByDefault()).thenReturn(true);
3157
3158
3159 final boolean tenanted[] = { false };
3160 final PluginPersistentStateStore warmStore = new LoadOnlyPluginPersistentStateStore();
3161 final PluginPersistentStateStore tenantedStore = new MemoryPluginPersistentStateStore();
3162 final PluginPersistentStateStore pluginPersistentStateStore = new DelegatingPluginPersistentStateStore()
3163 {
3164 @Override
3165 public PluginPersistentStateStore getDelegate()
3166 {
3167 return !tenanted[0] ? warmStore : tenantedStore;
3168 }
3169 };
3170
3171 final PluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
3172 final List<Plugin> bothPlugins = Arrays.asList(earlyPlugin, laterPlugin);
3173 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(bothPlugins);
3174 final List<PluginLoader> pluginLoaders = Arrays.asList(pluginLoader);
3175
3176 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
3177
3178 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
3179
3180 final PluginPredicate pluginPredicate = mock(PluginPredicate.class);
3181 when(pluginPredicate.matches(earlyPlugin)).thenReturn(false);
3182 when(pluginPredicate.matches(laterPlugin)).thenReturn(true);
3183
3184 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
3185 pluginPersistentStateStore, pluginLoaders, moduleDescriptorFactory, pluginEventManager, pluginPredicate);
3186
3187 defaultPluginManager.earlyStartup();
3188
3189
3190 tenanted[0] = true;
3191
3192 defaultPluginManager.lateStartup();
3193
3194 when(earlyPlugin.getPluginState()).thenReturn(PluginState.ENABLED);
3195 when(laterPlugin.getPluginState()).thenReturn(PluginState.ENABLED);
3196
3197
3198 defaultPluginManager.disablePlugin(earlyKey);
3199 defaultPluginManager.disablePlugin(laterKey);
3200
3201 final PluginPersistentState pluginPersistentState = tenantedStore.load();
3202 assertThat(pluginPersistentState.isEnabled(earlyPlugin), is(false));
3203 assertThat(pluginPersistentState.isEnabled(laterPlugin), is(false));
3204
3205 assertThat(pluginPersistentState.getMap().size(), is(2));
3206 }
3207
3208 @Test
3209 public void upgradePluginUpgradesPlugin()
3210 {
3211 final String pluginKey = "pluginKey";
3212 final PluginPersistentStateStore pluginPersistentStateStore = mock(
3213 PluginPersistentStateStore.class, RETURNS_DEEP_STUBS);
3214 when(pluginPersistentStateStore.load().getPluginRestartState(pluginKey)).thenReturn(PluginRestartState.NONE);
3215
3216 Plugin pluginV1 = mock(Plugin.class, RETURNS_DEEP_STUBS);
3217 Plugin pluginV2 = mock(Plugin.class, RETURNS_DEEP_STUBS);
3218 when(pluginV1.getKey()).thenReturn(pluginKey);
3219 when(pluginV2.getKey()).thenReturn(pluginKey);
3220 when(pluginV1.isDeleteable()).thenReturn(true);
3221 when(pluginV1.isUninstallable()).thenReturn(true);
3222
3223 when(pluginV1.compareTo(pluginV2)).thenReturn(-1);
3224 when(pluginV2.compareTo(pluginV1)).thenReturn(1);
3225
3226 DiscardablePluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
3227 when(pluginLoader.supportsAddition()).thenReturn(true);
3228 when(pluginLoader.supportsRemoval()).thenReturn(true);
3229 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(pluginV1));
3230 when(pluginLoader.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(pluginV2));
3231
3232 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
3233 pluginPersistentStateStore,
3234 Arrays.asList((PluginLoader)pluginLoader),
3235 mock(ModuleDescriptorFactory.class),
3236 mock(PluginEventManager.class),
3237 mock(PluginExceptionInterception.class)
3238 );
3239 defaultPluginManager.init();
3240
3241 assertThat(defaultPluginManager.getPlugin(pluginKey), is(pluginV1));
3242
3243 final int found = defaultPluginManager.scanForNewPlugins();
3244
3245 assertThat(found, is(1));
3246 assertThat(defaultPluginManager.getPlugin(pluginKey), is(pluginV2));
3247
3248 verify(pluginLoader).removePlugin(pluginV1);
3249 }
3250
3251 @Test
3252 public void upgradePluginDisablesDependentPlugins()
3253 {
3254 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
3255
3256 final String pluginKey = "pluginKey";
3257 final Plugin pluginV1 = mockStateChangePlugin(pluginKey, pluginEventManager);
3258 final Plugin pluginV2 = mockStateChangePlugin(pluginKey, pluginEventManager);
3259 when(pluginV1.isDeleteable()).thenReturn(true);
3260
3261 final String dependentPluginKey = "dependentPluginKey";
3262 final Plugin dependentPlugin = mockStateChangePlugin(dependentPluginKey, pluginEventManager);
3263 when(dependentPlugin.isEnabledByDefault()).thenReturn(true);
3264 when(dependentPlugin.getRequiredPlugins()).thenReturn(ImmutableSet.of(pluginKey));
3265
3266
3267 mockPluginsSortOrder(pluginV1, pluginV2, dependentPlugin);
3268
3269 final DiscardablePluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
3270 when(pluginLoader.supportsAddition()).thenReturn(true);
3271 when(pluginLoader.supportsRemoval()).thenReturn(true);
3272 final List<Plugin> initialPlugins = Arrays.asList(pluginV1, dependentPlugin);
3273 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(initialPlugins);
3274 when(pluginLoader.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(pluginV2));
3275
3276 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
3277 pluginPersistentStateStore,
3278 Arrays.asList((PluginLoader)pluginLoader),
3279 mock(ModuleDescriptorFactory.class),
3280 mock(PluginEventManager.class),
3281 mock(PluginExceptionInterception.class)
3282 );
3283 defaultPluginManager.init();
3284 verify(pluginV1).enable();
3285 verify(dependentPlugin).enable();
3286 when(pluginV1.getPluginState()).thenReturn(PluginState.ENABLED);
3287 when(dependentPlugin.getPluginState()).thenReturn(PluginState.ENABLED);
3288
3289 final int found = defaultPluginManager.scanForNewPlugins();
3290 assertThat(found, is(1));
3291 verify(dependentPlugin).disable();
3292 verify(dependentPlugin, times(2)).enable();
3293 }
3294
3295 @Test
3296 public void scanForNewPluginsScansAllPluginLoaders()
3297 {
3298 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
3299
3300 final PluginLoader pluginLoaderAlpha = mockPluginLoaderForPlugins();
3301 when(pluginLoaderAlpha.supportsAddition()).thenReturn(true);
3302 final PluginLoader pluginLoaderBeta = mockPluginLoaderForPlugins();
3303 when(pluginLoaderBeta.supportsAddition()).thenReturn(true);
3304
3305 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
3306 pluginPersistentStateStore,
3307 Arrays.asList(pluginLoaderAlpha, pluginLoaderBeta),
3308 mock(ModuleDescriptorFactory.class),
3309 mock(PluginEventManager.class),
3310 mock(PluginExceptionInterception.class)
3311 );
3312 defaultPluginManager.init();
3313
3314 assertThat(defaultPluginManager.getPlugins(), empty());
3315
3316 final Plugin pluginAlpha = mock(Plugin.class, RETURNS_DEEP_STUBS);
3317 when(pluginAlpha.getKey()).thenReturn("alpha");
3318 when(pluginLoaderAlpha.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(pluginAlpha));
3319 final Plugin pluginBeta = mock(Plugin.class, RETURNS_DEEP_STUBS);
3320 when(pluginBeta.getKey()).thenReturn("beta");
3321 when(pluginLoaderBeta.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(pluginBeta));
3322
3323 final int found = defaultPluginManager.scanForNewPlugins();
3324
3325 assertThat(found, is(2));
3326 assertThat(defaultPluginManager.getPlugins(), containsInAnyOrder(pluginAlpha, pluginBeta));
3327 }
3328
3329 @Test
3330 public void startupElementInPluginInformationOverridesDelayPredicate()
3331 {
3332 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
3333
3334 final String earlyKey = "earlyKey";
3335 final String laterKey = "laterKey";
3336 final Plugin earlyPlugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
3337 final Plugin laterPlugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
3338 when(earlyPlugin.getKey()).thenReturn(earlyKey);
3339 when(earlyPlugin.getPluginInformation().getStartup()).thenReturn("early");
3340 when(laterPlugin.getKey()).thenReturn(laterKey);
3341 when(laterPlugin.getPluginInformation().getStartup()).thenReturn("late");
3342
3343
3344
3345
3346 when(earlyPlugin.compareTo(laterPlugin)).thenReturn(-1);
3347 when(laterPlugin.compareTo(earlyPlugin)).thenReturn(1);
3348
3349 final PluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
3350 final List<Plugin> bothPlugins = Arrays.asList(earlyPlugin, laterPlugin);
3351 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(bothPlugins);
3352 final List<PluginLoader> pluginLoaders = Arrays.asList(pluginLoader);
3353
3354 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
3355
3356 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
3357
3358 final PluginPredicate pluginPredicate = mock(PluginPredicate.class);
3359
3360 when(pluginPredicate.matches(earlyPlugin)).thenReturn(true);
3361 when(pluginPredicate.matches(laterPlugin)).thenReturn(false);
3362
3363 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
3364 pluginPersistentStateStore, pluginLoaders, moduleDescriptorFactory, pluginEventManager, pluginPredicate);
3365
3366 defaultPluginManager.earlyStartup();
3367 verify(earlyPlugin).install();
3368 verify(laterPlugin, never()).install();
3369
3370 defaultPluginManager.lateStartup();
3371 verify(laterPlugin).install();
3372 }
3373
3374 @Test
3375 public void startupOverrideFileOverridesPluginInformationAndDelayPredicate() throws Exception
3376 {
3377 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
3378
3379 final String earlyOverrideNoInfoKey = "earlyOverrideNoInfoKey";
3380 final String lateOverrideNoInfoKey = "lateOverrideNoInfoKey";
3381 final Plugin earlyOverrideNoInfoPlugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
3382 final Plugin lateOverrideNoInfoPlugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
3383 when(earlyOverrideNoInfoPlugin.getKey()).thenReturn(earlyOverrideNoInfoKey);
3384 when(lateOverrideNoInfoPlugin.getKey()).thenReturn(lateOverrideNoInfoKey);
3385
3386 final String earlyOverrideLateInfoKey = "earlyOverrideLateInfoKey";
3387 final String lateOverrideEarlyInfoKey = "lateOverrideEarlyInfoKey";
3388 final Plugin earlyOverrideLateInfoPlugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
3389 final Plugin lateOverrideEarlyInfoPlugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
3390 when(earlyOverrideLateInfoPlugin.getKey()).thenReturn(earlyOverrideLateInfoKey);
3391 when(earlyOverrideLateInfoPlugin.getPluginInformation().getStartup()).thenReturn("late");
3392 when(lateOverrideEarlyInfoPlugin.getKey()).thenReturn(lateOverrideEarlyInfoKey);
3393 when(lateOverrideEarlyInfoPlugin.getPluginInformation().getStartup()).thenReturn("early");
3394
3395 final List<String> overrideContents = Arrays.asList(
3396 earlyOverrideNoInfoKey + "=early",
3397 lateOverrideNoInfoKey + "=late",
3398 earlyOverrideLateInfoKey + "=early",
3399 lateOverrideEarlyInfoKey + "=late"
3400 );
3401 writeLines(startupOverrideFile, overrideContents);
3402 System.setProperty(getStartupOverrideFileProperty(), startupOverrideFile.getPath());
3403
3404
3405 mockPluginsSortOrder(
3406 earlyOverrideLateInfoPlugin, earlyOverrideNoInfoPlugin, lateOverrideEarlyInfoPlugin, lateOverrideNoInfoPlugin);
3407
3408 final List<Plugin> allPlugins = Arrays.asList(
3409 earlyOverrideNoInfoPlugin, lateOverrideNoInfoPlugin, earlyOverrideLateInfoPlugin , lateOverrideEarlyInfoPlugin);
3410
3411 final PluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
3412 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(allPlugins);
3413 final List<PluginLoader> pluginLoaders = Arrays.asList(pluginLoader);
3414
3415 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
3416
3417 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
3418
3419 final PluginPredicate pluginPredicate = mock(PluginPredicate.class);
3420
3421
3422
3423 when(pluginPredicate.matches(earlyOverrideNoInfoPlugin)).thenReturn(true);
3424 when(pluginPredicate.matches(lateOverrideNoInfoPlugin)).thenReturn(false);
3425 when(pluginPredicate.matches(earlyOverrideLateInfoPlugin)).thenReturn(false);
3426 when(pluginPredicate.matches(lateOverrideEarlyInfoPlugin)).thenReturn(true);
3427
3428 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
3429 pluginPersistentStateStore, pluginLoaders, moduleDescriptorFactory, pluginEventManager, pluginPredicate);
3430
3431 defaultPluginManager.earlyStartup();
3432 verify(earlyOverrideNoInfoPlugin).install();
3433 verify(earlyOverrideLateInfoPlugin).install();
3434 verify(lateOverrideNoInfoPlugin, never()).install();
3435 verify(lateOverrideEarlyInfoPlugin, never()).install();
3436
3437 defaultPluginManager.lateStartup();
3438 verify(lateOverrideNoInfoPlugin).install();
3439 verify(lateOverrideEarlyInfoPlugin).install();
3440 }
3441
3442 @Test
3443 public void installEventSequencing()
3444 {
3445 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
3446
3447 final Plugin alphaPlugin = mockStateChangePlugin("alpha", pluginEventManager);
3448 final Plugin betaPlugin = mockStateChangePlugin("beta", pluginEventManager);
3449
3450 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
3451
3452 final PluginLoader pluginLoader = mockPluginLoaderForPlugins(alphaPlugin);
3453 when(pluginLoader.supportsAddition()).thenReturn(true);
3454
3455 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
3456 pluginPersistentStateStore,
3457 ImmutableList.of(pluginLoader),
3458 mock(ModuleDescriptorFactory.class),
3459 pluginEventManager,
3460 mock(PluginExceptionInterception.class)
3461 );
3462
3463 final ArgumentCaptor<Object> initEvents = ArgumentCaptor.forClass(Object.class);
3464 doNothing().when(pluginEventManager).broadcast(initEvents.capture());
3465
3466 defaultPluginManager.init();
3467
3468 assertThat(filter(initEvents.getAllValues(), PluginEventBaseClassShims.isPluginEvent()), contains(
3469 pluginStateChange(alphaPlugin, PluginState.INSTALLED),
3470 pluginStateChange(alphaPlugin, PluginState.ENABLED),
3471 pluginEvent(PluginEnabledEvent.class, alphaPlugin)
3472 ));
3473
3474 when(pluginLoader.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(betaPlugin));
3475
3476 final ArgumentCaptor<Object> scanEvents = ArgumentCaptor.forClass(Object.class);
3477 doNothing().when(pluginEventManager).broadcast(scanEvents.capture());
3478
3479 final int found = defaultPluginManager.scanForNewPlugins();
3480
3481 assertThat(found, is(1));
3482
3483 assertThat(filter(scanEvents.getAllValues(), PluginEventBaseClassShims.isPluginEvent()), contains(
3484 pluginStateChange(betaPlugin, PluginState.INSTALLED),
3485 pluginStateChange(betaPlugin, PluginState.ENABLED),
3486 pluginEvent(PluginEnabledEvent.class, betaPlugin)
3487 ));
3488 }
3489
3490 @Test
3491 public void enableAndDisableEventSequencing()
3492 {
3493 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
3494
3495 final String pluginKey = "pluginKey";
3496 final Plugin plugin = mockStateChangePlugin(pluginKey, pluginEventManager);
3497
3498 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
3499
3500 final PluginLoader pluginLoader = mockPluginLoaderForPlugins(plugin);
3501
3502 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
3503 pluginPersistentStateStore,
3504 ImmutableList.of(pluginLoader),
3505 mock(ModuleDescriptorFactory.class),
3506 pluginEventManager,
3507 mock(PluginExceptionInterception.class)
3508 );
3509
3510 defaultPluginManager.init();
3511
3512 final ArgumentCaptor<Object> disableEvents = ArgumentCaptor.forClass(Object.class);
3513 doNothing().when(pluginEventManager).broadcast(disableEvents.capture());
3514
3515 defaultPluginManager.disablePlugin(pluginKey);
3516
3517 assertThat(filter(disableEvents.getAllValues(), PluginEventBaseClassShims.isPluginEvent()), contains(
3518 pluginEvent(BeforePluginDisabledEvent.class, plugin),
3519 pluginStateChange(plugin, PluginState.DISABLED),
3520 pluginEvent(PluginDisabledEvent.class, plugin)
3521 ));
3522
3523 final ArgumentCaptor<Object> enableEvents = ArgumentCaptor.forClass(Object.class);
3524 doNothing().when(pluginEventManager).broadcast(enableEvents.capture());
3525
3526 defaultPluginManager.enablePlugins(pluginKey);
3527
3528 assertThat(filter(enableEvents.getAllValues(), PluginEventBaseClassShims.isPluginEvent()), contains(
3529
3530
3531 pluginStateChange(plugin, PluginState.ENABLED),
3532 pluginStateChange(plugin, PluginState.ENABLED),
3533 pluginEvent(PluginEnabledEvent.class, plugin)
3534 ));
3535 }
3536
3537 @Test
3538 public void upgradeEventSequencing()
3539 {
3540 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
3541
3542 final String pluginKey = "pluginKey";
3543 final Plugin pluginV1 = mockStateChangePlugin(pluginKey, pluginEventManager);
3544 final Plugin pluginV2 = mockStateChangePlugin(pluginKey, pluginEventManager);
3545
3546 final Plugin dependentPlugin = mockStateChangePlugin("dependentPluginKey", pluginEventManager);
3547 when(dependentPlugin.getRequiredPlugins()).thenReturn(ImmutableSet.of(pluginKey));
3548
3549 mockPluginsSortOrder(dependentPlugin, pluginV1, pluginV2);
3550
3551 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
3552
3553 final PluginLoader pluginLoader = mockPluginLoaderForPlugins(pluginV1, dependentPlugin);
3554 when(pluginLoader.supportsAddition()).thenReturn(true);
3555 when(pluginLoader.supportsRemoval()).thenReturn(true);
3556
3557 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
3558 pluginPersistentStateStore,
3559 ImmutableList.of(pluginLoader),
3560 mock(ModuleDescriptorFactory.class),
3561 pluginEventManager,
3562 mock(PluginExceptionInterception.class)
3563 );
3564
3565 defaultPluginManager.init();
3566
3567 when(pluginLoader.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(ImmutableList.of(pluginV2));
3568 final ArgumentCaptor<Object> events = ArgumentCaptor.forClass(Object.class);
3569 doNothing().when(pluginEventManager).broadcast(events.capture());
3570
3571 final int found = defaultPluginManager.scanForNewPlugins();
3572
3573 assertThat(found, is(1));
3574
3575 assertThat(filter(events.getAllValues(), PluginEventBaseClassShims.isPluginEvent()), contains(
3576 pluginEvent(BeforePluginDisabledEvent.class, dependentPlugin),
3577 pluginStateChange(dependentPlugin, PluginState.DISABLED),
3578 pluginEvent(PluginDisabledEvent.class, dependentPlugin),
3579 pluginEvent(BeforePluginDisabledEvent.class, pluginV1),
3580 pluginStateChange(pluginV1, PluginState.DISABLED),
3581 pluginEvent(PluginDisabledEvent.class, pluginV1),
3582 pluginStateChange(pluginV2, PluginState.INSTALLED),
3583 pluginEvent(PluginUpgradedEvent.class, pluginV2),
3584 pluginStateChange(pluginV2, PluginState.ENABLED),
3585 pluginStateChange(dependentPlugin, PluginState.ENABLED),
3586 pluginEvent(PluginEnabledEvent.class, pluginV2),
3587 pluginEvent(PluginEnabledEvent.class, dependentPlugin)
3588 ));
3589 }
3590
3591 @Test
3592 public void uninstallEventSequencing()
3593 {
3594 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
3595
3596 final String pluginKey = "pluginKey";
3597 final Plugin plugin = mockStateChangePlugin(pluginKey, pluginEventManager);
3598
3599 final Plugin dependentPlugin = mockStateChangePlugin("dependentPluginKey", pluginEventManager);
3600 when(dependentPlugin.getRequiredPlugins()).thenReturn(ImmutableSet.of(pluginKey));
3601
3602 mockPluginsSortOrder(dependentPlugin, plugin);
3603
3604 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
3605
3606 final PluginLoader pluginLoader = mockPluginLoaderForPlugins(plugin, dependentPlugin);
3607 when(pluginLoader.supportsRemoval()).thenReturn(true);
3608
3609 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
3610 pluginPersistentStateStore,
3611 ImmutableList.of(pluginLoader),
3612 mock(ModuleDescriptorFactory.class),
3613 pluginEventManager,
3614 mock(PluginExceptionInterception.class)
3615 );
3616
3617 defaultPluginManager.init();
3618
3619 final ArgumentCaptor<Object> events = ArgumentCaptor.forClass(Object.class);
3620 doNothing().when(pluginEventManager).broadcast(events.capture());
3621
3622 defaultPluginManager.uninstall(defaultPluginManager.getPlugin(pluginKey));
3623
3624 assertThat(filter(events.getAllValues(), PluginEventBaseClassShims.isPluginEvent()), contains(
3625 pluginEvent(BeforePluginDisabledEvent.class, dependentPlugin),
3626 pluginStateChange(dependentPlugin, PluginState.DISABLED),
3627 pluginEvent(PluginDisabledEvent.class, dependentPlugin),
3628 pluginEvent(BeforePluginDisabledEvent.class, plugin),
3629 pluginStateChange(plugin, PluginState.DISABLED),
3630 pluginEvent(PluginDisabledEvent.class, plugin),
3631 pluginEvent(PluginUninstalledEvent.class, plugin)
3632 ));
3633 }
3634
3635 private Plugin mockStateChangePlugin(final String pluginKey, final PluginEventManager pluginEventManager)
3636 {
3637 final Plugin plugin = mockPlugin(pluginKey);
3638
3639 doAnswerPluginStateChangeWhen(plugin, PluginState.INSTALLED, pluginEventManager).install();
3640 doAnswerPluginStateChangeWhen(plugin, PluginState.ENABLED, pluginEventManager).enable();
3641 doAnswerPluginStateChangeWhen(plugin, PluginState.DISABLED, pluginEventManager).disable();
3642 return plugin;
3643 }
3644
3645 private Plugin mockPlugin(final String pluginKey)
3646 {
3647 final Plugin plugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
3648 when(plugin.getKey()).thenReturn(pluginKey);
3649 when(plugin.getPluginState()).thenReturn(PluginState.UNINSTALLED);
3650 when(plugin.getModuleDescriptors()).thenReturn(ImmutableList.<ModuleDescriptor<?>>of());
3651 when(plugin.getPluginInformation().satisfiesMinJavaVersion()).thenReturn(true);
3652 when(plugin.isUninstallable()).thenReturn(true);
3653 return plugin;
3654 }
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664 private Plugin doAnswerPluginStateChangeWhen(
3665 final Plugin plugin, final PluginState pluginState, final PluginEventManager pluginEventManager)
3666 {
3667 final Answer answer = new Answer()
3668 {
3669 @Override
3670 public Object answer(final InvocationOnMock invocation) throws Throwable
3671 {
3672 when(plugin.getPluginState()).thenReturn(pluginState);
3673 pluginEventManager.broadcast(new PluginStateMarkerEvent(plugin, pluginState));
3674 return null;
3675 }
3676 };
3677 return doAnswer(answer).when(plugin);
3678 }
3679
3680
3681
3682
3683 private static class PluginStateMarkerEvent
3684 {
3685 private final Plugin plugin;
3686 private final PluginState pluginState;
3687
3688 PluginStateMarkerEvent(final Plugin plugin, final PluginState pluginState)
3689 {
3690 this.plugin = plugin;
3691 this.pluginState = pluginState;
3692 }
3693
3694 public Plugin getPlugin()
3695 {
3696 return plugin;
3697 }
3698
3699 public PluginState getPluginState()
3700 {
3701 return pluginState;
3702 }
3703
3704 @Override
3705 public String toString()
3706 {
3707 return "Marker event for " + plugin + " transition to pluginState " + pluginState;
3708 }
3709 }
3710
3711
3712
3713
3714 private static class PluginEventBaseClassShims
3715 {
3716 private final static Map<Object, Function<Object, Plugin>> pluginEventExtractors =
3717 ImmutableMap.<Object, Function<Object, Plugin>>builder()
3718 .put(BeforePluginDisabledEvent.class, new Function<Object, Plugin>()
3719 {
3720 @Override
3721 public Plugin apply(final Object pluginEvent)
3722 {
3723 return ((BeforePluginDisabledEvent) pluginEvent).getPlugin();
3724 }
3725 })
3726 .put(PluginDisabledEvent.class, new Function<Object, Plugin>()
3727 {
3728 @Override
3729 public Plugin apply(final Object pluginEvent)
3730 {
3731 return ((PluginDisabledEvent) pluginEvent).getPlugin();
3732 }
3733 })
3734 .put(PluginEnabledEvent.class, new Function<Object, Plugin>()
3735 {
3736 @Override
3737 public Plugin apply(final Object pluginEvent)
3738 {
3739 return ((PluginEnabledEvent) pluginEvent).getPlugin();
3740 }
3741 })
3742 .put(PluginUninstalledEvent.class, new Function<Object, Plugin>()
3743 {
3744 @Override
3745 public Plugin apply(final Object pluginEvent)
3746 {
3747 return ((PluginUninstalledEvent) pluginEvent).getPlugin();
3748 }
3749 })
3750 .put(PluginUpgradedEvent.class, new Function<Object, Plugin>()
3751 {
3752 @Override
3753 public Plugin apply(final Object pluginEvent)
3754 {
3755 return ((PluginUpgradedEvent) pluginEvent).getPlugin();
3756 }
3757 })
3758 .put(PluginStateMarkerEvent.class, new Function<Object, Plugin>()
3759 {
3760 @Override
3761 public Plugin apply(final Object pluginEvent)
3762 {
3763 return ((PluginStateMarkerEvent) pluginEvent).getPlugin();
3764 }
3765 })
3766 .build();
3767
3768 private static Predicate<Object> isPluginEvent()
3769 {
3770 return new Predicate<Object>()
3771 {
3772 @Override
3773 public boolean apply(final Object pluginEvent)
3774 {
3775 return (null != pluginEventExtractors.get(pluginEvent.getClass()));
3776 }
3777 };
3778 }
3779
3780 private static Plugin getPluginFromPluginEvent(final Object pluginEvent)
3781 {
3782 return pluginEventExtractors.get(pluginEvent.getClass()).apply(pluginEvent);
3783 }
3784 }
3785
3786 private Matcher<Object> pluginStateChange(final Plugin plugin, final PluginState pluginState)
3787 {
3788 final Matcher<Object> pluginEventMatcher = pluginEvent(PluginStateMarkerEvent.class, plugin);
3789 final Matcher<PluginState> pluginStateMatcher = is(pluginState);
3790
3791 return new TypeSafeMatcher<Object>()
3792 {
3793 @Override
3794 protected boolean matchesSafely(final Object event)
3795 {
3796 return pluginEventMatcher.matches(event)
3797
3798 && pluginStateMatcher.matches(((PluginStateMarkerEvent)event).getPluginState());
3799 }
3800
3801 @Override
3802 public void describeTo(final Description description)
3803 {
3804 pluginEventMatcher.describeTo(description);
3805 description.appendText(" and .getPluginState() ");
3806 pluginStateMatcher.describeTo(description);
3807 }
3808 };
3809 }
3810
3811 private Matcher<Object> pluginEvent(final Class clazz, final Plugin plugin)
3812 {
3813 final Matcher<Class> classMatcher = instanceOf(clazz);
3814 final Matcher<Plugin> pluginMatcher = is(plugin);
3815 return new TypeSafeMatcher<Object>()
3816 {
3817 @Override
3818 protected boolean matchesSafely(final Object pluginEvent)
3819 {
3820 final Plugin pluginFromEvent = PluginEventBaseClassShims.getPluginFromPluginEvent(pluginEvent);
3821 return classMatcher.matches(pluginEvent) && pluginMatcher.matches(pluginFromEvent);
3822 }
3823
3824 @Override
3825 public void describeTo(final Description description)
3826 {
3827 classMatcher.describeTo(description);
3828 description.appendText(" for which .getPlugin() ");
3829 pluginMatcher.describeTo(description);
3830 }
3831 };
3832 }
3833
3834 @Test
3835 public void moduleEnableDisableEventSequencing()
3836 {
3837 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
3838
3839 final String pluginKey = "pluginKey";
3840 final Plugin plugin = mockStateChangePlugin(pluginKey, pluginEventManager);
3841
3842 final String moduleKeyAlpha = "alpha";
3843 final ModuleDescriptor moduleAlpha = mockStateChangePluginModule(pluginKey, moduleKeyAlpha, pluginEventManager);
3844 final String moduleKeyBeta = "beta";
3845 final ModuleDescriptor moduleBeta = mockStateChangePluginModule(pluginKey, moduleKeyBeta, pluginEventManager);
3846
3847 when(plugin.getModuleDescriptors()).thenReturn(ImmutableList.<ModuleDescriptor<?>>of(moduleAlpha, moduleBeta));
3848 when(plugin.getModuleDescriptor(moduleKeyAlpha)).thenReturn(moduleAlpha);
3849 when(plugin.getModuleDescriptor(moduleKeyBeta)).thenReturn(moduleBeta);
3850
3851 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
3852
3853 final PluginLoader pluginLoader = mockPluginLoaderForPlugins(plugin);
3854
3855 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
3856 pluginPersistentStateStore,
3857 ImmutableList.of(pluginLoader),
3858 mock(ModuleDescriptorFactory.class),
3859 pluginEventManager,
3860 mock(PluginExceptionInterception.class)
3861 );
3862
3863 defaultPluginManager.init();
3864
3865 final ArgumentCaptor<Object> disableEventsCaptor = ArgumentCaptor.forClass(Object.class);
3866 doNothing().when(pluginEventManager).broadcast(disableEventsCaptor.capture());
3867 final Predicate<Object> isPluginModuleEvent = PluginModuleEventBaseClassShims.isPluginModuleEvent();
3868
3869 defaultPluginManager.disablePlugin(pluginKey);
3870
3871 final List<Object> disableEvents = disableEventsCaptor.getAllValues();
3872 final Iterable<Object> pluginModuleDisableEvents = filter(disableEvents, isPluginModuleEvent);
3873 assertThat(pluginModuleDisableEvents, contains(
3874 pluginModuleEvent(BeforePluginModuleDisabledEvent.class, moduleBeta),
3875 pluginModuleStateChange(moduleBeta, false),
3876 pluginModuleEvent(PluginModuleDisabledEvent.class, moduleBeta),
3877 pluginModuleEvent(BeforePluginModuleDisabledEvent.class, moduleAlpha),
3878 pluginModuleStateChange(moduleAlpha, false),
3879 pluginModuleEvent(PluginModuleDisabledEvent.class, moduleAlpha)
3880 ));
3881
3882
3883 final int pluginDisablingIndex = indexOf(disableEvents, Predicates.instanceOf(BeforePluginDisabledEvent.class));
3884 final int firstPluginModuleDisableEvent = disableEvents.indexOf(get(pluginModuleDisableEvents, 0));
3885 assertThat(firstPluginModuleDisableEvent, greaterThan(pluginDisablingIndex));
3886 final int lastPluginModuleDisableEventIndex = disableEvents.indexOf(getLast(pluginModuleDisableEvents));
3887 final int pluginDisabledIndex = indexOf(disableEvents, Predicates.instanceOf(PluginDisabledEvent.class));
3888 assertThat(lastPluginModuleDisableEventIndex, lessThan(pluginDisabledIndex));
3889
3890 final ArgumentCaptor<Object> enableEventsCaptor = ArgumentCaptor.forClass(Object.class);
3891 doNothing().when(pluginEventManager).broadcast(enableEventsCaptor.capture());
3892
3893 defaultPluginManager.enablePlugins(pluginKey);
3894
3895 final List<Object> enableEvents = enableEventsCaptor.getAllValues();
3896 final Iterable<Object> pluginModuleEnableEvents = filter(enableEvents, isPluginModuleEvent);
3897 assertThat(pluginModuleEnableEvents, contains(
3898 pluginModuleStateChange(moduleAlpha, true),
3899 pluginModuleEvent(PluginModuleEnabledEvent.class, moduleAlpha),
3900 pluginModuleStateChange(moduleBeta, true),
3901 pluginModuleEvent(PluginModuleEnabledEvent.class, moduleBeta)
3902 ));
3903
3904
3905 final int pluginEnablingIndex = indexOf(disableEvents, Predicates.instanceOf(BeforePluginDisabledEvent.class));
3906 final int firstPluginModuleEnableEvent = disableEvents.indexOf(get(pluginModuleDisableEvents, 0));
3907 assertThat(firstPluginModuleEnableEvent, greaterThan(pluginEnablingIndex));
3908 final int lastPluginModuleEnableEventIndex = disableEvents.indexOf(getLast(pluginModuleDisableEvents));
3909 final int pluginEnabledIndex = indexOf(disableEvents, Predicates.instanceOf(PluginDisabledEvent.class));
3910 assertThat(lastPluginModuleEnableEventIndex, lessThan(pluginEnabledIndex));
3911 }
3912
3913 private ModuleDescriptor<?> mockStateChangePluginModule(
3914 final String pluginKey, final String moduleKey, final PluginEventManager pluginEventManager)
3915 {
3916 final ModuleDescriptor<?> module = mock(ModuleDescriptor.class, withSettings().extraInterfaces(StateAware.class));
3917 when(module.getPluginKey()).thenReturn(pluginKey);
3918 when(module.getCompleteKey()).thenReturn(new ModuleCompleteKey(pluginKey, moduleKey).getCompleteKey());
3919 doAnswerModuleStateChangeWhen(module, true, pluginEventManager).enabled();
3920 doAnswerModuleStateChangeWhen(module, false, pluginEventManager).disabled();
3921 return module;
3922 }
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932 private StateAware doAnswerModuleStateChangeWhen(
3933 final ModuleDescriptor<?> module, final boolean enabled, final PluginEventManager pluginEventManager)
3934 {
3935 final Answer answer = new Answer()
3936 {
3937 @Override
3938 public Object answer(final InvocationOnMock invocation) throws Throwable
3939 {
3940 pluginEventManager.broadcast(new PluginModuleStateMarkerEvent(module, enabled));
3941 return null;
3942 }
3943 };
3944 return doAnswer(answer).when((StateAware) module);
3945 }
3946
3947 private Matcher<Object> pluginModuleStateChange(final ModuleDescriptor module, final boolean enabled)
3948 {
3949 final Matcher<Object> pluginModuleEventMatcher = pluginModuleEvent(PluginModuleStateMarkerEvent.class, module);
3950 final Matcher<Boolean> pluginModuleStateMatcher = is(enabled);
3951
3952 return new TypeSafeMatcher<Object>()
3953 {
3954 @Override
3955 protected boolean matchesSafely(final Object event)
3956 {
3957 return pluginModuleEventMatcher.matches(event)
3958
3959 && pluginModuleStateMatcher.matches(((PluginModuleStateMarkerEvent)event).isEnabled());
3960 }
3961
3962 @Override
3963 public void describeTo(final Description description)
3964 {
3965 pluginModuleEventMatcher.describeTo(description);
3966 description.appendText(" and .isEnabled() ");
3967 pluginModuleStateMatcher.describeTo(description);
3968 }
3969 };
3970 }
3971
3972
3973
3974
3975 private static class PluginModuleStateMarkerEvent
3976 {
3977 private final ModuleDescriptor module;
3978 private final boolean enabled;
3979
3980 PluginModuleStateMarkerEvent(final ModuleDescriptor module, final boolean enabled)
3981 {
3982 this.module = module;
3983 this.enabled = enabled;
3984 }
3985
3986 public ModuleDescriptor getModule()
3987 {
3988 return module;
3989 }
3990
3991 public boolean isEnabled()
3992 {
3993 return enabled;
3994 }
3995
3996 @Override
3997 public String toString()
3998 {
3999 return "Marker event for " + module + " with enabled " + Boolean.toString(enabled);
4000 }
4001 }
4002
4003
4004
4005
4006 private static class PluginModuleEventBaseClassShims
4007 {
4008 private final static Map<Object, Function<Object, ModuleDescriptor>> pluginModuleEventExtractors =
4009 ImmutableMap.<Object, Function<Object, ModuleDescriptor>>builder()
4010 .put(BeforePluginModuleDisabledEvent.class, new Function<Object, ModuleDescriptor>()
4011 {
4012 @Override
4013 public ModuleDescriptor apply(final Object pluginModuleEvent)
4014 {
4015 return ((BeforePluginModuleDisabledEvent) pluginModuleEvent).getModule();
4016 }
4017 })
4018 .put(PluginModuleDisabledEvent.class, new Function<Object, ModuleDescriptor>()
4019 {
4020 @Override
4021 public ModuleDescriptor apply(final Object pluginModuleEvent)
4022 {
4023 return ((PluginModuleDisabledEvent) pluginModuleEvent).getModule();
4024 }
4025 })
4026 .put(PluginModuleEnabledEvent.class, new Function<Object, ModuleDescriptor>()
4027 {
4028 @Override
4029 public ModuleDescriptor apply(final Object pluginModuleEvent)
4030 {
4031 return ((PluginModuleEnabledEvent) pluginModuleEvent).getModule();
4032 }
4033 })
4034 .put(PluginModuleStateMarkerEvent.class, new Function<Object, ModuleDescriptor>()
4035 {
4036 @Override
4037 public ModuleDescriptor apply(final Object pluginModuleEvent)
4038 {
4039 return ((PluginModuleStateMarkerEvent) pluginModuleEvent).getModule();
4040 }
4041 })
4042 .build();
4043
4044 private static Predicate<Object> isPluginModuleEvent()
4045 {
4046 return new Predicate<Object>()
4047 {
4048 @Override
4049 public boolean apply(final Object pluginModuleEvent)
4050 {
4051 return (null != pluginModuleEventExtractors.get(pluginModuleEvent.getClass()));
4052 }
4053 };
4054 }
4055
4056 private static ModuleDescriptor getModuleFromPluginModuleEvent(final Object pluginModuleEvent)
4057 {
4058 return pluginModuleEventExtractors.get(pluginModuleEvent.getClass()).apply(pluginModuleEvent);
4059 }
4060 }
4061
4062 private Matcher<Object> pluginModuleEvent(final Class clazz, final ModuleDescriptor module)
4063 {
4064 final Matcher<Class> classMatcher = instanceOf(clazz);
4065 final Matcher<ModuleDescriptor> pluginModuleMatcher = is(module);
4066 return new TypeSafeMatcher<Object>()
4067 {
4068 @Override
4069 protected boolean matchesSafely(final Object pluginModuleEvent)
4070 {
4071 final ModuleDescriptor module = PluginModuleEventBaseClassShims.getModuleFromPluginModuleEvent(pluginModuleEvent);
4072 return classMatcher.matches(pluginModuleEvent) && pluginModuleMatcher.matches(module);
4073 }
4074
4075 @Override
4076 public void describeTo(final Description description)
4077 {
4078 classMatcher.describeTo(description);
4079 description.appendText(" for which .getModule() ");
4080 pluginModuleMatcher.describeTo(description);
4081 }
4082 };
4083 }
4084
4085 @Test
4086 public void shutdownEventsAreSent()
4087 {
4088 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
4089 final List<PluginLoader> pluginLoaders = Collections.emptyList();
4090 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
4091 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
4092
4093 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
4094 pluginPersistentStateStore, pluginLoaders, moduleDescriptorFactory, pluginEventManager);
4095
4096 defaultPluginManager.init();
4097
4098
4099 final ArgumentCaptor<Object> shutdownEvents = ArgumentCaptor.forClass(Object.class);
4100 final NotificationException notificationException = new NotificationException(new Throwable());
4101 doThrow(notificationException).when(pluginEventManager).broadcast(shutdownEvents.capture());
4102
4103 defaultPluginManager.shutdown();
4104
4105
4106 assertThat(shutdownEvents.getAllValues(), contains(
4107 instanceOf(PluginFrameworkShuttingDownEvent.class), instanceOf(PluginFrameworkShutdownEvent.class)));
4108 }
4109
4110 @Test
4111 public void lateStartupDoesntRetryEnableWhenNotRequested()
4112 {
4113 validateLateStartupRetryEnable(false);
4114 }
4115
4116 @Test
4117 public void lateStartupDoesRetryEnableWhenRequested()
4118 {
4119 validateLateStartupRetryEnable(true);
4120 }
4121
4122 private void validateLateStartupRetryEnable(final boolean allowEnableRetry)
4123 {
4124
4125 System.setProperty(getLateStartupEnableRetryProperty(), Boolean.toString(allowEnableRetry));
4126
4127 final Plugin enablesFinePlugin = mockStateChangePlugin("enablesFine", pluginEventManager);
4128
4129 final Plugin firstEnableFailsPlugin = mockPlugin("firstEnableFails");
4130 doAnswerPluginStateChangeWhen(firstEnableFailsPlugin, PluginState.INSTALLED, pluginEventManager).install();
4131
4132 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
4133
4134 mockPluginsSortOrder(enablesFinePlugin, firstEnableFailsPlugin);
4135
4136 final PluginLoader pluginLoader = mockPluginLoaderForPlugins(enablesFinePlugin, firstEnableFailsPlugin);
4137
4138 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
4139 pluginPersistentStateStore,
4140 ImmutableList.of(pluginLoader),
4141 mock(ModuleDescriptorFactory.class),
4142 pluginEventManager,
4143 mock(PluginExceptionInterception.class)
4144 );
4145
4146 defaultPluginManager.earlyStartup();
4147
4148 verify(enablesFinePlugin).enable();
4149 verify(firstEnableFailsPlugin).enable();
4150
4151
4152 doAnswerPluginStateChangeWhen(firstEnableFailsPlugin, PluginState.ENABLED, pluginEventManager).enable();
4153
4154 defaultPluginManager.lateStartup();
4155
4156
4157 verify(enablesFinePlugin).enable();
4158
4159
4160
4161 if (allowEnableRetry)
4162 {
4163
4164
4165 verify(firstEnableFailsPlugin, times(2)).enable();
4166 final String pluginString = firstEnableFailsPlugin.toString();
4167 assertThat(capturedLogging, didLogWarn("Failed to enable", "fallback", "lateStartup", pluginString));
4168 assertThat(capturedLogging, didLogWarn("fallback enabled", "lateStartup", pluginString));
4169 }
4170 else
4171 {
4172
4173 verify(firstEnableFailsPlugin, times(1)).enable();
4174
4175
4176 assertThat(capturedLogging, not(didLogWarn("fallback", "lateStartup")));
4177 }
4178 }
4179
4180 private void mockPluginsSortOrder(final Plugin... mockPlugins)
4181 {
4182 for (int i = 0; i < mockPlugins.length; ++i)
4183 {
4184 for (int j = 0; j < mockPlugins.length; ++j)
4185 {
4186
4187 when(mockPlugins[i].compareTo(mockPlugins[j])).thenReturn(i-j);
4188 }
4189 }
4190 }
4191
4192 private PluginPersistentStateStore mockPluginPersistentStateStore()
4193 {
4194 final PluginPersistentStateStore pluginPersistentStateStore = mock(PluginPersistentStateStore.class, RETURNS_DEEP_STUBS);
4195 when(pluginPersistentStateStore.load().getPluginRestartState(anyString())).thenReturn(PluginRestartState.NONE);
4196 when(pluginPersistentStateStore.load().isEnabled(any(Plugin.class))).thenReturn(true);
4197 when(pluginPersistentStateStore.load().isEnabled(any(ModuleDescriptor.class))).thenReturn(true);
4198 return pluginPersistentStateStore;
4199 }
4200
4201 private PluginLoader mockPluginLoaderForPlugins(final Plugin... plugins)
4202 {
4203 final PluginLoader pluginLoader = mock(PluginLoader.class);
4204 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(plugins));
4205 return pluginLoader;
4206 }
4207
4208 public static class ThingsAreWrongListener
4209 {
4210 private volatile boolean called = false;
4211
4212 @PluginEventListener
4213 public void onFrameworkShutdown(final PluginFrameworkShutdownEvent event)
4214 {
4215 called = true;
4216 throw new NullPointerException("AAAH!");
4217 }
4218
4219 public boolean isCalled()
4220 {
4221 return called;
4222 }
4223 }
4224
4225 public Plugin createPluginWithVersion(final String version)
4226 {
4227 final Plugin p = new StaticPlugin();
4228 p.setKey("test.default.plugin");
4229 final PluginInformation pInfo = p.getPluginInformation();
4230 pInfo.setVersion(version);
4231 return p;
4232 }
4233
4234
4235
4236
4237
4238 private static class SinglePluginLoaderWithRemoval extends SinglePluginLoader
4239 {
4240 public SinglePluginLoaderWithRemoval(final String resource)
4241 {
4242 super(resource);
4243 }
4244
4245 public boolean supportsRemoval()
4246 {
4247
4248 return true;
4249 }
4250
4251 public void removePlugin(final Plugin plugin) throws PluginException
4252 {
4253 plugins = Collections.emptyList();
4254 }
4255
4256 protected StaticPlugin getNewPlugin()
4257 {
4258 return new StaticPlugin()
4259 {
4260 public boolean isUninstallable()
4261 {
4262 return true;
4263 }
4264 };
4265 }
4266 }
4267
4268 class NothingModuleDescriptor extends MockUnusedModuleDescriptor
4269 {
4270 }
4271
4272 @RequiresRestart
4273 public static class RequiresRestartModuleDescriptor extends MockUnusedModuleDescriptor
4274 {
4275 }
4276
4277
4278 public static class RequiresRestartSubclassModuleDescriptor extends RequiresRestartModuleDescriptor
4279 {
4280 }
4281
4282 private class MultiplePluginLoader implements PluginLoader
4283 {
4284 private final String[] descriptorPaths;
4285
4286 public MultiplePluginLoader(final String... descriptorPaths)
4287 {
4288 this.descriptorPaths = descriptorPaths;
4289 }
4290
4291 public Iterable<Plugin> loadAllPlugins(final ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
4292 {
4293 final ImmutableList.Builder<Plugin> result = ImmutableList.builder();
4294 for (final String path : descriptorPaths)
4295 {
4296 final SinglePluginLoader loader = new SinglePluginLoader(path);
4297 result.addAll(loader.loadAllPlugins(moduleDescriptorFactory));
4298 }
4299 return result.build();
4300 }
4301
4302 public boolean supportsAddition()
4303 {
4304 return false;
4305 }
4306
4307 public boolean supportsRemoval()
4308 {
4309 return false;
4310 }
4311
4312 public Iterable<Plugin> loadFoundPlugins(final ModuleDescriptorFactory moduleDescriptorFactory) throws PluginParseException
4313 {
4314 throw new UnsupportedOperationException("This PluginLoader does not support addition.");
4315 }
4316
4317 public void removePlugin(final Plugin plugin) throws PluginException
4318 {
4319 throw new UnsupportedOperationException("This PluginLoader does not support addition.");
4320 }
4321
4322 @Override
4323 public boolean isDynamicPluginLoader()
4324 {
4325 return false;
4326 }
4327 }
4328
4329 private static class DynamicSinglePluginLoader extends SinglePluginLoader implements PluginLoader, DynamicPluginLoader
4330 {
4331 private final AtomicBoolean canLoad = new AtomicBoolean(false);
4332
4333 private final String key;
4334
4335 public DynamicSinglePluginLoader(final String key, final String resource)
4336 {
4337 super(resource);
4338 this.key = key;
4339 }
4340
4341 @Override
4342 public boolean isDynamicPluginLoader()
4343 {
4344 return true;
4345 }
4346
4347 public String canLoad(final PluginArtifact pluginArtifact) throws PluginParseException
4348 {
4349 return canLoad.get() ? key : null;
4350 }
4351
4352 public boolean supportsAddition()
4353 {
4354 return true;
4355 }
4356
4357 @Override
4358 public Iterable<Plugin> loadAllPlugins(ModuleDescriptorFactory moduleDescriptorFactory)
4359 {
4360 if (canLoad.get())
4361 {
4362 return super.loadAllPlugins(moduleDescriptorFactory);
4363 }
4364 else
4365 {
4366 return ImmutableList.of();
4367 }
4368 }
4369
4370 @Override
4371 public Iterable<Plugin> loadFoundPlugins(final ModuleDescriptorFactory moduleDescriptorFactory)
4372 {
4373 if (canLoad.get())
4374 {
4375 return super.loadAllPlugins(moduleDescriptorFactory);
4376 }
4377 else
4378 {
4379 return ImmutableList.of();
4380 }
4381 }
4382 }
4383
4384 private static class CannotEnablePlugin extends StaticPlugin
4385 {
4386 public CannotEnablePlugin()
4387 {
4388 setKey("foo");
4389 }
4390
4391 @Override
4392 protected PluginState enableInternal()
4393 {
4394 throw new RuntimeException("boo");
4395 }
4396
4397 public void disabled()
4398 {
4399 }
4400 }
4401
4402 public static class PluginModuleEnabledListener
4403 {
4404 public volatile boolean called;
4405 @PluginEventListener
4406 public void onEnable(PluginModuleEnabledEvent event)
4407 {
4408 called = true;
4409 }
4410 }
4411
4412 public static class PluginModuleDisabledListener
4413 {
4414 public volatile boolean called;
4415 @PluginEventListener
4416 public void onDisable(PluginModuleDisabledEvent event)
4417 {
4418 called = true;
4419 }
4420 }
4421
4422 public static class PluginDisabledListener
4423 {
4424 public volatile boolean called;
4425 @PluginEventListener
4426 public void onDisable(PluginDisabledEvent event)
4427 {
4428 called = true;
4429 }
4430 }
4431
4432 private class Wrapper
4433 {
4434 private final String earlyKey;
4435 private final String laterKey;
4436 private Plugin laterPlugin;
4437 private DefaultPluginManager defaultPluginManager;
4438
4439 public Wrapper(String earlyKey, String laterKey)
4440 {
4441 this.earlyKey = earlyKey;
4442 this.laterKey = laterKey;
4443 }
4444
4445 public Plugin getLaterPlugin()
4446 {
4447 return laterPlugin;
4448 }
4449
4450 public DefaultPluginManager getDefaultPluginManager()
4451 {
4452 return defaultPluginManager;
4453 }
4454
4455 public Wrapper invoke(final boolean isLatePluginEnabledByDefault)
4456 {
4457 final PluginPersistentStateStore pluginPersistentStateStore = mock(
4458 PluginPersistentStateStore.class, RETURNS_DEEP_STUBS);
4459 when(pluginPersistentStateStore.load().getPluginRestartState(earlyKey)).thenReturn(PluginRestartState.NONE);
4460 when(pluginPersistentStateStore.load().getPluginRestartState(laterKey)).thenReturn(PluginRestartState.NONE);
4461 doAnswer(new Answer<Void>()
4462 {
4463 @Override
4464 public Void answer(InvocationOnMock invocationOnMock) throws Throwable
4465 {
4466 final PluginPersistentState pluginState = (PluginPersistentState) invocationOnMock.getArguments()[0];
4467 when(pluginPersistentStateStore.load()).thenReturn(pluginState);
4468 return null;
4469 }
4470 }).when(pluginPersistentStateStore).save(isA(PluginPersistentState.class));
4471
4472 PluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
4473 Plugin earlyPlugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
4474 laterPlugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
4475 when(earlyPlugin.getKey()).thenReturn(earlyKey);
4476 when(laterPlugin.getKey()).thenReturn(laterKey);
4477 when(earlyPlugin.isEnabledByDefault()).thenReturn(true);
4478 when(laterPlugin.isEnabledByDefault()).thenReturn(isLatePluginEnabledByDefault);
4479 List<Plugin> bothPlugins = Arrays.asList(earlyPlugin, laterPlugin);
4480 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(bothPlugins);
4481 List<PluginLoader> pluginLoaders = Arrays.asList(pluginLoader);
4482
4483 ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
4484
4485 PluginEventManager pluginEventManager = mock(PluginEventManager.class);
4486
4487 PluginPredicate pluginPredicate = mock(PluginPredicate.class);
4488 when(pluginPredicate.matches(earlyPlugin)).thenReturn(false);
4489 when(pluginPredicate.matches(laterPlugin)).thenReturn(true);
4490
4491 defaultPluginManager = new DefaultPluginManager(
4492 pluginPersistentStateStore, pluginLoaders, moduleDescriptorFactory, pluginEventManager, pluginPredicate);
4493 return this;
4494 }
4495 }
4496 }