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