1 package com.atlassian.plugin.manager;
2
3 import com.atlassian.plugin.DefaultModuleDescriptorFactory;
4 import com.atlassian.plugin.ModuleDescriptor;
5 import com.atlassian.plugin.ModuleDescriptorFactory;
6 import com.atlassian.plugin.Permissions;
7 import com.atlassian.plugin.Plugin;
8 import com.atlassian.plugin.PluginArtifact;
9 import com.atlassian.plugin.PluginController;
10 import com.atlassian.plugin.PluginDependencies;
11 import com.atlassian.plugin.PluginException;
12 import com.atlassian.plugin.PluginInstaller;
13 import com.atlassian.plugin.PluginInternal;
14 import com.atlassian.plugin.PluginParseException;
15 import com.atlassian.plugin.PluginRestartState;
16 import com.atlassian.plugin.PluginState;
17 import com.atlassian.plugin.StateAware;
18 import com.atlassian.plugin.event.NotificationException;
19 import com.atlassian.plugin.event.PluginEventManager;
20 import com.atlassian.plugin.event.events.BeforePluginDisabledEvent;
21 import com.atlassian.plugin.event.events.BeforePluginModuleDisabledEvent;
22 import com.atlassian.plugin.event.events.PluginDependentsChangedEvent;
23 import com.atlassian.plugin.event.events.PluginDisabledEvent;
24 import com.atlassian.plugin.event.events.PluginDisablingEvent;
25 import com.atlassian.plugin.event.events.PluginEnabledEvent;
26 import com.atlassian.plugin.event.events.PluginEnablingEvent;
27 import com.atlassian.plugin.event.events.PluginEvent;
28 import com.atlassian.plugin.event.events.PluginFrameworkShutdownEvent;
29 import com.atlassian.plugin.event.events.PluginFrameworkShuttingDownEvent;
30 import com.atlassian.plugin.event.events.PluginInstalledEvent;
31 import com.atlassian.plugin.event.events.PluginInstallingEvent;
32 import com.atlassian.plugin.event.events.PluginModuleDisabledEvent;
33 import com.atlassian.plugin.event.events.PluginModuleDisablingEvent;
34 import com.atlassian.plugin.event.events.PluginModuleEnabledEvent;
35 import com.atlassian.plugin.event.events.PluginModuleEnablingEvent;
36 import com.atlassian.plugin.event.events.PluginModuleEvent;
37 import com.atlassian.plugin.event.events.PluginUninstalledEvent;
38 import com.atlassian.plugin.event.events.PluginUninstallingEvent;
39 import com.atlassian.plugin.event.events.PluginUpgradedEvent;
40 import com.atlassian.plugin.event.events.PluginUpgradingEvent;
41 import com.atlassian.plugin.event.impl.DefaultPluginEventManager;
42 import com.atlassian.plugin.event.listeners.PassListener;
43 import com.atlassian.plugin.exception.PluginExceptionInterception;
44 import com.atlassian.plugin.hostcontainer.DefaultHostContainer;
45 import com.atlassian.plugin.impl.StaticPlugin;
46 import com.atlassian.plugin.loaders.DiscardablePluginLoader;
47 import com.atlassian.plugin.loaders.DynamicPluginLoader;
48 import com.atlassian.plugin.loaders.PluginLoader;
49 import com.atlassian.plugin.manager.store.MemoryPluginPersistentStateStore;
50 import com.atlassian.plugin.parsers.DescriptorParser;
51 import com.google.common.base.Predicates;
52 import com.google.common.collect.ImmutableList;
53 import com.google.common.collect.ImmutableSet;
54 import org.dom4j.Element;
55 import org.junit.Rule;
56 import org.junit.Test;
57 import org.junit.rules.ExpectedException;
58 import org.mockito.ArgumentCaptor;
59
60 import java.io.IOException;
61 import java.util.ArrayList;
62 import java.util.Arrays;
63 import java.util.Collection;
64 import java.util.Collections;
65 import java.util.List;
66 import java.util.stream.Collectors;
67
68 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.anyPluginEvent;
69 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.anyPluginStateChange;
70 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.mockPluginLoaderForPlugins;
71 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.mockPluginPersistentStateStore;
72 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.mockPluginsSortOrder;
73 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.mockStateChangePlugin;
74 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.mockStateChangePluginModule;
75 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.pluginEvent;
76 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.pluginModuleEvent;
77 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.pluginModuleStateChange;
78 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.pluginStateChange;
79 import static com.google.common.collect.ImmutableList.copyOf;
80 import static com.google.common.collect.Iterables.filter;
81 import static com.google.common.collect.Iterables.get;
82 import static com.google.common.collect.Iterables.getLast;
83 import static com.google.common.collect.Iterables.indexOf;
84 import static org.hamcrest.MatcherAssert.assertThat;
85 import static org.hamcrest.Matchers.contains;
86 import static org.hamcrest.Matchers.containsInAnyOrder;
87 import static org.hamcrest.Matchers.containsString;
88 import static org.hamcrest.Matchers.empty;
89 import static org.hamcrest.Matchers.greaterThan;
90 import static org.hamcrest.Matchers.instanceOf;
91 import static org.hamcrest.Matchers.is;
92 import static org.hamcrest.Matchers.lessThan;
93 import static org.junit.Assert.assertEquals;
94 import static org.junit.Assert.assertFalse;
95 import static org.junit.Assert.assertTrue;
96 import static org.junit.Assert.fail;
97 import static org.mockito.Matchers.any;
98 import static org.mockito.Matchers.isA;
99 import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
100 import static org.mockito.Mockito.doNothing;
101 import static org.mockito.Mockito.doThrow;
102 import static org.mockito.Mockito.mock;
103 import static org.mockito.Mockito.never;
104 import static org.mockito.Mockito.times;
105 import static org.mockito.Mockito.verify;
106 import static org.mockito.Mockito.when;
107 import static org.mockito.Mockito.withSettings;
108
109 public class TestDefaultPluginManagerController {
110 @Rule
111 public ExpectedException expectedException = ExpectedException.none();
112
113
114
115
116 private PluginController manager;
117
118 private PluginEventManager pluginEventManager = new DefaultPluginEventManager();
119
120 private ModuleDescriptorFactory moduleDescriptorFactory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
121
122 private DefaultPluginManager newDefaultPluginManager(PluginLoader... pluginLoaders) {
123 DefaultPluginManager dpm = DefaultPluginManager.newBuilder().
124 withPluginLoaders(copyOf(pluginLoaders))
125 .withModuleDescriptorFactory(moduleDescriptorFactory)
126 .withPluginEventManager(pluginEventManager)
127 .withStore(new MemoryPluginPersistentStateStore())
128 .withVerifyRequiredPlugins(true)
129 .build();
130 manager = dpm;
131 return dpm;
132 }
133
134 private PluginController initNewDefaultPluginManager(PluginLoader... pluginLoaders) {
135 DefaultPluginManager dpm = newDefaultPluginManager(pluginLoaders);
136 dpm.init();
137 return dpm;
138 }
139
140 @Test
141 public void pluginReturnedByLoadAllPluginsButNotUsedIsDiscarded() {
142 final String pluginKey = "pluginKey";
143 final PluginPersistentStateStore pluginPersistentStateStore = mock(
144 PluginPersistentStateStore.class, RETURNS_DEEP_STUBS);
145 when(pluginPersistentStateStore.load().getPluginRestartState(pluginKey)).thenReturn(PluginRestartState.NONE);
146
147 DiscardablePluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
148 Plugin pluginV1 = mock(Plugin.class, RETURNS_DEEP_STUBS);
149 Plugin pluginV2 = mock(Plugin.class, RETURNS_DEEP_STUBS);
150 when(pluginV1.getKey()).thenReturn(pluginKey);
151 when(pluginV2.getKey()).thenReturn(pluginKey);
152
153 when(pluginV1.compareTo(pluginV2)).thenReturn(-1);
154 when(pluginV2.compareTo(pluginV1)).thenReturn(1);
155 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(pluginV1, pluginV2));
156
157 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
158 pluginPersistentStateStore,
159 Arrays.asList((PluginLoader) pluginLoader),
160 mock(ModuleDescriptorFactory.class),
161 mock(PluginEventManager.class),
162 mock(PluginExceptionInterception.class)
163 );
164 defaultPluginManager.init();
165 verify(pluginLoader).discardPlugin(pluginV1);
166 }
167
168 @Test
169 public void oldPluginReturnedByLoadFoundPluginsIsDiscarded() {
170 final String pluginKey = "pluginKey";
171 final PluginPersistentStateStore pluginPersistentStateStore = mock(
172 PluginPersistentStateStore.class, RETURNS_DEEP_STUBS);
173 when(pluginPersistentStateStore.load().getPluginRestartState(pluginKey)).thenReturn(PluginRestartState.NONE);
174
175 DiscardablePluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
176 Plugin pluginV1 = mock(Plugin.class, RETURNS_DEEP_STUBS);
177 Plugin pluginV2 = mock(Plugin.class, RETURNS_DEEP_STUBS);
178 when(pluginV1.getKey()).thenReturn(pluginKey);
179 when(pluginV2.getKey()).thenReturn(pluginKey);
180
181 when(pluginV1.compareTo(pluginV2)).thenReturn(-1);
182 when(pluginV2.compareTo(pluginV1)).thenReturn(1);
183 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(pluginV2));
184 when(pluginLoader.supportsAddition()).thenReturn(true);
185 when(pluginLoader.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(pluginV1));
186
187 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
188 pluginPersistentStateStore,
189 Arrays.asList((PluginLoader) pluginLoader),
190 mock(ModuleDescriptorFactory.class),
191 mock(PluginEventManager.class),
192 mock(PluginExceptionInterception.class)
193 );
194 defaultPluginManager.init();
195 final int found = defaultPluginManager.scanForNewPlugins();
196 assertThat(found, is(1));
197 verify(pluginLoader).discardPlugin(pluginV1);
198 }
199
200 @Test
201 public void upgradePluginDisablesDependentPlugins() {
202 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
203
204 final String pluginKey = "pluginKey";
205 final Plugin pluginV1 = mockStateChangePlugin(pluginKey, pluginEventManager);
206 final Plugin pluginV2 = mockStateChangePlugin(pluginKey, pluginEventManager);
207 when(pluginV1.isDeleteable()).thenReturn(true);
208
209 final String dependentPluginKey = "dependentPluginKey";
210 final Plugin dependentPlugin = mockStateChangePlugin(dependentPluginKey, pluginEventManager);
211 when(dependentPlugin.isEnabledByDefault()).thenReturn(true);
212 when(dependentPlugin.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of(pluginKey), null, null));
213
214
215 mockPluginsSortOrder(pluginV1, pluginV2, dependentPlugin);
216
217 final DiscardablePluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
218 when(pluginLoader.supportsAddition()).thenReturn(true);
219 when(pluginLoader.supportsRemoval()).thenReturn(true);
220 final List<Plugin> initialPlugins = Arrays.asList(pluginV1, dependentPlugin);
221 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(initialPlugins);
222 when(pluginLoader.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(pluginV2));
223
224 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
225 pluginPersistentStateStore,
226 Arrays.asList((PluginLoader) pluginLoader),
227 mock(ModuleDescriptorFactory.class),
228 mock(PluginEventManager.class),
229 mock(PluginExceptionInterception.class)
230 );
231 defaultPluginManager.init();
232 verify(pluginV1).enable();
233 verify(dependentPlugin).enable();
234 when(pluginV1.getPluginState()).thenReturn(PluginState.ENABLED);
235 when(dependentPlugin.getPluginState()).thenReturn(PluginState.ENABLED);
236
237 final int found = defaultPluginManager.scanForNewPlugins();
238 assertThat(found, is(1));
239 verify(dependentPlugin).disable();
240 verify(dependentPlugin, times(2)).enable();
241 }
242
243 @Test
244 public void scanForNewPluginsScansAllPluginLoaders() {
245 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
246
247 final PluginLoader pluginLoaderAlpha = mockPluginLoaderForPlugins();
248 when(pluginLoaderAlpha.supportsAddition()).thenReturn(true);
249 final PluginLoader pluginLoaderBeta = mockPluginLoaderForPlugins();
250 when(pluginLoaderBeta.supportsAddition()).thenReturn(true);
251
252 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
253 pluginPersistentStateStore,
254 Arrays.asList(pluginLoaderAlpha, pluginLoaderBeta),
255 mock(ModuleDescriptorFactory.class),
256 mock(PluginEventManager.class),
257 mock(PluginExceptionInterception.class)
258 );
259 defaultPluginManager.init();
260
261 assertThat(defaultPluginManager.getPlugins(), empty());
262
263 final Plugin pluginAlpha = mock(Plugin.class, RETURNS_DEEP_STUBS);
264 when(pluginAlpha.getKey()).thenReturn("alpha");
265 when(pluginLoaderAlpha.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(pluginAlpha));
266 final Plugin pluginBeta = mock(Plugin.class, RETURNS_DEEP_STUBS);
267 when(pluginBeta.getKey()).thenReturn("beta");
268 when(pluginLoaderBeta.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(pluginBeta));
269
270 final int found = defaultPluginManager.scanForNewPlugins();
271
272 assertThat(found, is(2));
273 assertThat(defaultPluginManager.getPlugins(), containsInAnyOrder(pluginAlpha, pluginBeta));
274 }
275
276
277 @Test
278 public void installEventSequencing() {
279 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
280
281 final Plugin alphaPlugin = mockStateChangePlugin("alpha", pluginEventManager);
282 final Plugin betaPlugin = mockStateChangePlugin("beta", pluginEventManager);
283
284 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
285
286 final PluginLoader pluginLoader = mockPluginLoaderForPlugins(alphaPlugin);
287 when(pluginLoader.supportsAddition()).thenReturn(true);
288
289 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
290 pluginPersistentStateStore,
291 ImmutableList.of(pluginLoader),
292 mock(ModuleDescriptorFactory.class),
293 pluginEventManager,
294 mock(PluginExceptionInterception.class)
295 );
296
297 final ArgumentCaptor<Object> initEvents = ArgumentCaptor.forClass(Object.class);
298 doNothing().when(pluginEventManager).broadcast(initEvents.capture());
299
300 defaultPluginManager.init();
301
302 assertThat(filter(initEvents.getAllValues(), PluginEvent.class), contains(
303 pluginEvent(PluginInstallingEvent.class, alphaPlugin),
304 pluginStateChange(alphaPlugin, PluginState.INSTALLED),
305 pluginEvent(PluginInstalledEvent.class, alphaPlugin),
306 pluginEvent(PluginEnablingEvent.class, alphaPlugin),
307 pluginStateChange(alphaPlugin, PluginState.ENABLED),
308 pluginEvent(PluginEnabledEvent.class, alphaPlugin)
309 ));
310
311 when(pluginLoader.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(betaPlugin));
312
313 final ArgumentCaptor<Object> scanEvents = ArgumentCaptor.forClass(Object.class);
314 doNothing().when(pluginEventManager).broadcast(scanEvents.capture());
315
316 final int found = defaultPluginManager.scanForNewPlugins();
317
318 assertThat(found, is(1));
319
320 assertThat(filter(scanEvents.getAllValues(), PluginEvent.class), contains(
321 pluginEvent(PluginInstallingEvent.class, betaPlugin),
322 pluginStateChange(betaPlugin, PluginState.INSTALLED),
323 pluginEvent(PluginInstalledEvent.class, betaPlugin),
324 pluginEvent(PluginEnablingEvent.class, betaPlugin),
325 pluginStateChange(betaPlugin, PluginState.ENABLED),
326 pluginEvent(PluginEnabledEvent.class, betaPlugin)
327 ));
328 }
329
330
331 @Test
332 public void upgradeEventSequencing() {
333 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
334
335 final String pluginKey = "pluginKey";
336 final Plugin pluginV1 = mockStateChangePlugin(pluginKey, pluginEventManager);
337 final Plugin pluginV2 = mockStateChangePlugin(pluginKey, pluginEventManager);
338
339 final Plugin dependentPlugin = mockStateChangePlugin("dependentPluginKey", pluginEventManager);
340 when(dependentPlugin.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of(pluginKey), null, null));
341
342 mockPluginsSortOrder(dependentPlugin, pluginV1, pluginV2);
343
344 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
345
346 final PluginLoader pluginLoader = mockPluginLoaderForPlugins(pluginV1, dependentPlugin);
347 when(pluginLoader.supportsAddition()).thenReturn(true);
348 when(pluginLoader.supportsRemoval()).thenReturn(true);
349
350 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
351 pluginPersistentStateStore,
352 ImmutableList.of(pluginLoader),
353 mock(ModuleDescriptorFactory.class),
354 pluginEventManager,
355 mock(PluginExceptionInterception.class)
356 );
357
358 defaultPluginManager.init();
359
360 when(pluginLoader.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(ImmutableList.of(pluginV2));
361 final ArgumentCaptor<Object> events = ArgumentCaptor.forClass(Object.class);
362 doNothing().when(pluginEventManager).broadcast(events.capture());
363
364 final int found = defaultPluginManager.scanForNewPlugins();
365
366 assertThat(found, is(1));
367
368 assertThat(filter(events.getAllValues(), PluginEvent.class), contains(
369 pluginEvent(BeforePluginDisabledEvent.class, dependentPlugin),
370 pluginEvent(PluginDisablingEvent.class, dependentPlugin),
371 pluginStateChange(dependentPlugin, PluginState.DISABLED),
372 pluginEvent(PluginDisabledEvent.class, dependentPlugin),
373 pluginEvent(PluginUpgradingEvent.class, pluginV1),
374 pluginEvent(BeforePluginDisabledEvent.class, pluginV1),
375 pluginEvent(PluginDisablingEvent.class, pluginV1),
376 pluginStateChange(pluginV1, PluginState.DISABLED),
377 pluginEvent(PluginDisabledEvent.class, pluginV1),
378 pluginStateChange(pluginV2, PluginState.INSTALLED),
379 pluginEvent(PluginUpgradedEvent.class, pluginV2),
380 pluginEvent(PluginEnablingEvent.class, pluginV2),
381 pluginEvent(PluginEnablingEvent.class, dependentPlugin),
382 pluginStateChange(pluginV2, PluginState.ENABLED),
383 pluginStateChange(dependentPlugin, PluginState.ENABLED),
384 pluginEvent(PluginEnabledEvent.class, pluginV2),
385 pluginEvent(PluginEnabledEvent.class, dependentPlugin),
386 pluginEvent(PluginDependentsChangedEvent.class, pluginV2)
387 ));
388 }
389
390 @Test
391 public void uninstallEventSequencing() {
392 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
393
394 final String pluginKey = "pluginKey";
395 final Plugin plugin = mockStateChangePlugin(pluginKey, pluginEventManager);
396
397 final Plugin dependentPlugin = mockStateChangePlugin("dependentPluginKey", pluginEventManager);
398 when(dependentPlugin.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of(pluginKey), null, null));
399
400 mockPluginsSortOrder(dependentPlugin, plugin);
401
402 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
403
404 final PluginLoader pluginLoader = mockPluginLoaderForPlugins(plugin, dependentPlugin);
405 when(pluginLoader.supportsRemoval()).thenReturn(true);
406
407 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
408 pluginPersistentStateStore,
409 ImmutableList.of(pluginLoader),
410 mock(ModuleDescriptorFactory.class),
411 pluginEventManager,
412 mock(PluginExceptionInterception.class)
413 );
414
415 defaultPluginManager.init();
416
417 final ArgumentCaptor<Object> events = ArgumentCaptor.forClass(Object.class);
418 doNothing().when(pluginEventManager).broadcast(events.capture());
419
420 defaultPluginManager.uninstall(defaultPluginManager.getPlugin(pluginKey));
421
422 assertThat(filter(events.getAllValues(), PluginEvent.class), contains(
423 pluginEvent(BeforePluginDisabledEvent.class, dependentPlugin),
424 pluginEvent(PluginDisablingEvent.class, dependentPlugin),
425 pluginStateChange(dependentPlugin, PluginState.DISABLED),
426 pluginEvent(PluginDisabledEvent.class, dependentPlugin),
427 pluginEvent(PluginUninstallingEvent.class, plugin),
428 pluginEvent(BeforePluginDisabledEvent.class, plugin),
429 pluginEvent(PluginDisablingEvent.class, plugin),
430 pluginStateChange(plugin, PluginState.DISABLED),
431 pluginEvent(PluginDisabledEvent.class, plugin),
432 pluginEvent(PluginUninstalledEvent.class, plugin),
433 pluginEvent(PluginDependentsChangedEvent.class, plugin)
434 ));
435 }
436
437 @Test
438 public void uninstallPluginsEventSequencing() {
439 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
440
441 final String pluginKey = "pluginKey";
442 final Plugin plugin = mockStateChangePlugin(pluginKey, pluginEventManager);
443
444 final Plugin dependentPlugin = mockStateChangePlugin("dependentPluginKey", pluginEventManager);
445 when(dependentPlugin.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of(pluginKey), null, null));
446
447 final String dependent2PluginKey = "dependent2PluginKey";
448 final Plugin dependent2Plugin = mockStateChangePlugin(dependent2PluginKey, pluginEventManager);
449 when(dependent2Plugin.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of(pluginKey), null, null));
450
451 mockPluginsSortOrder(dependentPlugin, dependent2Plugin, plugin);
452
453 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
454
455 final PluginLoader pluginLoader = mockPluginLoaderForPlugins(plugin, dependentPlugin, dependent2Plugin);
456 when(pluginLoader.supportsRemoval()).thenReturn(true);
457
458 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
459 pluginPersistentStateStore,
460 ImmutableList.of(pluginLoader),
461 mock(ModuleDescriptorFactory.class),
462 pluginEventManager,
463 mock(PluginExceptionInterception.class)
464 );
465
466 defaultPluginManager.init();
467
468 final ArgumentCaptor<Object> events = ArgumentCaptor.forClass(Object.class);
469 doNothing().when(pluginEventManager).broadcast(events.capture());
470
471 Plugin[] uninstalled = new Plugin[]{plugin, dependent2Plugin};
472
473 defaultPluginManager.uninstallPlugins(Arrays.asList(uninstalled));
474
475
476 List<PluginEvent> pluginEvents = events.getAllValues().stream()
477 .filter(PluginEvent.class::isInstance)
478 .map(PluginEvent.class::cast)
479 .collect(Collectors.toList());
480
481
482 Arrays.stream(uninstalled)
483 .forEach(p -> assertThat(
484 pluginEvents.stream()
485 .filter(e -> e.getPlugin() == p)
486 .collect(Collectors.toList()),
487 contains(
488 pluginEvent(PluginUninstallingEvent.class, p),
489 pluginEvent(BeforePluginDisabledEvent.class, p),
490 pluginEvent(PluginDisablingEvent.class, p),
491 pluginStateChange(p, PluginState.DISABLED),
492 pluginEvent(PluginDisabledEvent.class, p),
493 pluginEvent(PluginUninstalledEvent.class, p),
494 pluginEvent(PluginDependentsChangedEvent.class, p)
495 )));
496
497
498 assertThat(pluginEvents,
499 contains(
500
501 pluginEvent(BeforePluginDisabledEvent.class, dependentPlugin),
502 pluginEvent(PluginDisablingEvent.class, dependentPlugin),
503 pluginStateChange(dependentPlugin, PluginState.DISABLED),
504 pluginEvent(PluginDisabledEvent.class, dependentPlugin),
505
506
507 anyPluginEvent(PluginUninstallingEvent.class, uninstalled),
508 anyPluginEvent(PluginUninstallingEvent.class, uninstalled),
509
510
511 anyPluginEvent(BeforePluginDisabledEvent.class, uninstalled),
512 anyPluginEvent(PluginDisablingEvent.class, uninstalled),
513 anyPluginStateChange(PluginState.DISABLED, uninstalled),
514 anyPluginEvent(PluginDisabledEvent.class, uninstalled),
515
516
517 anyPluginEvent(BeforePluginDisabledEvent.class, uninstalled),
518 anyPluginEvent(PluginDisablingEvent.class, uninstalled),
519 anyPluginStateChange(PluginState.DISABLED, uninstalled),
520 anyPluginEvent(PluginDisabledEvent.class, uninstalled),
521
522
523 anyPluginEvent(PluginUninstalledEvent.class, uninstalled),
524 anyPluginEvent(PluginUninstalledEvent.class, uninstalled),
525
526
527 anyPluginEvent(PluginDependentsChangedEvent.class, uninstalled),
528 anyPluginEvent(PluginDependentsChangedEvent.class, uninstalled)
529 ));
530 }
531
532 @Test
533 public void moduleEnableDisableEventSequencing() {
534 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
535
536 final String pluginKey = "pluginKey";
537 final Plugin plugin = mockStateChangePlugin(pluginKey, pluginEventManager);
538
539 final String moduleKeyAlpha = "alpha";
540 final ModuleDescriptor moduleAlpha = mockStateChangePluginModule(pluginKey, moduleKeyAlpha, pluginEventManager);
541 final String moduleKeyBeta = "beta";
542 final ModuleDescriptor moduleBeta = mockStateChangePluginModule(pluginKey, moduleKeyBeta, pluginEventManager);
543
544 when(plugin.getModuleDescriptors()).thenReturn(ImmutableList.<ModuleDescriptor<?>>of(moduleAlpha, moduleBeta));
545 when(plugin.getModuleDescriptor(moduleKeyAlpha)).thenReturn(moduleAlpha);
546 when(plugin.getModuleDescriptor(moduleKeyBeta)).thenReturn(moduleBeta);
547
548 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
549
550 final PluginLoader pluginLoader = mockPluginLoaderForPlugins(plugin);
551
552 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
553 pluginPersistentStateStore,
554 ImmutableList.of(pluginLoader),
555 mock(ModuleDescriptorFactory.class),
556 pluginEventManager,
557 mock(PluginExceptionInterception.class)
558 );
559
560 defaultPluginManager.init();
561
562 final ArgumentCaptor<Object> disableEventsCaptor = ArgumentCaptor.forClass(Object.class);
563 doNothing().when(pluginEventManager).broadcast(disableEventsCaptor.capture());
564
565 defaultPluginManager.disablePlugin(pluginKey);
566
567 final List<Object> disableEvents = disableEventsCaptor.getAllValues();
568 final Iterable<PluginModuleEvent> pluginModuleDisableEvents = filter(disableEvents, PluginModuleEvent.class);
569 assertThat(pluginModuleDisableEvents, contains(
570 pluginModuleEvent(BeforePluginModuleDisabledEvent.class, moduleBeta),
571 pluginModuleEvent(PluginModuleDisablingEvent.class, moduleBeta),
572 pluginModuleStateChange(moduleBeta, false),
573 pluginModuleEvent(PluginModuleDisabledEvent.class, moduleBeta),
574 pluginModuleEvent(BeforePluginModuleDisabledEvent.class, moduleAlpha),
575 pluginModuleEvent(PluginModuleDisablingEvent.class, moduleAlpha),
576 pluginModuleStateChange(moduleAlpha, false),
577 pluginModuleEvent(PluginModuleDisabledEvent.class, moduleAlpha)
578 ));
579
580
581 final int pluginDisablingIndex = indexOf(disableEvents, Predicates.instanceOf(PluginDisablingEvent.class));
582 final int firstPluginModuleDisableEvent = disableEvents.indexOf(get(pluginModuleDisableEvents, 0));
583 assertThat(firstPluginModuleDisableEvent, greaterThan(pluginDisablingIndex));
584 final int lastPluginModuleDisableEventIndex = disableEvents.indexOf(getLast(pluginModuleDisableEvents));
585 final int pluginDisabledIndex = indexOf(disableEvents, Predicates.instanceOf(PluginDisabledEvent.class));
586 assertThat(lastPluginModuleDisableEventIndex, lessThan(pluginDisabledIndex));
587
588 final ArgumentCaptor<Object> enableEventsCaptor = ArgumentCaptor.forClass(Object.class);
589 doNothing().when(pluginEventManager).broadcast(enableEventsCaptor.capture());
590
591 defaultPluginManager.enablePlugins(pluginKey);
592
593 final List<Object> enableEvents = enableEventsCaptor.getAllValues();
594 final Iterable<PluginModuleEvent> pluginModuleEnableEvents = filter(enableEvents, PluginModuleEvent.class);
595 assertThat(pluginModuleEnableEvents, contains(
596 pluginModuleEvent(PluginModuleEnablingEvent.class, moduleAlpha),
597 pluginModuleStateChange(moduleAlpha, true),
598 pluginModuleEvent(PluginModuleEnabledEvent.class, moduleAlpha),
599 pluginModuleEvent(PluginModuleEnablingEvent.class, moduleBeta),
600 pluginModuleStateChange(moduleBeta, true),
601 pluginModuleEvent(PluginModuleEnabledEvent.class, moduleBeta)
602 ));
603
604
605 final int pluginEnablingIndex = indexOf(disableEvents, Predicates.instanceOf(PluginDisablingEvent.class));
606 final int firstPluginModuleEnableEvent = disableEvents.indexOf(get(pluginModuleDisableEvents, 0));
607 assertThat(firstPluginModuleEnableEvent, greaterThan(pluginEnablingIndex));
608 final int lastPluginModuleEnableEventIndex = disableEvents.indexOf(getLast(pluginModuleDisableEvents));
609 final int pluginEnabledIndex = indexOf(disableEvents, Predicates.instanceOf(PluginDisabledEvent.class));
610 assertThat(lastPluginModuleEnableEventIndex, lessThan(pluginEnabledIndex));
611 }
612
613 @Test
614 public void shutdownEventsAreSent() {
615 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
616 final List<PluginLoader> pluginLoaders = Collections.emptyList();
617 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
618 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
619
620 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
621 pluginPersistentStateStore, pluginLoaders, moduleDescriptorFactory, pluginEventManager);
622
623 defaultPluginManager.init();
624
625
626 final ArgumentCaptor<Object> shutdownEvents = ArgumentCaptor.forClass(Object.class);
627 final NotificationException notificationException = new NotificationException(new Throwable());
628 doThrow(notificationException).when(pluginEventManager).broadcast(shutdownEvents.capture());
629
630 defaultPluginManager.shutdown();
631
632
633 assertThat(shutdownEvents.getAllValues(), contains(
634 instanceOf(PluginFrameworkShuttingDownEvent.class), instanceOf(PluginFrameworkShutdownEvent.class)));
635 }
636
637 @Test
638 public void enableAndDisableEventSequencing() {
639 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
640
641 final String pluginKey = "pluginKey";
642 final Plugin plugin = mockStateChangePlugin(pluginKey, pluginEventManager);
643
644 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
645
646 final PluginLoader pluginLoader = mockPluginLoaderForPlugins(plugin);
647
648 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
649 pluginPersistentStateStore,
650 ImmutableList.of(pluginLoader),
651 mock(ModuleDescriptorFactory.class),
652 pluginEventManager,
653 mock(PluginExceptionInterception.class)
654 );
655
656 defaultPluginManager.init();
657
658 final ArgumentCaptor<Object> disableEvents = ArgumentCaptor.forClass(Object.class);
659 doNothing().when(pluginEventManager).broadcast(disableEvents.capture());
660
661 defaultPluginManager.disablePlugin(pluginKey);
662
663 assertThat(filter(disableEvents.getAllValues(), PluginEvent.class), contains(
664 pluginEvent(BeforePluginDisabledEvent.class, plugin),
665 pluginEvent(PluginDisablingEvent.class, plugin),
666 pluginStateChange(plugin, PluginState.DISABLED),
667 pluginEvent(PluginDisabledEvent.class, plugin)
668 ));
669
670 final ArgumentCaptor<Object> enableEvents = ArgumentCaptor.forClass(Object.class);
671 doNothing().when(pluginEventManager).broadcast(enableEvents.capture());
672
673 defaultPluginManager.enablePlugins(pluginKey);
674
675 assertThat(filter(enableEvents.getAllValues(), PluginEvent.class), contains(
676 pluginEvent(PluginEnablingEvent.class, plugin),
677 pluginStateChange(plugin, PluginState.ENABLED),
678 pluginEvent(PluginEnabledEvent.class, plugin)
679 ));
680 }
681
682 @Test
683 public void upgradePluginUpgradesPlugin() {
684 final String pluginKey = "pluginKey";
685 final PluginPersistentStateStore pluginPersistentStateStore = mock(
686 PluginPersistentStateStore.class, RETURNS_DEEP_STUBS);
687 when(pluginPersistentStateStore.load().getPluginRestartState(pluginKey)).thenReturn(PluginRestartState.NONE);
688
689 Plugin pluginV1 = mock(Plugin.class, RETURNS_DEEP_STUBS);
690 Plugin pluginV2 = mock(Plugin.class, RETURNS_DEEP_STUBS);
691 when(pluginV1.getKey()).thenReturn(pluginKey);
692 when(pluginV2.getKey()).thenReturn(pluginKey);
693 when(pluginV1.isDeleteable()).thenReturn(true);
694 when(pluginV1.isUninstallable()).thenReturn(true);
695
696 when(pluginV1.compareTo(pluginV2)).thenReturn(-1);
697 when(pluginV2.compareTo(pluginV1)).thenReturn(1);
698
699 DiscardablePluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
700 when(pluginLoader.supportsAddition()).thenReturn(true);
701 when(pluginLoader.supportsRemoval()).thenReturn(true);
702 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(pluginV1));
703 when(pluginLoader.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(pluginV2));
704
705 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
706 pluginPersistentStateStore,
707 Arrays.asList((PluginLoader) pluginLoader),
708 mock(ModuleDescriptorFactory.class),
709 mock(PluginEventManager.class),
710 mock(PluginExceptionInterception.class)
711 );
712 defaultPluginManager.init();
713
714 assertThat(defaultPluginManager.getPlugin(pluginKey), is(pluginV1));
715
716 final int found = defaultPluginManager.scanForNewPlugins();
717
718 assertThat(found, is(1));
719 assertThat(defaultPluginManager.getPlugin(pluginKey), is(pluginV2));
720
721 verify(pluginLoader).removePlugin(pluginV1);
722 }
723
724
725 @Test
726 public void uninstallingNotDeletableUninstallablePluginRemovesItFromLoader() {
727 final Plugin plugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
728 final PluginLoader pluginLoader = mock(PluginLoader.class);
729 final DefaultPluginManager defaultPluginManager = setupUninstallTest(false, true, plugin, pluginLoader);
730 defaultPluginManager.uninstall(plugin);
731 verify(pluginLoader).removePlugin(plugin);
732 }
733
734 @Test
735 public void uninstallingDeletableUninstallablePluginRemovesItFromLoader() {
736 final Plugin plugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
737 final PluginLoader pluginLoader = mock(PluginLoader.class);
738 final DefaultPluginManager defaultPluginManager = setupUninstallTest(true, true, plugin, pluginLoader);
739 defaultPluginManager.uninstall(plugin);
740 verify(pluginLoader).removePlugin(plugin);
741 }
742
743 @Test
744 public void uninstallingDeletableNotUninstallablePluginDoesNotRemoveItFromLoader() {
745 final Plugin plugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
746 final PluginLoader pluginLoader = mock(PluginLoader.class);
747 final DefaultPluginManager defaultPluginManager = setupUninstallTest(true, false, plugin, pluginLoader);
748
749 doThrow(new AssertionError("Unexpected PluginLoader.removePlugin call")).when(pluginLoader).removePlugin(plugin);
750 expectedException.expect(PluginException.class);
751 expectedException.expectMessage(plugin.getKey());
752 defaultPluginManager.uninstall(plugin);
753 }
754
755 @Test
756 public void uninstallingNotDeletableNotUninstallablePluginDoesNotRemoveItFromLoader() {
757 final Plugin plugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
758 final PluginLoader pluginLoader = mock(PluginLoader.class);
759 final DefaultPluginManager defaultPluginManager = setupUninstallTest(false, false, plugin, pluginLoader);
760
761 doThrow(new AssertionError("Unexpected PluginLoader.removePlugin call")).when(pluginLoader).removePlugin(plugin);
762 expectedException.expect(PluginException.class);
763 expectedException.expectMessage(plugin.getKey());
764 defaultPluginManager.uninstall(plugin);
765 }
766
767 private DefaultPluginManager setupUninstallTest(
768 final boolean isDeleteable,
769 final boolean isUninstallable,
770 final Plugin plugin,
771 final PluginLoader pluginLoader) {
772 final String pluginKey = "uninstall-test-plugin-key";
773 when(plugin.getKey()).thenReturn(pluginKey);
774 when(plugin.toString()).thenReturn(pluginKey);
775 when(plugin.isDeleteable()).thenReturn(isDeleteable);
776 when(plugin.isUninstallable()).thenReturn(isUninstallable);
777 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(Arrays.asList(plugin));
778 when(pluginLoader.supportsRemoval()).thenReturn(true);
779
780 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
781 mock(PluginPersistentStateStore.class, RETURNS_DEEP_STUBS),
782 Arrays.asList(pluginLoader),
783 mock(ModuleDescriptorFactory.class),
784 mock(PluginEventManager.class),
785 mock(PluginExceptionInterception.class)
786 );
787 defaultPluginManager.init();
788 return defaultPluginManager;
789 }
790
791 @Test
792 public void addDynamicModuleNoLoader() {
793 final PluginLoader pluginLoader = mock(PluginLoader.class);
794 final PluginInternal plugin = mock(PluginInternal.class);
795
796 manager = newDefaultPluginManager(pluginLoader);
797
798 when(plugin.toString()).thenReturn("bleh");
799
800 expectedException.expect(PluginException.class);
801 expectedException.expectMessage(containsString("bleh"));
802
803 manager.addDynamicModule(plugin, mock(Element.class));
804 }
805
806
807 @Test
808 public void removeDynamicModuleNotPresent() {
809 final PluginLoader pluginLoader = mock(PluginLoader.class);
810 final PluginInternal plugin = mock(PluginInternal.class);
811 final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class, withSettings().extraInterfaces(StateAware.class));
812
813 manager = newDefaultPluginManager(pluginLoader);
814
815 when(plugin.removeDynamicModuleDescriptor(moduleDescriptor)).thenReturn(false);
816 when(plugin.toString()).thenReturn("crapPlugin");
817 when(moduleDescriptor.getKey()).thenReturn("moduleKey");
818
819 expectedException.expect(PluginException.class);
820 expectedException.expectMessage(containsString("crapPlugin"));
821 expectedException.expectMessage(containsString("moduleKey"));
822
823 manager.removeDynamicModule(plugin, moduleDescriptor);
824
825 verify((StateAware) moduleDescriptor, never()).enabled();
826 }
827
828 @Test
829 public void removeDynamicModule() {
830 final PluginLoader pluginLoader = mock(PluginLoader.class);
831 final PluginInternal plugin = mock(PluginInternal.class);
832 final ModuleDescriptor<?> moduleDescriptor = mock(ModuleDescriptor.class, withSettings().extraInterfaces(StateAware.class));
833
834 manager = newDefaultPluginManager(pluginLoader);
835
836 when(plugin.removeDynamicModuleDescriptor(moduleDescriptor)).thenReturn(true);
837
838 manager.removeDynamicModule(plugin, moduleDescriptor);
839
840 verify(plugin).removeDynamicModuleDescriptor(moduleDescriptor);
841 verify(moduleDescriptor).destroy();
842 verify((StateAware) moduleDescriptor).disabled();
843 }
844
845 @Test
846 public void testUninstallPluginWithMultiLevelDependencies() throws PluginException, IOException {
847
848 Plugin child = mock(Plugin.class);
849 when(child.getKey()).thenReturn("child");
850 when(child.isEnabledByDefault()).thenReturn(true);
851 when(child.getPluginState()).thenReturn(PluginState.ENABLED);
852 when(child.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of("parent"), null, null));
853 when(child.compareTo(any(Plugin.class))).thenReturn(-1);
854
855 Plugin parent = mock(Plugin.class);
856 when(parent.getKey()).thenReturn("parent");
857 when(parent.isEnabledByDefault()).thenReturn(true);
858 when(parent.getPluginState()).thenReturn(PluginState.ENABLED);
859 when(parent.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of("grandparent"), null, null));
860 when(parent.compareTo(any(Plugin.class))).thenReturn(-1);
861
862 Plugin grandparent = mock(Plugin.class);
863 when(grandparent.getKey()).thenReturn("grandparent");
864 when(grandparent.isEnabledByDefault()).thenReturn(true);
865 when(grandparent.isDeleteable()).thenReturn(true);
866 when(grandparent.isUninstallable()).thenReturn(true);
867 when(grandparent.getPluginState()).thenReturn(PluginState.ENABLED);
868 when(grandparent.compareTo(any(Plugin.class))).thenReturn(-1);
869 when(grandparent.getDependencies()).thenReturn(new PluginDependencies());
870
871 PluginLoader pluginLoader = mockPluginLoaderForPlugins(child, parent, grandparent);
872 when(pluginLoader.supportsRemoval()).thenReturn(true);
873
874 manager = initNewDefaultPluginManager(pluginLoader);
875
876 manager.uninstall(grandparent);
877 verify(grandparent).enable();
878 verify(grandparent).disable();
879 verify(pluginLoader).removePlugin(grandparent);
880
881 verify(parent).enable();
882 verify(parent).disable();
883 verify(child).enable();
884 verify(child).disable();
885 }
886
887 @Test
888 public void testUninstallPluginWithDependencies() throws PluginException, IOException {
889 Plugin child = mock(Plugin.class);
890 when(child.getKey()).thenReturn("child");
891 when(child.isEnabledByDefault()).thenReturn(true);
892 when(child.getPluginState()).thenReturn(PluginState.ENABLED);
893 when(child.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of("parent"), null, null));
894 when(child.compareTo(any(Plugin.class))).thenReturn(-1);
895 Plugin parent = mock(Plugin.class);
896 when(parent.getKey()).thenReturn("parent");
897 when(parent.isEnabledByDefault()).thenReturn(true);
898 when(parent.isDeleteable()).thenReturn(true);
899 when(parent.isUninstallable()).thenReturn(true);
900 when(parent.getPluginState()).thenReturn(PluginState.ENABLED);
901 when(parent.compareTo(any(Plugin.class))).thenReturn(-1);
902 when(parent.getDependencies()).thenReturn(new PluginDependencies());
903
904 PluginLoader pluginLoader = mockPluginLoaderForPlugins(child, parent);
905 when(pluginLoader.supportsRemoval()).thenReturn(true);
906
907 manager = initNewDefaultPluginManager(pluginLoader);
908
909 manager.uninstall(parent);
910 verify(parent).enable();
911 verify(parent).disable();
912 verify(pluginLoader).removePlugin(parent);
913
914 verify(child).enable();
915 verify(child).disable();
916 }
917
918 @Test
919 public void testInstallPluginsWithTwoButOneFailsValidationWithException() {
920 DynamicPluginLoader loader = mock(DynamicPluginLoader.class);
921 when(loader.isDynamicPluginLoader()).thenReturn(true);
922
923 moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
924 PluginInstaller installer = mock(PluginInstaller.class);
925 DefaultPluginManager pm = newDefaultPluginManager(loader);
926 pm.setPluginInstaller(installer);
927 PluginArtifact artifactA = mock(PluginArtifact.class);
928 Plugin pluginA = mock(Plugin.class);
929 when(loader.canLoad(artifactA)).thenReturn("a");
930 PluginArtifact artifactB = mock(PluginArtifact.class);
931 Plugin pluginB = mock(Plugin.class);
932 doThrow(new PluginParseException()).when(loader).canLoad(artifactB);
933
934 when(loader.loadFoundPlugins(moduleDescriptorFactory)).thenReturn(Arrays.asList(pluginA, pluginB));
935
936 try {
937 manager.installPlugins(artifactA, artifactB);
938 fail("Should have not installed plugins");
939 } catch (PluginParseException ex) {
940
941 }
942
943 verify(loader).canLoad(artifactA);
944 verify(loader).canLoad(artifactB);
945 verify(installer, never()).installPlugin("a", artifactA);
946 verify(installer, never()).installPlugin("b", artifactB);
947 }
948
949 @Test
950 public void testInstallPlugin() throws Exception {
951 final PluginPersistentStateStore mockPluginStateStore = mock(PluginPersistentStateStore.class);
952 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
953 final DynamicPluginLoader mockPluginLoader = mock(DynamicPluginLoader.class);
954 when(mockPluginLoader.isDynamicPluginLoader()).thenReturn(true);
955
956 final DescriptorParser mockDescriptorParser = mock(DescriptorParser.class);
957 final PluginArtifact pluginArtifact = mock(PluginArtifact.class);
958 final PluginInstaller mockRepository = mock(PluginInstaller.class);
959 final Plugin plugin = mock(Plugin.class);
960
961
962 final DefaultPluginManager pluginManager = new DefaultPluginManager(mockPluginStateStore,
963 Collections.<PluginLoader>singletonList(mockPluginLoader), moduleDescriptorFactory, pluginEventManager);
964
965 when(mockPluginStateStore.load()).thenReturn(new DefaultPluginPersistentState());
966 when(mockPluginStateStore.load()).thenReturn(new DefaultPluginPersistentState());
967 when(mockPluginStateStore.load()).thenReturn(new DefaultPluginPersistentState());
968 when(mockDescriptorParser.getKey()).thenReturn("test");
969 when(mockPluginLoader.loadAllPlugins(moduleDescriptorFactory)).thenReturn((Iterable) Collections.emptyList());
970 when(mockPluginLoader.supportsAddition()).thenReturn(true);
971 when(mockPluginLoader.loadFoundPlugins(moduleDescriptorFactory)).thenReturn(Collections.singletonList(plugin));
972 when(mockPluginLoader.canLoad(pluginArtifact)).thenReturn("test");
973 when(plugin.getKey()).thenReturn("test");
974 when(plugin.getModuleDescriptors()).thenReturn((Collection) new ArrayList<Object>());
975 when(plugin.getModuleDescriptors()).thenReturn((Collection) new ArrayList<Object>());
976 when(plugin.isEnabledByDefault()).thenReturn(true);
977
978 when(plugin.isEnabledByDefault()).thenReturn(true);
979 when(plugin.isEnabled()).thenReturn(true);
980 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
981 when(plugin.hasAllPermissions()).thenReturn(true);
982 when(plugin.getActivePermissions()).thenReturn(ImmutableSet.of(Permissions.ALL_PERMISSIONS));
983 when(plugin.getDependencies()).thenReturn(new PluginDependencies());
984
985 pluginManager.setPluginInstaller(mockRepository);
986 pluginManager.init();
987 final PassListener enabledListener = new PassListener(PluginEnabledEvent.class);
988 pluginEventManager.register(enabledListener);
989 pluginManager.installPlugin(pluginArtifact);
990
991 assertEquals(plugin, pluginManager.getPlugin("test"));
992 assertTrue(pluginManager.isPluginEnabled("test"));
993
994 enabledListener.assertCalled();
995 }
996
997 @Test
998 public void testInstallPluginsWithOne() {
999 DynamicPluginLoader loader = mock(DynamicPluginLoader.class);
1000 when(loader.isDynamicPluginLoader()).thenReturn(true);
1001
1002 moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
1003 PluginInstaller installer = mock(PluginInstaller.class);
1004 DefaultPluginManager pm = newDefaultPluginManager(loader);
1005 pm.setPluginInstaller(installer);
1006 when(loader.loadAllPlugins(moduleDescriptorFactory)).thenReturn(Arrays.<Plugin>asList());
1007 PluginArtifact artifact = mock(PluginArtifact.class);
1008 Plugin plugin = mock(Plugin.class);
1009 when(loader.canLoad(artifact)).thenReturn("foo");
1010 when(loader.loadFoundPlugins(moduleDescriptorFactory)).thenReturn(Arrays.asList(plugin));
1011
1012 pm.init();
1013 manager.installPlugins(artifact);
1014
1015 verify(loader).canLoad(artifact);
1016 verify(installer).installPlugin("foo", artifact);
1017 }
1018
1019 @Test
1020 public void testInstallPluginsWithTwo() {
1021 DynamicPluginLoader loader = mock(DynamicPluginLoader.class);
1022 when(loader.isDynamicPluginLoader()).thenReturn(true);
1023
1024 moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
1025 PluginInstaller installer = mock(PluginInstaller.class);
1026 DefaultPluginManager pm = newDefaultPluginManager(loader);
1027 pm.setPluginInstaller(installer);
1028 when(loader.loadAllPlugins(moduleDescriptorFactory)).thenReturn(Arrays.<Plugin>asList());
1029 PluginArtifact artifactA = mock(PluginArtifact.class);
1030 Plugin pluginA = mock(Plugin.class);
1031 when(loader.canLoad(artifactA)).thenReturn("a");
1032 PluginArtifact artifactB = mock(PluginArtifact.class);
1033 Plugin pluginB = mock(Plugin.class);
1034 when(loader.canLoad(artifactB)).thenReturn("b");
1035
1036 when(loader.loadFoundPlugins(moduleDescriptorFactory)).thenReturn(Arrays.asList(pluginA, pluginB));
1037
1038 pm.init();
1039 manager.installPlugins(artifactA, artifactB);
1040
1041 verify(loader).canLoad(artifactA);
1042 verify(loader).canLoad(artifactB);
1043 verify(installer).installPlugin("a", artifactA);
1044 verify(installer).installPlugin("b", artifactB);
1045 }
1046
1047 @Test
1048 public void testInstallPluginsWithTwoButOneFailsValidation() {
1049 DynamicPluginLoader loader = mock(DynamicPluginLoader.class);
1050 when(loader.isDynamicPluginLoader()).thenReturn(true);
1051
1052 ModuleDescriptorFactory descriptorFactory = mock(ModuleDescriptorFactory.class);
1053 PluginEventManager eventManager = mock(PluginEventManager.class);
1054 PluginInstaller installer = mock(PluginInstaller.class);
1055 DefaultPluginManager pm = newDefaultPluginManager(loader);
1056 pm.setPluginInstaller(installer);
1057 PluginArtifact artifactA = mock(PluginArtifact.class);
1058 Plugin pluginA = mock(Plugin.class);
1059 when(loader.canLoad(artifactA)).thenReturn("a");
1060 PluginArtifact artifactB = mock(PluginArtifact.class);
1061 Plugin pluginB = mock(Plugin.class);
1062 when(loader.canLoad(artifactB)).thenReturn(null);
1063
1064 when(loader.loadFoundPlugins(descriptorFactory)).thenReturn(Arrays.asList(pluginA, pluginB));
1065
1066 try {
1067 manager.installPlugins(artifactA, artifactB);
1068 fail("Should have not installed plugins");
1069 } catch (PluginParseException ex) {
1070
1071 }
1072
1073 verify(loader).canLoad(artifactA);
1074 verify(loader).canLoad(artifactB);
1075 verify(installer, never()).installPlugin("a", artifactA);
1076 verify(installer, never()).installPlugin("b", artifactB);
1077 }
1078
1079 @Test
1080 public void testCircularDependencyWouldNotCauseInfiniteLoop() throws PluginException, IOException {
1081
1082 Plugin p1 = mock(Plugin.class);
1083 when(p1.getKey()).thenReturn("p1");
1084 when(p1.isEnabledByDefault()).thenReturn(true);
1085 when(p1.getPluginState()).thenReturn(PluginState.ENABLED);
1086 when(p1.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of("p2", "parent"), null, null));
1087 when(p1.compareTo(any(Plugin.class))).thenReturn(-1);
1088
1089
1090 Plugin p2 = mock(Plugin.class);
1091 when(p2.getKey()).thenReturn("p2");
1092 when(p2.isEnabledByDefault()).thenReturn(true);
1093 when(p2.getPluginState()).thenReturn(PluginState.ENABLED);
1094 when(p2.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of("p1"), null, null));
1095 when(p2.compareTo(any(Plugin.class))).thenReturn(-1);
1096
1097 Plugin parent = mock(Plugin.class);
1098 when(parent.getKey()).thenReturn("parent");
1099 when(parent.isEnabledByDefault()).thenReturn(true);
1100 when(parent.isDeleteable()).thenReturn(true);
1101 when(parent.isUninstallable()).thenReturn(true);
1102 when(parent.getPluginState()).thenReturn(PluginState.ENABLED);
1103 when(parent.compareTo(any(Plugin.class))).thenReturn(-1);
1104 when(parent.getDependencies()).thenReturn(new PluginDependencies());
1105
1106 PluginLoader pluginLoader = mockPluginLoaderForPlugins(p1, p2, parent);
1107 when(pluginLoader.supportsRemoval()).thenReturn(true);
1108
1109 manager = initNewDefaultPluginManager(pluginLoader);
1110
1111 manager.uninstall(parent);
1112 verify(parent, times(1)).enable();
1113 verify(parent, times(1)).disable();
1114 verify(pluginLoader).removePlugin(parent);
1115
1116 verify(p1, times(1)).enable();
1117 verify(p1, times(1)).disable();
1118 verify(p2, times(1)).enable();
1119 verify(p2, times(1)).disable();
1120 }
1121
1122 @Test
1123 public void testThreeCycleDependencyWouldNotCauseInfiniteLoop() throws PluginException, IOException {
1124 Plugin p1 = mock(Plugin.class);
1125 when(p1.getKey()).thenReturn("p1");
1126 when(p1.isEnabledByDefault()).thenReturn(true);
1127 when(p1.getPluginState()).thenReturn(PluginState.ENABLED);
1128 when(p1.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of("p2", "parent"), null, null));
1129 when(p1.compareTo(any(Plugin.class))).thenReturn(-1);
1130
1131
1132 Plugin p2 = mock(Plugin.class);
1133 when(p2.getKey()).thenReturn("p2");
1134 when(p2.isEnabledByDefault()).thenReturn(true);
1135 when(p2.getPluginState()).thenReturn(PluginState.ENABLED);
1136 when(p2.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of("p3"), null, null));
1137 when(p2.compareTo(any(Plugin.class))).thenReturn(-1);
1138
1139 Plugin p3 = mock(Plugin.class);
1140 when(p3.getKey()).thenReturn("p3");
1141 when(p3.isEnabledByDefault()).thenReturn(true);
1142 when(p3.getPluginState()).thenReturn(PluginState.ENABLED);
1143 when(p3.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of("p1"), null, null));
1144 when(p3.compareTo(any(Plugin.class))).thenReturn(-1);
1145
1146 Plugin parent = mock(Plugin.class);
1147 when(parent.getKey()).thenReturn("parent");
1148 when(parent.isEnabledByDefault()).thenReturn(true);
1149 when(parent.isDeleteable()).thenReturn(true);
1150 when(parent.isUninstallable()).thenReturn(true);
1151 when(parent.getPluginState()).thenReturn(PluginState.ENABLED);
1152 when(parent.compareTo(any(Plugin.class))).thenReturn(-1);
1153 when(parent.getDependencies()).thenReturn(new PluginDependencies());
1154
1155 PluginLoader pluginLoader = mockPluginLoaderForPlugins(p1, p2, p3, parent);
1156 when(pluginLoader.supportsRemoval()).thenReturn(true);
1157
1158 manager = initNewDefaultPluginManager(pluginLoader);
1159
1160 manager.uninstall(parent);
1161 verify(parent, times(1)).enable();
1162 verify(parent, times(1)).disable();
1163 verify(pluginLoader).removePlugin(parent);
1164
1165 verify(p1, times(1)).enable();
1166 verify(p1, times(1)).disable();
1167 verify(p2, times(1)).enable();
1168 verify(p2, times(1)).disable();
1169 verify(p3, times(1)).enable();
1170 verify(p3, times(1)).disable();
1171 }
1172
1173 @Test
1174 public void addDynamicModule() {
1175 final PluginLoader pluginLoader = mock(PluginLoader.class);
1176 final PluginInternal plugin = mock(PluginInternal.class);
1177 final Element module = mock(Element.class);
1178 final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class, withSettings().extraInterfaces(StateAware.class));
1179
1180 newDefaultPluginManager(pluginLoader)
1181 .addPlugins(pluginLoader, ImmutableList.<Plugin>of(plugin));
1182
1183 when(pluginLoader.createModule(plugin, module, moduleDescriptorFactory)).thenReturn(moduleDescriptor);
1184 when(moduleDescriptor.isEnabledByDefault()).thenReturn(true);
1185 when(plugin.addDynamicModuleDescriptor(moduleDescriptor)).thenReturn(true);
1186 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
1187
1188 assertThat(manager.addDynamicModule(plugin, module), is(moduleDescriptor));
1189
1190 verify((StateAware) moduleDescriptor).enabled();
1191 }
1192
1193 @Test
1194 public void addDynamicModulePluginDisabled() {
1195 final PluginLoader pluginLoader = mock(PluginLoader.class);
1196 final PluginInternal plugin = mock(PluginInternal.class);
1197 final Element module = mock(Element.class);
1198 final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class, withSettings().extraInterfaces(StateAware.class));
1199
1200 newDefaultPluginManager(pluginLoader)
1201 .addPlugins(pluginLoader, ImmutableList.<Plugin>of(plugin));
1202
1203 when(pluginLoader.createModule(plugin, module, moduleDescriptorFactory)).thenReturn(moduleDescriptor);
1204 when(moduleDescriptor.isEnabledByDefault()).thenReturn(true);
1205 when(plugin.addDynamicModuleDescriptor(moduleDescriptor)).thenReturn(true);
1206 when(plugin.getPluginState()).thenReturn(PluginState.DISABLED);
1207
1208 manager.addDynamicModule(plugin, module);
1209
1210 verify((StateAware) moduleDescriptor, never()).enabled();
1211 }
1212
1213 @Test
1214 public void addDynamicModuleModuleNotEnabledByDefault() {
1215 final PluginLoader pluginLoader = mock(PluginLoader.class);
1216 final PluginInternal plugin = mock(PluginInternal.class);
1217 final Element module = mock(Element.class);
1218 final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class, withSettings().extraInterfaces(StateAware.class));
1219
1220 newDefaultPluginManager(pluginLoader)
1221 .addPlugins(pluginLoader, ImmutableList.<Plugin>of(plugin));
1222
1223 when(pluginLoader.createModule(plugin, module, moduleDescriptorFactory)).thenReturn(moduleDescriptor);
1224 when(moduleDescriptor.isEnabledByDefault()).thenReturn(false);
1225 when(plugin.addDynamicModuleDescriptor(moduleDescriptor)).thenReturn(true);
1226 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
1227
1228 manager.addDynamicModule(plugin, module);
1229
1230 verify((StateAware) moduleDescriptor, never()).enabled();
1231 }
1232
1233 @Test
1234 public void addDynamicModuleDuplicateKey() {
1235 final PluginLoader pluginLoader = mock(PluginLoader.class);
1236 final PluginInternal plugin = mock(PluginInternal.class);
1237 final Element module = mock(Element.class);
1238 final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class, withSettings().extraInterfaces(StateAware.class));
1239 final ModuleDescriptor existingModuleDescriptor = mock(ModuleDescriptor.class);
1240
1241 newDefaultPluginManager(pluginLoader)
1242 .addPlugins(pluginLoader, ImmutableList.<Plugin>of(plugin));
1243
1244 when(pluginLoader.createModule(plugin, module, moduleDescriptorFactory)).thenReturn(moduleDescriptor);
1245 when(plugin.toString()).thenReturn("awesomePlugin");
1246 when(plugin.getModuleDescriptor("moduleKey")).thenReturn(existingModuleDescriptor);
1247 when(moduleDescriptor.getKey()).thenReturn("moduleKey");
1248
1249 expectedException.expect(PluginException.class);
1250 expectedException.expectMessage(containsString("awesomePlugin"));
1251 expectedException.expectMessage(containsString("moduleKey"));
1252
1253 manager.addDynamicModule(plugin, module);
1254
1255 verify((StateAware) moduleDescriptor, never()).enabled();
1256 }
1257
1258 @Test
1259 public void addDynamicModuleAlreadyPresentAsDynamicModule() {
1260 final PluginLoader pluginLoader = mock(PluginLoader.class);
1261 final PluginInternal plugin = mock(PluginInternal.class);
1262 final Element module = mock(Element.class);
1263 final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class, withSettings().extraInterfaces(StateAware.class));
1264
1265 newDefaultPluginManager(pluginLoader)
1266 .addPlugins(pluginLoader, ImmutableList.<Plugin>of(plugin));
1267
1268 when(pluginLoader.createModule(plugin, module, moduleDescriptorFactory)).thenReturn(moduleDescriptor);
1269 when(plugin.addDynamicModuleDescriptor(moduleDescriptor)).thenReturn(false);
1270 when(plugin.toString()).thenReturn("lawsonPlugin");
1271 when(moduleDescriptor.getKey()).thenReturn("moduleKey");
1272
1273 expectedException.expect(PluginException.class);
1274 expectedException.expectMessage(containsString("lawsonPlugin"));
1275 expectedException.expectMessage(containsString("moduleKey"));
1276
1277 manager.addDynamicModule(plugin, module);
1278
1279 verify((StateAware) moduleDescriptor, never()).enabled();
1280 }
1281
1282 private static class CannotEnablePlugin extends StaticPlugin {
1283 public CannotEnablePlugin() {
1284 setKey("foo");
1285 }
1286
1287 @Override
1288 protected PluginState enableInternal() {
1289 throw new RuntimeException("boo");
1290 }
1291
1292 public void disabled() {
1293 }
1294 }
1295
1296 @Test
1297 public void testAddPluginsThatThrowExceptionOnEnabled() throws Exception {
1298 final Plugin plugin = new CannotEnablePlugin();
1299
1300 newDefaultPluginManager()
1301 .addPlugins(null, Arrays.asList(plugin));
1302
1303 assertFalse(plugin.getPluginState() == PluginState.ENABLED);
1304 }
1305 }