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