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.PluginDependentsChangedEvent;
21 import com.atlassian.plugin.event.events.PluginDisabledEvent;
22 import com.atlassian.plugin.event.events.PluginDisablingEvent;
23 import com.atlassian.plugin.event.events.PluginEnabledEvent;
24 import com.atlassian.plugin.event.events.PluginEnablingEvent;
25 import com.atlassian.plugin.event.events.PluginEvent;
26 import com.atlassian.plugin.event.events.PluginFrameworkShutdownEvent;
27 import com.atlassian.plugin.event.events.PluginFrameworkShuttingDownEvent;
28 import com.atlassian.plugin.event.events.PluginInstalledEvent;
29 import com.atlassian.plugin.event.events.PluginInstallingEvent;
30 import com.atlassian.plugin.event.events.PluginModuleDisabledEvent;
31 import com.atlassian.plugin.event.events.PluginModuleDisablingEvent;
32 import com.atlassian.plugin.event.events.PluginModuleEnabledEvent;
33 import com.atlassian.plugin.event.events.PluginModuleEnablingEvent;
34 import com.atlassian.plugin.event.events.PluginModuleEvent;
35 import com.atlassian.plugin.event.events.PluginUninstalledEvent;
36 import com.atlassian.plugin.event.events.PluginUninstallingEvent;
37 import com.atlassian.plugin.event.events.PluginUpgradedEvent;
38 import com.atlassian.plugin.event.events.PluginUpgradingEvent;
39 import com.atlassian.plugin.event.impl.DefaultPluginEventManager;
40 import com.atlassian.plugin.event.listeners.PassListener;
41 import com.atlassian.plugin.exception.PluginExceptionInterception;
42 import com.atlassian.plugin.hostcontainer.DefaultHostContainer;
43 import com.atlassian.plugin.impl.StaticPlugin;
44 import com.atlassian.plugin.loaders.DiscardablePluginLoader;
45 import com.atlassian.plugin.loaders.DynamicPluginLoader;
46 import com.atlassian.plugin.loaders.PluginLoader;
47 import com.atlassian.plugin.manager.store.MemoryPluginPersistentStateStore;
48 import com.atlassian.plugin.parsers.DescriptorParser;
49 import com.google.common.base.Predicates;
50 import com.google.common.collect.ImmutableList;
51 import com.google.common.collect.ImmutableSet;
52 import org.dom4j.Element;
53 import org.junit.Rule;
54 import org.junit.Test;
55 import org.junit.rules.ExpectedException;
56 import org.mockito.ArgumentCaptor;
57
58 import java.io.IOException;
59 import java.util.ArrayList;
60 import java.util.Arrays;
61 import java.util.Collection;
62 import java.util.Collections;
63 import java.util.List;
64 import java.util.stream.Collectors;
65
66 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.anyPluginEvent;
67 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.anyPluginStateChange;
68 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.mockPluginLoaderForPlugins;
69 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.mockPluginPersistentStateStore;
70 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.mockPluginsSortOrder;
71 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.mockStateChangePlugin;
72 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.mockStateChangePluginModule;
73 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.pluginEvent;
74 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.pluginModuleEvent;
75 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.pluginModuleStateChange;
76 import static com.atlassian.plugin.manager.DefaultPluginManagerMocks.pluginStateChange;
77 import static com.google.common.collect.ImmutableList.copyOf;
78 import static com.google.common.collect.Iterables.filter;
79 import static com.google.common.collect.Iterables.get;
80 import static com.google.common.collect.Iterables.getLast;
81 import static com.google.common.collect.Iterables.indexOf;
82 import static java.util.Collections.emptyList;
83 import static java.util.Collections.singletonList;
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.assertNotSame;
95 import static org.junit.Assert.assertTrue;
96 import static org.junit.Assert.fail;
97 import static org.mockito.ArgumentMatchers.any;
98 import static org.mockito.ArgumentMatchers.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 final 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 = newPluginMock();
149 Plugin pluginV2 = newPluginMock();
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 singletonList(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 = newPluginMock();
177 Plugin pluginV2 = newPluginMock();
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(singletonList(pluginV2));
184 when(pluginLoader.supportsAddition()).thenReturn(true);
185 when(pluginLoader.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(singletonList(pluginV1));
186
187 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
188 pluginPersistentStateStore,
189 singletonList(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(singletonList(pluginV2));
223
224 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
225 pluginPersistentStateStore,
226 singletonList(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 = newPluginMock();
264 when(pluginAlpha.getKey()).thenReturn("alpha");
265 when(pluginLoaderAlpha.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(singletonList(pluginAlpha));
266 final Plugin pluginBeta = newPluginMock();
267 when(pluginBeta.getKey()).thenReturn("beta");
268 when(pluginLoaderBeta.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(singletonList(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(singletonList(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(PluginDisablingEvent.class, dependentPlugin),
370 pluginStateChange(dependentPlugin, PluginState.DISABLED),
371 pluginEvent(PluginDisabledEvent.class, dependentPlugin),
372 pluginEvent(PluginUpgradingEvent.class, pluginV1),
373 pluginEvent(PluginDisablingEvent.class, pluginV1),
374 pluginStateChange(pluginV1, PluginState.DISABLED),
375 pluginEvent(PluginDisabledEvent.class, pluginV1),
376 pluginStateChange(pluginV2, PluginState.INSTALLED),
377 pluginEvent(PluginUpgradedEvent.class, pluginV2),
378 pluginEvent(PluginEnablingEvent.class, pluginV2),
379 pluginEvent(PluginEnablingEvent.class, dependentPlugin),
380 pluginStateChange(pluginV2, PluginState.ENABLED),
381 pluginStateChange(dependentPlugin, PluginState.ENABLED),
382 pluginEvent(PluginEnabledEvent.class, pluginV2),
383 pluginEvent(PluginEnabledEvent.class, dependentPlugin),
384 pluginEvent(PluginDependentsChangedEvent.class, pluginV2)
385 ));
386 }
387
388 @Test
389 public void uninstallEventSequencing() {
390 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
391
392 final String pluginKey = "pluginKey";
393 final Plugin plugin = mockStateChangePlugin(pluginKey, pluginEventManager);
394
395 final Plugin dependentPlugin = mockStateChangePlugin("dependentPluginKey", pluginEventManager);
396 when(dependentPlugin.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of(pluginKey), null, null));
397
398 mockPluginsSortOrder(dependentPlugin, plugin);
399
400 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
401
402 final PluginLoader pluginLoader = mockPluginLoaderForPlugins(plugin, dependentPlugin);
403 when(pluginLoader.supportsRemoval()).thenReturn(true);
404
405 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
406 pluginPersistentStateStore,
407 ImmutableList.of(pluginLoader),
408 mock(ModuleDescriptorFactory.class),
409 pluginEventManager,
410 mock(PluginExceptionInterception.class)
411 );
412
413 defaultPluginManager.init();
414
415 final ArgumentCaptor<Object> events = ArgumentCaptor.forClass(Object.class);
416 doNothing().when(pluginEventManager).broadcast(events.capture());
417
418 defaultPluginManager.uninstall(defaultPluginManager.getPlugin(pluginKey));
419
420 assertThat(filter(events.getAllValues(), PluginEvent.class), contains(
421 pluginEvent(PluginDisablingEvent.class, dependentPlugin),
422 pluginStateChange(dependentPlugin, PluginState.DISABLED),
423 pluginEvent(PluginDisabledEvent.class, dependentPlugin),
424 pluginEvent(PluginUninstallingEvent.class, plugin),
425 pluginEvent(PluginDisablingEvent.class, plugin),
426 pluginStateChange(plugin, PluginState.DISABLED),
427 pluginEvent(PluginDisabledEvent.class, plugin),
428 pluginEvent(PluginUninstalledEvent.class, plugin),
429 pluginEvent(PluginDependentsChangedEvent.class, plugin)
430 ));
431 }
432
433 @Test
434 public void uninstallPluginsEventSequencing() {
435 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
436
437 final String pluginKey = "pluginKey";
438 final Plugin plugin = mockStateChangePlugin(pluginKey, pluginEventManager);
439
440 final Plugin dependentPlugin = mockStateChangePlugin("dependentPluginKey", pluginEventManager);
441 when(dependentPlugin.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of(pluginKey), null, null));
442
443 final String dependent2PluginKey = "dependent2PluginKey";
444 final Plugin dependent2Plugin = mockStateChangePlugin(dependent2PluginKey, pluginEventManager);
445 when(dependent2Plugin.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of(pluginKey), null, null));
446
447 mockPluginsSortOrder(dependentPlugin, dependent2Plugin, plugin);
448
449 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
450
451 final PluginLoader pluginLoader = mockPluginLoaderForPlugins(plugin, dependentPlugin, dependent2Plugin);
452 when(pluginLoader.supportsRemoval()).thenReturn(true);
453
454 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
455 pluginPersistentStateStore,
456 ImmutableList.of(pluginLoader),
457 mock(ModuleDescriptorFactory.class),
458 pluginEventManager,
459 mock(PluginExceptionInterception.class)
460 );
461
462 defaultPluginManager.init();
463
464 final ArgumentCaptor<Object> events = ArgumentCaptor.forClass(Object.class);
465 doNothing().when(pluginEventManager).broadcast(events.capture());
466
467 Plugin[] uninstalled = new Plugin[]{plugin, dependent2Plugin};
468
469 defaultPluginManager.uninstallPlugins(Arrays.asList(uninstalled));
470
471
472 List<PluginEvent> pluginEvents = events.getAllValues().stream()
473 .filter(PluginEvent.class::isInstance)
474 .map(PluginEvent.class::cast)
475 .collect(Collectors.toList());
476
477
478 Arrays.stream(uninstalled)
479 .forEach(p -> assertThat(
480 pluginEvents.stream()
481 .filter(e -> e.getPlugin() == p)
482 .collect(Collectors.toList()),
483 contains(
484 pluginEvent(PluginUninstallingEvent.class, p),
485 pluginEvent(PluginDisablingEvent.class, p),
486 pluginStateChange(p, PluginState.DISABLED),
487 pluginEvent(PluginDisabledEvent.class, p),
488 pluginEvent(PluginUninstalledEvent.class, p),
489 pluginEvent(PluginDependentsChangedEvent.class, p)
490 )));
491
492
493 assertThat(pluginEvents,
494 contains(
495
496 pluginEvent(PluginDisablingEvent.class, dependentPlugin),
497 pluginStateChange(dependentPlugin, PluginState.DISABLED),
498 pluginEvent(PluginDisabledEvent.class, dependentPlugin),
499
500
501 anyPluginEvent(PluginUninstallingEvent.class, uninstalled),
502 anyPluginEvent(PluginUninstallingEvent.class, uninstalled),
503
504
505 anyPluginEvent(PluginDisablingEvent.class, uninstalled),
506 anyPluginStateChange(PluginState.DISABLED, uninstalled),
507 anyPluginEvent(PluginDisabledEvent.class, uninstalled),
508
509
510 anyPluginEvent(PluginDisablingEvent.class, uninstalled),
511 anyPluginStateChange(PluginState.DISABLED, uninstalled),
512 anyPluginEvent(PluginDisabledEvent.class, uninstalled),
513
514
515 anyPluginEvent(PluginUninstalledEvent.class, uninstalled),
516 anyPluginEvent(PluginUninstalledEvent.class, uninstalled),
517
518
519 anyPluginEvent(PluginDependentsChangedEvent.class, uninstalled),
520 anyPluginEvent(PluginDependentsChangedEvent.class, uninstalled)
521 ));
522 }
523
524 @Test
525 public void moduleEnableDisableEventSequencing() {
526 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
527
528 final String pluginKey = "pluginKey";
529 final Plugin plugin = mockStateChangePlugin(pluginKey, pluginEventManager);
530
531 final String moduleKeyAlpha = "alpha";
532 final ModuleDescriptor moduleAlpha = mockStateChangePluginModule(pluginKey, moduleKeyAlpha, pluginEventManager);
533 final String moduleKeyBeta = "beta";
534 final ModuleDescriptor moduleBeta = mockStateChangePluginModule(pluginKey, moduleKeyBeta, pluginEventManager);
535
536 when(plugin.getModuleDescriptors()).thenReturn(ImmutableList.of(moduleAlpha, moduleBeta));
537 when(plugin.getModuleDescriptor(moduleKeyAlpha)).thenReturn(moduleAlpha);
538 when(plugin.getModuleDescriptor(moduleKeyBeta)).thenReturn(moduleBeta);
539
540 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
541
542 final PluginLoader pluginLoader = mockPluginLoaderForPlugins(plugin);
543
544 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
545 pluginPersistentStateStore,
546 ImmutableList.of(pluginLoader),
547 mock(ModuleDescriptorFactory.class),
548 pluginEventManager,
549 mock(PluginExceptionInterception.class)
550 );
551
552 defaultPluginManager.init();
553
554 final ArgumentCaptor<Object> disableEventsCaptor = ArgumentCaptor.forClass(Object.class);
555 doNothing().when(pluginEventManager).broadcast(disableEventsCaptor.capture());
556
557 defaultPluginManager.disablePlugin(pluginKey);
558
559 final List<Object> disableEvents = disableEventsCaptor.getAllValues();
560 final Iterable<PluginModuleEvent> pluginModuleDisableEvents = filter(disableEvents, PluginModuleEvent.class);
561 assertThat(pluginModuleDisableEvents, contains(
562 pluginModuleEvent(PluginModuleDisablingEvent.class, moduleBeta),
563 pluginModuleStateChange(moduleBeta, false),
564 pluginModuleEvent(PluginModuleDisabledEvent.class, moduleBeta),
565 pluginModuleEvent(PluginModuleDisablingEvent.class, moduleAlpha),
566 pluginModuleStateChange(moduleAlpha, false),
567 pluginModuleEvent(PluginModuleDisabledEvent.class, moduleAlpha)
568 ));
569
570
571 final int pluginDisablingIndex = indexOf(disableEvents, Predicates.instanceOf(PluginDisablingEvent.class));
572 final int firstPluginModuleDisableEvent = disableEvents.indexOf(get(pluginModuleDisableEvents, 0));
573 assertThat(firstPluginModuleDisableEvent, greaterThan(pluginDisablingIndex));
574 final int lastPluginModuleDisableEventIndex = disableEvents.indexOf(getLast(pluginModuleDisableEvents));
575 final int pluginDisabledIndex = indexOf(disableEvents, Predicates.instanceOf(PluginDisabledEvent.class));
576 assertThat(lastPluginModuleDisableEventIndex, lessThan(pluginDisabledIndex));
577
578 final ArgumentCaptor<Object> enableEventsCaptor = ArgumentCaptor.forClass(Object.class);
579 doNothing().when(pluginEventManager).broadcast(enableEventsCaptor.capture());
580
581 defaultPluginManager.enablePlugins(pluginKey);
582
583 final List<Object> enableEvents = enableEventsCaptor.getAllValues();
584 final Iterable<PluginModuleEvent> pluginModuleEnableEvents = filter(enableEvents, PluginModuleEvent.class);
585 assertThat(pluginModuleEnableEvents, contains(
586 pluginModuleEvent(PluginModuleEnablingEvent.class, moduleAlpha),
587 pluginModuleStateChange(moduleAlpha, true),
588 pluginModuleEvent(PluginModuleEnabledEvent.class, moduleAlpha),
589 pluginModuleEvent(PluginModuleEnablingEvent.class, moduleBeta),
590 pluginModuleStateChange(moduleBeta, true),
591 pluginModuleEvent(PluginModuleEnabledEvent.class, moduleBeta)
592 ));
593
594
595 final int pluginEnablingIndex = indexOf(disableEvents, Predicates.instanceOf(PluginDisablingEvent.class));
596 final int firstPluginModuleEnableEvent = disableEvents.indexOf(get(pluginModuleDisableEvents, 0));
597 assertThat(firstPluginModuleEnableEvent, greaterThan(pluginEnablingIndex));
598 final int lastPluginModuleEnableEventIndex = disableEvents.indexOf(getLast(pluginModuleDisableEvents));
599 final int pluginEnabledIndex = indexOf(disableEvents, Predicates.instanceOf(PluginDisabledEvent.class));
600 assertThat(lastPluginModuleEnableEventIndex, lessThan(pluginEnabledIndex));
601 }
602
603 @Test
604 public void shutdownEventsAreSent() {
605 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
606 final List<PluginLoader> pluginLoaders = emptyList();
607 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
608 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
609
610 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
611 pluginPersistentStateStore, pluginLoaders, moduleDescriptorFactory, pluginEventManager);
612
613 defaultPluginManager.init();
614
615
616 final ArgumentCaptor<Object> shutdownEvents = ArgumentCaptor.forClass(Object.class);
617 final NotificationException notificationException = new NotificationException(new Throwable());
618 doThrow(notificationException).when(pluginEventManager).broadcast(shutdownEvents.capture());
619
620 defaultPluginManager.shutdown();
621
622
623 assertThat(shutdownEvents.getAllValues(), contains(
624 instanceOf(PluginFrameworkShuttingDownEvent.class), instanceOf(PluginFrameworkShutdownEvent.class)));
625 }
626
627 @Test
628 public void enableAndDisableEventSequencing() {
629 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
630
631 final String pluginKey = "pluginKey";
632 final Plugin plugin = mockStateChangePlugin(pluginKey, pluginEventManager);
633
634 final PluginPersistentStateStore pluginPersistentStateStore = mockPluginPersistentStateStore();
635
636 final PluginLoader pluginLoader = mockPluginLoaderForPlugins(plugin);
637
638 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
639 pluginPersistentStateStore,
640 ImmutableList.of(pluginLoader),
641 mock(ModuleDescriptorFactory.class),
642 pluginEventManager,
643 mock(PluginExceptionInterception.class)
644 );
645
646 defaultPluginManager.init();
647
648 final ArgumentCaptor<Object> disableEvents = ArgumentCaptor.forClass(Object.class);
649 doNothing().when(pluginEventManager).broadcast(disableEvents.capture());
650
651 defaultPluginManager.disablePlugin(pluginKey);
652
653 assertThat(filter(disableEvents.getAllValues(), PluginEvent.class), contains(
654 pluginEvent(PluginDisablingEvent.class, plugin),
655 pluginStateChange(plugin, PluginState.DISABLED),
656 pluginEvent(PluginDisabledEvent.class, plugin)
657 ));
658
659 final ArgumentCaptor<Object> enableEvents = ArgumentCaptor.forClass(Object.class);
660 doNothing().when(pluginEventManager).broadcast(enableEvents.capture());
661
662 defaultPluginManager.enablePlugins(pluginKey);
663
664 assertThat(filter(enableEvents.getAllValues(), PluginEvent.class), contains(
665 pluginEvent(PluginEnablingEvent.class, plugin),
666 pluginStateChange(plugin, PluginState.ENABLED),
667 pluginEvent(PluginEnabledEvent.class, plugin)
668 ));
669 }
670
671 @Test
672 public void upgradePluginUpgradesPlugin() {
673 final String pluginKey = "pluginKey";
674 final PluginPersistentStateStore pluginPersistentStateStore = mock(
675 PluginPersistentStateStore.class, RETURNS_DEEP_STUBS);
676 when(pluginPersistentStateStore.load().getPluginRestartState(pluginKey)).thenReturn(PluginRestartState.NONE);
677
678 Plugin pluginV1 = newPluginMock();
679 Plugin pluginV2 = newPluginMock();
680 when(pluginV1.getKey()).thenReturn(pluginKey);
681 when(pluginV2.getKey()).thenReturn(pluginKey);
682 when(pluginV1.isDeleteable()).thenReturn(true);
683 when(pluginV1.isUninstallable()).thenReturn(true);
684
685 when(pluginV1.compareTo(pluginV2)).thenReturn(-1);
686 when(pluginV2.compareTo(pluginV1)).thenReturn(1);
687
688 DiscardablePluginLoader pluginLoader = mock(DiscardablePluginLoader.class);
689 when(pluginLoader.supportsAddition()).thenReturn(true);
690 when(pluginLoader.supportsRemoval()).thenReturn(true);
691 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(singletonList(pluginV1));
692 when(pluginLoader.loadFoundPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(singletonList(pluginV2));
693
694 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
695 pluginPersistentStateStore,
696 singletonList(pluginLoader),
697 mock(ModuleDescriptorFactory.class),
698 mock(PluginEventManager.class),
699 mock(PluginExceptionInterception.class)
700 );
701 defaultPluginManager.init();
702
703 assertThat(defaultPluginManager.getPlugin(pluginKey), is(pluginV1));
704
705 final int found = defaultPluginManager.scanForNewPlugins();
706
707 assertThat(found, is(1));
708 assertThat(defaultPluginManager.getPlugin(pluginKey), is(pluginV2));
709
710 verify(pluginLoader).removePlugin(pluginV1);
711 }
712
713 @Test
714 public void uninstallingNotDeletableUninstallablePluginRemovesItFromLoader() {
715 final Plugin plugin = newPluginMock();
716 final PluginLoader pluginLoader = mock(PluginLoader.class);
717 final DefaultPluginManager defaultPluginManager = setupUninstallTest(false, true, plugin, pluginLoader);
718 defaultPluginManager.uninstall(plugin);
719 verify(pluginLoader).removePlugin(plugin);
720 }
721
722 @Test
723 public void uninstallingDeletableUninstallablePluginRemovesItFromLoader() {
724 final Plugin plugin = newPluginMock();
725 final PluginLoader pluginLoader = mock(PluginLoader.class);
726 final DefaultPluginManager defaultPluginManager = setupUninstallTest(true, true, plugin, pluginLoader);
727 defaultPluginManager.uninstall(plugin);
728 verify(pluginLoader).removePlugin(plugin);
729 }
730
731 @Test
732 public void uninstallingDeletableNotUninstallablePluginDoesNotRemoveItFromLoader() {
733 final Plugin plugin = newPluginMock();
734 final PluginLoader pluginLoader = mock(PluginLoader.class);
735 final DefaultPluginManager defaultPluginManager = setupUninstallTest(true, false, plugin, pluginLoader);
736
737 doThrow(new AssertionError("Unexpected PluginLoader.removePlugin call")).when(pluginLoader).removePlugin(plugin);
738 expectedException.expect(PluginException.class);
739 expectedException.expectMessage(plugin.getKey());
740 defaultPluginManager.uninstall(plugin);
741 }
742
743 @Test
744 public void uninstallingNotDeletableNotUninstallablePluginDoesNotRemoveItFromLoader() {
745 final Plugin plugin = newPluginMock();
746 final PluginLoader pluginLoader = mock(PluginLoader.class);
747 final DefaultPluginManager defaultPluginManager = setupUninstallTest(false, 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 private DefaultPluginManager setupUninstallTest(
756 final boolean isDeleteable,
757 final boolean isUninstallable,
758 final Plugin plugin,
759 final PluginLoader pluginLoader) {
760 final String pluginKey = "uninstall-test-plugin-key";
761 when(plugin.getKey()).thenReturn(pluginKey);
762 when(plugin.toString()).thenReturn(pluginKey);
763 when(plugin.isDeleteable()).thenReturn(isDeleteable);
764 when(plugin.isUninstallable()).thenReturn(isUninstallable);
765 when(pluginLoader.loadAllPlugins(isA(ModuleDescriptorFactory.class))).thenReturn(singletonList(plugin));
766 when(pluginLoader.supportsRemoval()).thenReturn(true);
767
768 final DefaultPluginManager defaultPluginManager = new DefaultPluginManager(
769 mock(PluginPersistentStateStore.class, RETURNS_DEEP_STUBS),
770 singletonList(pluginLoader),
771 mock(ModuleDescriptorFactory.class),
772 mock(PluginEventManager.class),
773 mock(PluginExceptionInterception.class)
774 );
775 defaultPluginManager.init();
776 return defaultPluginManager;
777 }
778
779 @Test
780 public void addDynamicModuleNoLoader() {
781 final PluginLoader pluginLoader = mock(PluginLoader.class);
782 final PluginInternal plugin = mock(PluginInternal.class);
783
784 manager = newDefaultPluginManager(pluginLoader);
785
786 when(plugin.toString()).thenReturn("bleh");
787
788 expectedException.expect(PluginException.class);
789 expectedException.expectMessage(containsString("bleh"));
790
791 manager.addDynamicModule(plugin, mock(Element.class));
792 }
793
794
795 @Test
796 public void removeDynamicModuleNotPresent() {
797 final PluginLoader pluginLoader = mock(PluginLoader.class);
798 final PluginInternal plugin = mock(PluginInternal.class);
799 final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class, withSettings().extraInterfaces(StateAware.class));
800
801 manager = newDefaultPluginManager(pluginLoader);
802
803 when(plugin.removeDynamicModuleDescriptor(moduleDescriptor)).thenReturn(false);
804 when(plugin.toString()).thenReturn("crapPlugin");
805 when(moduleDescriptor.getKey()).thenReturn("moduleKey");
806
807 expectedException.expect(PluginException.class);
808 expectedException.expectMessage(containsString("crapPlugin"));
809 expectedException.expectMessage(containsString("moduleKey"));
810
811 manager.removeDynamicModule(plugin, moduleDescriptor);
812
813 verify((StateAware) moduleDescriptor, never()).enabled();
814 }
815
816 @Test
817 public void removeDynamicModule() {
818 final PluginLoader pluginLoader = mock(PluginLoader.class);
819 final PluginInternal plugin = mock(PluginInternal.class);
820 final ModuleDescriptor<?> moduleDescriptor = mock(ModuleDescriptor.class, withSettings().extraInterfaces(StateAware.class));
821
822 manager = newDefaultPluginManager(pluginLoader);
823
824 when(plugin.removeDynamicModuleDescriptor(moduleDescriptor)).thenReturn(true);
825
826 manager.removeDynamicModule(plugin, moduleDescriptor);
827
828 verify(plugin).removeDynamicModuleDescriptor(moduleDescriptor);
829 verify(moduleDescriptor).destroy();
830 verify((StateAware) moduleDescriptor).disabled();
831 }
832
833 @Test
834 public void testUninstallPluginWithMultiLevelDependencies() throws PluginException {
835
836 Plugin child = mock(Plugin.class);
837 when(child.getKey()).thenReturn("child");
838 when(child.isEnabledByDefault()).thenReturn(true);
839 when(child.getPluginState()).thenReturn(PluginState.ENABLED);
840 when(child.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of("parent"), null, null));
841 when(child.compareTo(any(Plugin.class))).thenReturn(-1);
842
843 Plugin parent = mock(Plugin.class);
844 when(parent.getKey()).thenReturn("parent");
845 when(parent.isEnabledByDefault()).thenReturn(true);
846 when(parent.getPluginState()).thenReturn(PluginState.ENABLED);
847 when(parent.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of("grandparent"), null, null));
848 when(parent.compareTo(any(Plugin.class))).thenReturn(-1);
849
850 Plugin grandparent = mock(Plugin.class);
851 when(grandparent.getKey()).thenReturn("grandparent");
852 when(grandparent.isEnabledByDefault()).thenReturn(true);
853 when(grandparent.isDeleteable()).thenReturn(true);
854 when(grandparent.isUninstallable()).thenReturn(true);
855 when(grandparent.getPluginState()).thenReturn(PluginState.ENABLED);
856 when(grandparent.compareTo(any(Plugin.class))).thenReturn(-1);
857 when(grandparent.getDependencies()).thenReturn(new PluginDependencies());
858
859 PluginLoader pluginLoader = mockPluginLoaderForPlugins(child, parent, grandparent);
860 when(pluginLoader.supportsRemoval()).thenReturn(true);
861
862 manager = initNewDefaultPluginManager(pluginLoader);
863
864 manager.uninstall(grandparent);
865 verify(grandparent).enable();
866 verify(grandparent).disable();
867 verify(pluginLoader).removePlugin(grandparent);
868
869 verify(parent).enable();
870 verify(parent).disable();
871 verify(child).enable();
872 verify(child).disable();
873 }
874
875 @Test
876 public void testUninstallPluginWithDependencies() throws PluginException {
877 Plugin child = mock(Plugin.class);
878 when(child.getKey()).thenReturn("child");
879 when(child.isEnabledByDefault()).thenReturn(true);
880 when(child.getPluginState()).thenReturn(PluginState.ENABLED);
881 when(child.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of("parent"), null, null));
882 when(child.compareTo(any(Plugin.class))).thenReturn(-1);
883 Plugin parent = mock(Plugin.class);
884 when(parent.getKey()).thenReturn("parent");
885 when(parent.isEnabledByDefault()).thenReturn(true);
886 when(parent.isDeleteable()).thenReturn(true);
887 when(parent.isUninstallable()).thenReturn(true);
888 when(parent.getPluginState()).thenReturn(PluginState.ENABLED);
889 when(parent.compareTo(any(Plugin.class))).thenReturn(-1);
890 when(parent.getDependencies()).thenReturn(new PluginDependencies());
891
892 PluginLoader pluginLoader = mockPluginLoaderForPlugins(child, parent);
893 when(pluginLoader.supportsRemoval()).thenReturn(true);
894
895 manager = initNewDefaultPluginManager(pluginLoader);
896
897 manager.uninstall(parent);
898 verify(parent).enable();
899 verify(parent).disable();
900 verify(pluginLoader).removePlugin(parent);
901
902 verify(child).enable();
903 verify(child).disable();
904 }
905
906 @Test
907 public void testInstallPluginsWithTwoButOneFailsValidationWithException() {
908 DynamicPluginLoader loader = mock(DynamicPluginLoader.class);
909 when(loader.isDynamicPluginLoader()).thenReturn(true);
910
911 moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
912 PluginInstaller installer = mock(PluginInstaller.class);
913 DefaultPluginManager pm = newDefaultPluginManager(loader);
914 pm.setPluginInstaller(installer);
915 PluginArtifact artifactA = mock(PluginArtifact.class);
916 Plugin pluginA = mock(Plugin.class);
917 when(loader.canLoad(artifactA)).thenReturn("a");
918 PluginArtifact artifactB = mock(PluginArtifact.class);
919 Plugin pluginB = mock(Plugin.class);
920 doThrow(new PluginParseException()).when(loader).canLoad(artifactB);
921
922 when(loader.loadFoundPlugins(moduleDescriptorFactory)).thenReturn(Arrays.asList(pluginA, pluginB));
923
924 try {
925 manager.installPlugins(artifactA, artifactB);
926 fail("Should have not installed plugins");
927 } catch (PluginParseException ex) {
928
929 }
930
931 verify(loader).canLoad(artifactA);
932 verify(loader).canLoad(artifactB);
933 verify(installer, never()).installPlugin("a", artifactA);
934 verify(installer, never()).installPlugin("b", artifactB);
935 }
936
937 @Test
938 public void testInstallPlugin() {
939 final PluginPersistentStateStore mockPluginStateStore = mock(PluginPersistentStateStore.class);
940 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
941 final DynamicPluginLoader mockPluginLoader = mock(DynamicPluginLoader.class);
942 when(mockPluginLoader.isDynamicPluginLoader()).thenReturn(true);
943
944 final DescriptorParser mockDescriptorParser = mock(DescriptorParser.class);
945 final PluginArtifact pluginArtifact = mock(PluginArtifact.class);
946 final PluginInstaller mockRepository = mock(PluginInstaller.class);
947 final Plugin plugin = mock(Plugin.class);
948
949
950 final DefaultPluginManager pluginManager = new DefaultPluginManager(mockPluginStateStore,
951 singletonList(mockPluginLoader), moduleDescriptorFactory, pluginEventManager);
952
953 when(mockPluginStateStore.load()).thenReturn(PluginPersistentState.builder().toState());
954 when(mockPluginStateStore.load()).thenReturn(PluginPersistentState.builder().toState());
955 when(mockPluginStateStore.load()).thenReturn(PluginPersistentState.builder().toState());
956 when(mockDescriptorParser.getKey()).thenReturn("test");
957 when(mockPluginLoader.loadAllPlugins(moduleDescriptorFactory)).thenReturn(emptyList());
958 when(mockPluginLoader.supportsAddition()).thenReturn(true);
959 when(mockPluginLoader.loadFoundPlugins(moduleDescriptorFactory)).thenReturn(singletonList(plugin));
960 when(mockPluginLoader.canLoad(pluginArtifact)).thenReturn("test");
961 when(plugin.getKey()).thenReturn("test");
962 when(plugin.getModuleDescriptors()).thenReturn(new ArrayList<>());
963 when(plugin.getModuleDescriptors()).thenReturn(new ArrayList<>());
964 when(plugin.isEnabledByDefault()).thenReturn(true);
965
966 when(plugin.isEnabledByDefault()).thenReturn(true);
967 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
968 when(plugin.hasAllPermissions()).thenReturn(true);
969 when(plugin.getActivePermissions()).thenReturn(ImmutableSet.of(Permissions.ALL_PERMISSIONS));
970 when(plugin.getDependencies()).thenReturn(new PluginDependencies());
971
972 pluginManager.setPluginInstaller(mockRepository);
973 pluginManager.init();
974 final PassListener enabledListener = new PassListener(PluginEnabledEvent.class);
975 pluginEventManager.register(enabledListener);
976 pluginManager.installPlugins(pluginArtifact);
977
978 assertEquals(plugin, pluginManager.getPlugin("test"));
979 assertTrue(pluginManager.isPluginEnabled("test"));
980
981 enabledListener.assertCalled();
982 }
983
984 @Test
985 public void testInstallPluginsWithOne() {
986 DynamicPluginLoader loader = mock(DynamicPluginLoader.class);
987 when(loader.isDynamicPluginLoader()).thenReturn(true);
988
989 moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
990 PluginInstaller installer = mock(PluginInstaller.class);
991 DefaultPluginManager pm = newDefaultPluginManager(loader);
992 pm.setPluginInstaller(installer);
993 when(loader.loadAllPlugins(moduleDescriptorFactory)).thenReturn(emptyList());
994 PluginArtifact artifact = mock(PluginArtifact.class);
995 Plugin plugin = mock(Plugin.class);
996 when(loader.canLoad(artifact)).thenReturn("foo");
997 when(loader.loadFoundPlugins(moduleDescriptorFactory)).thenReturn(singletonList(plugin));
998
999 pm.init();
1000 manager.installPlugins(artifact);
1001
1002 verify(loader).canLoad(artifact);
1003 verify(installer).installPlugin("foo", artifact);
1004 }
1005
1006 @Test
1007 public void testInstallPluginsWithTwo() {
1008 DynamicPluginLoader loader = mock(DynamicPluginLoader.class);
1009 when(loader.isDynamicPluginLoader()).thenReturn(true);
1010
1011 moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
1012 PluginInstaller installer = mock(PluginInstaller.class);
1013 DefaultPluginManager pm = newDefaultPluginManager(loader);
1014 pm.setPluginInstaller(installer);
1015 when(loader.loadAllPlugins(moduleDescriptorFactory)).thenReturn(emptyList());
1016 PluginArtifact artifactA = mock(PluginArtifact.class);
1017 Plugin pluginA = mock(Plugin.class);
1018 when(loader.canLoad(artifactA)).thenReturn("a");
1019 PluginArtifact artifactB = mock(PluginArtifact.class);
1020 Plugin pluginB = mock(Plugin.class);
1021 when(loader.canLoad(artifactB)).thenReturn("b");
1022
1023 when(loader.loadFoundPlugins(moduleDescriptorFactory)).thenReturn(Arrays.asList(pluginA, pluginB));
1024
1025 pm.init();
1026 manager.installPlugins(artifactA, artifactB);
1027
1028 verify(loader).canLoad(artifactA);
1029 verify(loader).canLoad(artifactB);
1030 verify(installer).installPlugin("a", artifactA);
1031 verify(installer).installPlugin("b", artifactB);
1032 }
1033
1034 @Test
1035 public void testInstallPluginsWithTwoButOneFailsValidation() {
1036 DynamicPluginLoader loader = mock(DynamicPluginLoader.class);
1037 when(loader.isDynamicPluginLoader()).thenReturn(true);
1038
1039 ModuleDescriptorFactory descriptorFactory = mock(ModuleDescriptorFactory.class);
1040 PluginInstaller installer = mock(PluginInstaller.class);
1041 DefaultPluginManager pm = newDefaultPluginManager(loader);
1042 pm.setPluginInstaller(installer);
1043 PluginArtifact artifactA = mock(PluginArtifact.class);
1044 Plugin pluginA = mock(Plugin.class);
1045 when(loader.canLoad(artifactA)).thenReturn("a");
1046 PluginArtifact artifactB = mock(PluginArtifact.class);
1047 Plugin pluginB = mock(Plugin.class);
1048 when(loader.canLoad(artifactB)).thenReturn(null);
1049
1050 when(loader.loadFoundPlugins(descriptorFactory)).thenReturn(Arrays.asList(pluginA, pluginB));
1051
1052 try {
1053 manager.installPlugins(artifactA, artifactB);
1054 fail("Should have not installed plugins");
1055 } catch (PluginParseException ex) {
1056
1057 }
1058
1059 verify(loader).canLoad(artifactA);
1060 verify(loader).canLoad(artifactB);
1061 verify(installer, never()).installPlugin("a", artifactA);
1062 verify(installer, never()).installPlugin("b", artifactB);
1063 }
1064
1065 @Test
1066 public void testCircularDependencyWouldNotCauseInfiniteLoop() throws PluginException {
1067
1068 Plugin p1 = mock(Plugin.class);
1069 when(p1.getKey()).thenReturn("p1");
1070 when(p1.isEnabledByDefault()).thenReturn(true);
1071 when(p1.getPluginState()).thenReturn(PluginState.ENABLED);
1072 when(p1.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of("p2", "parent"), null, null));
1073 when(p1.compareTo(any(Plugin.class))).thenReturn(-1);
1074
1075
1076 Plugin p2 = mock(Plugin.class);
1077 when(p2.getKey()).thenReturn("p2");
1078 when(p2.isEnabledByDefault()).thenReturn(true);
1079 when(p2.getPluginState()).thenReturn(PluginState.ENABLED);
1080 when(p2.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of("p1"), null, null));
1081 when(p2.compareTo(any(Plugin.class))).thenReturn(-1);
1082
1083 Plugin parent = mock(Plugin.class);
1084 when(parent.getKey()).thenReturn("parent");
1085 when(parent.isEnabledByDefault()).thenReturn(true);
1086 when(parent.isDeleteable()).thenReturn(true);
1087 when(parent.isUninstallable()).thenReturn(true);
1088 when(parent.getPluginState()).thenReturn(PluginState.ENABLED);
1089 when(parent.compareTo(any(Plugin.class))).thenReturn(-1);
1090 when(parent.getDependencies()).thenReturn(new PluginDependencies());
1091
1092 PluginLoader pluginLoader = mockPluginLoaderForPlugins(p1, p2, parent);
1093 when(pluginLoader.supportsRemoval()).thenReturn(true);
1094
1095 manager = initNewDefaultPluginManager(pluginLoader);
1096
1097 manager.uninstall(parent);
1098 verify(parent, times(1)).enable();
1099 verify(parent, times(1)).disable();
1100 verify(pluginLoader).removePlugin(parent);
1101
1102 verify(p1, times(1)).enable();
1103 verify(p1, times(1)).disable();
1104 verify(p2, times(1)).enable();
1105 verify(p2, times(1)).disable();
1106 }
1107
1108 @Test
1109 public void testThreeCycleDependencyWouldNotCauseInfiniteLoop() throws PluginException {
1110 Plugin p1 = mock(Plugin.class);
1111 when(p1.getKey()).thenReturn("p1");
1112 when(p1.isEnabledByDefault()).thenReturn(true);
1113 when(p1.getPluginState()).thenReturn(PluginState.ENABLED);
1114 when(p1.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of("p2", "parent"), null, null));
1115 when(p1.compareTo(any(Plugin.class))).thenReturn(-1);
1116
1117
1118 Plugin p2 = mock(Plugin.class);
1119 when(p2.getKey()).thenReturn("p2");
1120 when(p2.isEnabledByDefault()).thenReturn(true);
1121 when(p2.getPluginState()).thenReturn(PluginState.ENABLED);
1122 when(p2.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of("p3"), null, null));
1123 when(p2.compareTo(any(Plugin.class))).thenReturn(-1);
1124
1125 Plugin p3 = mock(Plugin.class);
1126 when(p3.getKey()).thenReturn("p3");
1127 when(p3.isEnabledByDefault()).thenReturn(true);
1128 when(p3.getPluginState()).thenReturn(PluginState.ENABLED);
1129 when(p3.getDependencies()).thenReturn(new PluginDependencies(ImmutableSet.of("p1"), null, null));
1130 when(p3.compareTo(any(Plugin.class))).thenReturn(-1);
1131
1132 Plugin parent = mock(Plugin.class);
1133 when(parent.getKey()).thenReturn("parent");
1134 when(parent.isEnabledByDefault()).thenReturn(true);
1135 when(parent.isDeleteable()).thenReturn(true);
1136 when(parent.isUninstallable()).thenReturn(true);
1137 when(parent.getPluginState()).thenReturn(PluginState.ENABLED);
1138 when(parent.compareTo(any(Plugin.class))).thenReturn(-1);
1139 when(parent.getDependencies()).thenReturn(new PluginDependencies());
1140
1141 PluginLoader pluginLoader = mockPluginLoaderForPlugins(p1, p2, p3, parent);
1142 when(pluginLoader.supportsRemoval()).thenReturn(true);
1143
1144 manager = initNewDefaultPluginManager(pluginLoader);
1145
1146 manager.uninstall(parent);
1147 verify(parent, times(1)).enable();
1148 verify(parent, times(1)).disable();
1149 verify(pluginLoader).removePlugin(parent);
1150
1151 verify(p1, times(1)).enable();
1152 verify(p1, times(1)).disable();
1153 verify(p2, times(1)).enable();
1154 verify(p2, times(1)).disable();
1155 verify(p3, times(1)).enable();
1156 verify(p3, times(1)).disable();
1157 }
1158
1159 @Test
1160 public void addDynamicModule() {
1161 final PluginLoader pluginLoader = mock(PluginLoader.class);
1162 final PluginInternal plugin = mock(PluginInternal.class);
1163 final Element module = mock(Element.class);
1164 final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class, withSettings().extraInterfaces(StateAware.class));
1165
1166 newDefaultPluginManager(pluginLoader)
1167 .addPlugins(pluginLoader, ImmutableList.of(plugin));
1168
1169 when(pluginLoader.createModule(plugin, module, moduleDescriptorFactory)).thenReturn(moduleDescriptor);
1170 when(moduleDescriptor.isEnabledByDefault()).thenReturn(true);
1171 when(plugin.addDynamicModuleDescriptor(moduleDescriptor)).thenReturn(true);
1172 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
1173
1174 assertThat(manager.addDynamicModule(plugin, module), is(moduleDescriptor));
1175
1176 verify((StateAware) moduleDescriptor).enabled();
1177 }
1178
1179 @Test
1180 public void addDynamicModulePluginDisabled() {
1181 final PluginLoader pluginLoader = mock(PluginLoader.class);
1182 final PluginInternal plugin = mock(PluginInternal.class);
1183 final Element module = mock(Element.class);
1184 final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class, withSettings().extraInterfaces(StateAware.class));
1185
1186 newDefaultPluginManager(pluginLoader)
1187 .addPlugins(pluginLoader, ImmutableList.of(plugin));
1188
1189 when(pluginLoader.createModule(plugin, module, moduleDescriptorFactory)).thenReturn(moduleDescriptor);
1190 when(moduleDescriptor.isEnabledByDefault()).thenReturn(true);
1191 when(plugin.addDynamicModuleDescriptor(moduleDescriptor)).thenReturn(true);
1192 when(plugin.getPluginState()).thenReturn(PluginState.DISABLED);
1193
1194 manager.addDynamicModule(plugin, module);
1195
1196 verify((StateAware) moduleDescriptor, never()).enabled();
1197 }
1198
1199 @Test
1200 public void addDynamicModuleModuleNotEnabledByDefault() {
1201 final PluginLoader pluginLoader = mock(PluginLoader.class);
1202 final PluginInternal plugin = mock(PluginInternal.class);
1203 final Element module = mock(Element.class);
1204 final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class, withSettings().extraInterfaces(StateAware.class));
1205
1206 newDefaultPluginManager(pluginLoader)
1207 .addPlugins(pluginLoader, ImmutableList.of(plugin));
1208
1209 when(pluginLoader.createModule(plugin, module, moduleDescriptorFactory)).thenReturn(moduleDescriptor);
1210 when(moduleDescriptor.isEnabledByDefault()).thenReturn(false);
1211 when(plugin.addDynamicModuleDescriptor(moduleDescriptor)).thenReturn(true);
1212 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
1213
1214 manager.addDynamicModule(plugin, module);
1215
1216 verify((StateAware) moduleDescriptor, never()).enabled();
1217 }
1218
1219 @Test
1220 public void addDynamicModuleDuplicateKey() {
1221 final PluginLoader pluginLoader = mock(PluginLoader.class);
1222 final PluginInternal plugin = mock(PluginInternal.class);
1223 final Element module = mock(Element.class);
1224 final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class, withSettings().extraInterfaces(StateAware.class));
1225 final ModuleDescriptor existingModuleDescriptor = mock(ModuleDescriptor.class);
1226
1227 newDefaultPluginManager(pluginLoader)
1228 .addPlugins(pluginLoader, ImmutableList.of(plugin));
1229
1230 when(pluginLoader.createModule(plugin, module, moduleDescriptorFactory)).thenReturn(moduleDescriptor);
1231 when(plugin.toString()).thenReturn("awesomePlugin");
1232 when(plugin.getModuleDescriptor("moduleKey")).thenReturn(existingModuleDescriptor);
1233 when(moduleDescriptor.getKey()).thenReturn("moduleKey");
1234
1235 expectedException.expect(PluginException.class);
1236 expectedException.expectMessage(containsString("awesomePlugin"));
1237 expectedException.expectMessage(containsString("moduleKey"));
1238
1239 manager.addDynamicModule(plugin, module);
1240
1241 verify((StateAware) moduleDescriptor, never()).enabled();
1242 }
1243
1244 @Test
1245 public void addDynamicModuleAlreadyPresentAsDynamicModule() {
1246 final PluginLoader pluginLoader = mock(PluginLoader.class);
1247 final PluginInternal plugin = mock(PluginInternal.class);
1248 final Element module = mock(Element.class);
1249 final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class, withSettings().extraInterfaces(StateAware.class));
1250
1251 newDefaultPluginManager(pluginLoader)
1252 .addPlugins(pluginLoader, ImmutableList.of(plugin));
1253
1254 when(pluginLoader.createModule(plugin, module, moduleDescriptorFactory)).thenReturn(moduleDescriptor);
1255 when(plugin.addDynamicModuleDescriptor(moduleDescriptor)).thenReturn(false);
1256 when(plugin.toString()).thenReturn("lawsonPlugin");
1257 when(moduleDescriptor.getKey()).thenReturn("moduleKey");
1258
1259 expectedException.expect(PluginException.class);
1260 expectedException.expectMessage(containsString("lawsonPlugin"));
1261 expectedException.expectMessage(containsString("moduleKey"));
1262
1263 manager.addDynamicModule(plugin, module);
1264
1265 verify((StateAware) moduleDescriptor, never()).enabled();
1266 }
1267
1268 private static class CannotEnablePlugin extends StaticPlugin {
1269 public CannotEnablePlugin() {
1270 setKey("foo");
1271 }
1272
1273 @Override
1274 protected PluginState enableInternal() {
1275 throw new RuntimeException("boo");
1276 }
1277
1278 public void disabled() {
1279 }
1280 }
1281
1282 @Test
1283 public void testAddPluginsThatThrowExceptionOnEnabled() {
1284 final Plugin plugin = new CannotEnablePlugin();
1285
1286 newDefaultPluginManager()
1287 .addPlugins(null, singletonList(plugin));
1288
1289 assertNotSame(plugin.getPluginState(), PluginState.ENABLED);
1290 }
1291
1292 private Plugin newPluginMock() {
1293 Plugin plugin = mock(Plugin.class, RETURNS_DEEP_STUBS);
1294 when(plugin.getModuleDescriptors()).thenReturn(emptyList());
1295 return plugin;
1296 }
1297 }