1 package com.atlassian.plugin.loaders;
2
3 import com.atlassian.plugin.ModuleDescriptor;
4 import com.atlassian.plugin.ModuleDescriptorFactory;
5 import com.atlassian.plugin.Plugin;
6 import com.atlassian.plugin.PluginArtifact;
7 import com.atlassian.plugin.PluginArtifactFactory;
8 import com.atlassian.plugin.PluginException;
9 import com.atlassian.plugin.PluginState;
10 import com.atlassian.plugin.event.PluginEventManager;
11 import com.atlassian.plugin.event.events.PluginFrameworkShutdownEvent;
12 import com.atlassian.plugin.factories.PluginFactory;
13 import com.atlassian.plugin.impl.AbstractDelegatingPlugin;
14 import com.atlassian.plugin.impl.UnloadablePlugin;
15 import com.atlassian.plugin.loaders.classloading.DeploymentUnit;
16 import com.atlassian.plugin.loaders.classloading.Scanner;
17 import com.google.common.collect.Iterables;
18 import org.dom4j.Element;
19 import org.junit.Before;
20 import org.junit.Rule;
21 import org.junit.Test;
22 import org.junit.rules.ExpectedException;
23 import org.junit.runner.RunWith;
24 import org.mockito.Mock;
25 import org.mockito.runners.MockitoJUnitRunner;
26
27 import java.io.File;
28 import java.util.Arrays;
29
30 import static org.hamcrest.MatcherAssert.assertThat;
31 import static org.hamcrest.Matchers.containsInAnyOrder;
32 import static org.hamcrest.Matchers.is;
33 import static org.hamcrest.Matchers.nullValue;
34 import static org.junit.Assert.assertEquals;
35 import static org.junit.Assert.assertNotNull;
36 import static org.junit.Assert.assertNotSame;
37 import static org.junit.Assert.assertSame;
38 import static org.junit.Assert.assertTrue;
39 import static org.junit.Assert.fail;
40 import static org.mockito.Matchers.any;
41 import static org.mockito.Mockito.mock;
42 import static org.mockito.Mockito.never;
43 import static org.mockito.Mockito.verify;
44 import static org.mockito.Mockito.when;
45
46 @RunWith(MockitoJUnitRunner.class)
47 public class TestScanningPluginLoader {
48 private static final String PLUGIN_KEY = "plugin-key";
49
50 @Rule
51 public ExpectedException expectedException = ExpectedException.none();
52
53 @Mock
54 private PluginArtifactFactory pluginArtifactFactory;
55
56 @Mock
57 private PluginArtifact pluginArtifact;
58
59 @Mock
60 private PluginFactory pluginFactory;
61
62 @Mock
63 private ModuleDescriptorFactory moduleDescriptorFactory;
64
65 @Mock
66 private Plugin plugin;
67
68 @Mock
69 private Scanner scanner;
70
71 @Mock
72 private PluginEventManager pluginEventManager;
73
74 @Mock
75 private Element module;
76
77 private DeploymentUnit deploymentUnit;
78
79 @Before
80 public void configureMocks() {
81 deploymentUnit = new DeploymentUnit(new File("foo.jar"));
82 when(plugin.getKey()).thenReturn(PLUGIN_KEY);
83 when(pluginArtifactFactory.create(deploymentUnit.getPath().toURI())).thenReturn(pluginArtifact);
84 when(pluginFactory.canCreate(pluginArtifact)).thenReturn("foo");
85 when(scanner.getDeploymentUnits()).thenReturn(Arrays.asList(deploymentUnit));
86 }
87
88 @Test
89 public void loadAllPluginsLoadsPlugin() {
90 when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
91
92 final ScanningPluginLoader loader = buildScanningPluginLoader();
93 final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
94 assertThat(plugins, containsInAnyOrder(plugin));
95 }
96
97 @Test
98 public void removeEnabledPluginFails() {
99 when(plugin.getPluginState()).thenReturn(PluginState.ENABLED);
100
101 final ScanningPluginLoader loader = buildScanningPluginLoader();
102
103 expectedException.expect(PluginException.class);
104 expectedException.expectMessage(PLUGIN_KEY);
105 loader.removePlugin(plugin);
106 }
107
108 @Test
109 public void removeNotUninstallablePluginFails() {
110 when(plugin.isUninstallable()).thenReturn(false);
111
112 final ScanningPluginLoader loader = buildScanningPluginLoader();
113
114 expectedException.expect(PluginException.class);
115 expectedException.expectMessage(PLUGIN_KEY);
116 loader.removePlugin(plugin);
117 }
118
119 @Test
120 public void removeDeleteablePluginDoesUninstallAndDelete() {
121 when(plugin.isUninstallable()).thenReturn(true);
122 when(plugin.isDeleteable()).thenReturn(true);
123 when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
124
125 final ScanningPluginLoader loader = buildScanningPluginLoader();
126 final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
127 assertThat(plugins, containsInAnyOrder(plugin));
128 loader.removePlugin(plugin);
129 verify(plugin).uninstall();
130 verify(scanner).remove(deploymentUnit);
131 }
132
133 @Test
134 public void removeNotDeleteablePluginDoesUninstallButDoesntDelete() {
135 when(plugin.isUninstallable()).thenReturn(true);
136 when(plugin.isDeleteable()).thenReturn(false);
137 when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
138
139 final ScanningPluginLoader loader = buildScanningPluginLoader();
140 final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
141 assertThat(plugins, containsInAnyOrder(plugin));
142 loader.removePlugin(plugin);
143 verify(plugin).uninstall();
144 verify(scanner, never()).remove(any(DeploymentUnit.class));
145 }
146
147 @Test
148 public void discardedPluginIsNotTracked() {
149 when(plugin.isUninstallable()).thenReturn(true);
150 when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
151
152 final ScanningPluginLoader loader = buildScanningPluginLoader();
153 final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
154 assertThat(plugins, containsInAnyOrder(plugin));
155 loader.discardPlugin(plugin);
156 try {
157
158 loader.removePlugin(plugin);
159 fail();
160 } catch (final PluginException pe) {
161
162 }
163
164 loader.onShutdown(mock(PluginFrameworkShutdownEvent.class));
165 verify(plugin, never()).uninstall();
166 }
167
168 @Test
169 public void shutdownUninstallsUninstallablePlugin() {
170 when(plugin.isUninstallable()).thenReturn(true);
171 when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
172
173 final ScanningPluginLoader loader = buildScanningPluginLoader();
174 loader.loadAllPlugins(moduleDescriptorFactory);
175 loader.onShutdown(mock(PluginFrameworkShutdownEvent.class));
176 verify(plugin).uninstall();
177 }
178
179 @Test
180 public void shutdownDoesNotUninstallNotUninstallablePlugin() {
181 when(plugin.isUninstallable()).thenReturn(false);
182 when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
183
184 final ScanningPluginLoader loader = buildScanningPluginLoader();
185 loader.loadAllPlugins(moduleDescriptorFactory);
186 loader.onShutdown(mock(PluginFrameworkShutdownEvent.class));
187 verify(plugin, never()).uninstall();
188 }
189
190 @Test
191 public void factoryThrowingRuntimeExceptionYieldsUnloadablePlugin() {
192 factoryThrowingYieldsUnloadablePlugin(new IllegalArgumentException());
193 }
194
195 @Test
196 public void factoryThrowingErrorYieldsUnloadablePlugin() {
197 factoryThrowingYieldsUnloadablePlugin(new NoClassDefFoundError());
198 }
199
200 private void factoryThrowingYieldsUnloadablePlugin(final Throwable throwable) {
201 when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenThrow(throwable);
202
203 final ScanningPluginLoader loader = buildScanningPluginLoader();
204 final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
205 assertNotNull(plugins);
206 assertEquals(1, Iterables.size(plugins));
207 assertTrue(Iterables.getOnlyElement(plugins) instanceof UnloadablePlugin);
208 }
209
210 @Test
211 public void pluginLoaderCallsPostProcess() {
212 when(plugin.isUninstallable()).thenReturn(true);
213 when(pluginFactory.create(pluginArtifact, moduleDescriptorFactory)).thenReturn(plugin);
214
215 final ScanningPluginLoader loader = new ScanningPluginLoader(
216 scanner, Arrays.asList(pluginFactory), pluginArtifactFactory, pluginEventManager) {
217 @Override
218 protected Plugin postProcess(final Plugin plugin) {
219 return new WrappedPlugin(plugin);
220 }
221 };
222 final Iterable<Plugin> allPlugins = loader.loadAllPlugins(moduleDescriptorFactory);
223 assertPluginsIsWrapperFor(allPlugins, plugin);
224
225 final DeploymentUnit unitB = new DeploymentUnit(new File("bar.jar"));
226 final PluginArtifact pluginArtifactB = mock(PluginArtifact.class);
227 final Plugin pluginB = mock(Plugin.class);
228 when(scanner.scan()).thenReturn(Arrays.asList(unitB));
229 when(pluginArtifactFactory.create(unitB.getPath().toURI())).thenReturn(pluginArtifactB);
230 when(pluginFactory.canCreate(pluginArtifactB)).thenReturn("bar");
231 when(pluginFactory.create(pluginArtifactB, moduleDescriptorFactory)).thenReturn(pluginB);
232
233 final Iterable<Plugin> foundPlugins = loader.loadFoundPlugins(moduleDescriptorFactory);
234 assertPluginsIsWrapperFor(foundPlugins, pluginB);
235 }
236
237 @Test
238 public void createModule() {
239 final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class);
240
241 final ScanningPluginLoader scanningPluginLoader = buildScanningPluginLoader();
242
243 when(pluginFactory.createModule(plugin, module, moduleDescriptorFactory)).thenReturn(moduleDescriptor);
244
245 assertThat(scanningPluginLoader.createModule(plugin, module, moduleDescriptorFactory), is(moduleDescriptor));
246 }
247
248 @Test
249 public void createModuleNoFactory() {
250 final ScanningPluginLoader scanningPluginLoader = buildScanningPluginLoader();
251
252 when(pluginFactory.createModule(plugin, module, moduleDescriptorFactory)).thenReturn(null);
253
254 assertThat(scanningPluginLoader.createModule(plugin, module, moduleDescriptorFactory), nullValue());
255 }
256
257
258
259
260
261
262
263 private class WrappedPlugin extends AbstractDelegatingPlugin {
264 public WrappedPlugin(final Plugin plugin) {
265 super(plugin);
266 }
267 }
268
269 private void assertPluginsIsWrapperFor(final Iterable<Plugin> plugins, final Plugin originalPlugin) {
270 assertNotNull(plugins);
271 assertEquals(1, Iterables.size(plugins));
272 final Plugin loadedPlugin = Iterables.getOnlyElement(plugins);
273 assertNotSame(loadedPlugin, originalPlugin);
274 assertTrue(loadedPlugin instanceof WrappedPlugin);
275 final WrappedPlugin wrappedPlugin = (WrappedPlugin) loadedPlugin;
276 assertSame(wrappedPlugin.getDelegate(), originalPlugin);
277 }
278
279 private ScanningPluginLoader buildScanningPluginLoader() {
280 return new ScanningPluginLoader(scanner, Arrays.asList(pluginFactory), pluginArtifactFactory, pluginEventManager);
281 }
282 }