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