1 package com.atlassian.plugin.loaders;
2
3 import java.io.File;
4 import java.io.IOException;
5 import java.net.URISyntaxException;
6 import java.net.URL;
7 import java.util.Arrays;
8 import java.util.Collection;
9 import java.util.List;
10
11 import com.atlassian.plugin.ModuleDescriptorFactory;
12 import com.atlassian.plugin.Plugin;
13 import com.atlassian.plugin.PluginArtifact;
14 import com.atlassian.plugin.PluginException;
15 import com.atlassian.plugin.event.PluginEventManager;
16 import com.atlassian.plugin.loaders.classloading.DeploymentUnit;
17 import com.atlassian.plugin.loaders.classloading.Scanner;
18 import com.atlassian.plugin.test.PluginJarBuilder;
19 import com.atlassian.plugin.factories.PluginFactory;
20 import com.atlassian.plugin.test.PluginTestUtils;
21
22 import com.google.common.base.Function;
23 import com.google.common.collect.Iterables;
24 import com.google.common.collect.Lists;
25
26 import org.apache.commons.io.FileUtils;
27 import org.junit.After;
28 import org.junit.Before;
29 import org.junit.Rule;
30 import org.junit.Test;
31 import org.junit.rules.ExpectedException;
32 import org.mockito.ArgumentCaptor;
33
34
35 import static org.hamcrest.Matchers.containsInAnyOrder;
36 import static org.hamcrest.Matchers.instanceOf;
37 import static org.junit.Assert.assertEquals;
38 import static org.junit.Assert.assertThat;
39 import static org.junit.Assert.assertTrue;
40 import static org.mockito.Matchers.isA;
41 import static org.mockito.Matchers.same;
42 import static org.mockito.Mockito.mock;
43 import static org.mockito.Mockito.verify;
44 import static org.mockito.Mockito.when;
45
46 public class TestBundledPluginLoader
47 {
48 @Rule
49 public ExpectedException expectedException = ExpectedException.none();
50
51 private File pluginDir;
52
53 private static final String fooXml = "foo.xml";
54
55 @Before
56 public void createTemporaryDirectory() throws IOException, URISyntaxException
57 {
58 pluginDir = PluginTestUtils.createTempDirectory(TestBundledPluginLoader.class);
59 }
60
61 @After
62 public void deleteTemporaryDirectory() throws Exception
63 {
64 FileUtils.deleteDirectory(pluginDir);
65 pluginDir = null;
66 }
67
68 @Test
69 public void loaderFromZipWithUrlConstructorContainsExpectedFiles() throws IOException
70 {
71 final File bundledZip = buildBundledZip();
72 final BundledPluginLoader loader = buildBundledPluginLoader(bundledZip, true);
73 assertLoaderContains(loader, fooXml);
74 }
75
76 @Test
77 public void loaderFromZipWithNonUrlConstructorContainsExpectedFiles() throws IOException
78 {
79 final File bundledZip = buildBundledZip();
80 final BundledPluginLoader loader = buildBundledPluginLoader(bundledZip, false);
81
82
83
84 assertLoaderContains(loader);
85 }
86
87 @Test
88 public void loaderFromDirectoryWithUrlConstructorContainsExpectedFiles() throws IOException
89 {
90 validateLoaderFromDirectoryContainsExpectedFiles(true);
91 }
92
93 @Test
94 public void loaderFromDirectoryWithNonUrlConstructorContainsExpectedFiles() throws IOException
95 {
96 validateLoaderFromDirectoryContainsExpectedFiles(false);
97 }
98
99 private void validateLoaderFromDirectoryContainsExpectedFiles(final boolean useUrlConstructor) throws IOException
100 {
101 final File dir = PluginTestUtils.createTempDirectory(TestBundledPluginLoader.class);
102 FileUtils.writeStringToFile(new File(dir, "foo.txt"), "hello");
103
104 final BundledPluginLoader loader = buildBundledPluginLoader(dir, useUrlConstructor);
105 assertLoaderContains(loader, "foo.txt");
106 }
107
108 @Test
109 public void loaderFromFileListWithUrlConstructorContainsExpectedFiles() throws IOException
110 {
111 validateLoaderFromFileListContainsExpectedFiles(true);
112 }
113
114 @Test
115 public void loaderFromFileListWithNonUrlConstructorContainsExpectedFiles() throws IOException
116 {
117 validateLoaderFromFileListContainsExpectedFiles(false);
118 }
119
120 private void validateLoaderFromFileListContainsExpectedFiles(final boolean useUrlConstructor) throws IOException
121 {
122 FileUtils.writeStringToFile(new File(pluginDir, "foo.txt"), "hello");
123 FileUtils.writeStringToFile(new File(pluginDir, "bar.txt"), "world");
124 final File listFile = new File(pluginDir, "bundled-plugins" + BundledPluginLoader.getListSuffix());
125 FileUtils.writeStringToFile(listFile, "foo.txt\nbar.txt");
126
127 final BundledPluginLoader loader = buildBundledPluginLoader(listFile, useUrlConstructor);
128 assertLoaderContains(loader, "foo.txt", "bar.txt");
129 }
130
131 @Test
132 public void loaderFromUnsupportedFileWithUrlConstructorContainsExpectedFiles() throws IOException
133 {
134 validateLoaderFromUnsupportedFileContainsExpectedFiles(true);
135 }
136
137 @Test
138 public void loaderFromUnsupportedFileWithNonUrlConstructorContainsExpectedFiles() throws IOException
139 {
140 validateLoaderFromUnsupportedFileContainsExpectedFiles(false);
141 }
142
143 private void validateLoaderFromUnsupportedFileContainsExpectedFiles(final boolean useUrlConstructor) throws IOException
144 {
145 final File unsupportedFile = new File(pluginDir, "notASuitableFile.unknown-suffix");
146 FileUtils.writeStringToFile(unsupportedFile, "Some\nRandom\nContent\n");
147
148 final BundledPluginLoader loader = buildBundledPluginLoader(unsupportedFile, useUrlConstructor);
149 assertLoaderContains(loader);
150 }
151
152 @Test
153 public void loaderScannerDoesNotRemoveUnderlyingFiles() throws IOException
154 {
155 final File bundledZip = buildBundledZip();
156
157
158 final BundledPluginLoader loader = buildBundledPluginLoader(bundledZip, true);
159 final Scanner scanner = loader.scanner;
160 assertLoaderContains(loader, fooXml);
161 final Collection<DeploymentUnit> units = scanner.getDeploymentUnits();
162 final int countBefore = units.size();
163 int found = 0;
164 for (final DeploymentUnit unit : units)
165 {
166 final File file = unit.getPath();
167 if (fooXml.equals(file.getName()))
168 {
169 found += 1;
170 assertTrue(file.exists());
171 scanner.remove(unit);
172
173 assertTrue(file.exists());
174 }
175 }
176
177 assertEquals(1, found);
178
179 assertEquals(countBefore, scanner.getDeploymentUnits().size());
180 }
181
182 @Test
183 public void loaderRemoveDoesUninstallPlugin() throws IOException
184 {
185 final File bundledZip = buildBundledZip();
186 final Plugin mockPlugin = mock(Plugin.class);
187 final PluginFactory pluginFactory = mock(PluginFactory.class);
188 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
189
190 final String key = "plugin.key.foo";
191 final ArgumentCaptor<PluginArtifact> pluginArtifact = ArgumentCaptor.forClass(PluginArtifact.class);
192
193 when(mockPlugin.getKey()).thenReturn(key);
194 when(mockPlugin.isDeleteable()).thenReturn(true);
195 when(mockPlugin.isUninstallable()).thenReturn(true);
196 when(pluginFactory.canCreate(isA(PluginArtifact.class))).thenReturn(key);
197 when(pluginFactory.create(pluginArtifact.capture(), same(moduleDescriptorFactory))).thenReturn(mockPlugin);
198
199
200 final BundledPluginLoader loader = buildBundledPluginLoader(bundledZip, true, pluginFactory);
201 final Collection<Plugin> plugins = Lists.newArrayList(loader.loadAllPlugins(moduleDescriptorFactory));
202 assertEquals(1, plugins.size());
203
204 final Plugin bundledPlugin = Iterables.getOnlyElement(plugins);
205 assertEquals(key, bundledPlugin.getKey());
206 assertTrue(bundledPlugin.isBundledPlugin());
207 assertTrue(bundledPlugin.isDeleteable());
208 verify(mockPlugin).isDeleteable();
209
210
211 loader.removePlugin(bundledPlugin);
212 verify(mockPlugin).isUninstallable();
213 verify(mockPlugin).uninstall();
214
215 assertTrue(pluginArtifact.getValue().toFile().exists());
216
217
218
219
220
221
222
223 expectedException.expect(PluginException.class);
224 loader.removePlugin(bundledPlugin);
225 }
226
227 @Test
228 public void pluginArtifactsAllowReference() throws IOException
229 {
230
231 final File dir = PluginTestUtils.createTempDirectory(TestBundledPluginLoader.class);
232 final File bundleList = new File(dir, "bundled-plugins" + BundledPluginLoader.getListSuffix());
233 FileUtils.writeStringToFile(bundleList, "some.jar\n");
234 final Plugin mockPlugin = mock(Plugin.class);
235 final PluginFactory pluginFactory = mock(PluginFactory.class);
236 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
237
238 final String key = "plugin.key.foo";
239 final ArgumentCaptor<PluginArtifact> pluginArtifact = ArgumentCaptor.forClass(PluginArtifact.class);
240
241 when(mockPlugin.getKey()).thenReturn(key);
242 when(pluginFactory.canCreate(isA(PluginArtifact.class))).thenReturn(key);
243 when(pluginFactory.create(pluginArtifact.capture(), same(moduleDescriptorFactory))).thenReturn(mockPlugin);
244
245
246 final BundledPluginLoader loader = buildBundledPluginLoader(bundleList, false, pluginFactory);
247 loader.loadAllPlugins(moduleDescriptorFactory);
248 assertThat(pluginArtifact.getValue(), instanceOf(PluginArtifact.AllowsReference.class));
249 assertTrue(((PluginArtifact.AllowsReference) pluginArtifact.getValue()).allowsReference());
250 }
251
252 private File buildBundledZip() throws IOException
253 {
254 return new PluginJarBuilder("bundledPlugins")
255 .addResource(fooXml, "<foo/>")
256 .buildWithNoManifest();
257 }
258
259 private BundledPluginLoader buildBundledPluginLoader(
260 final File bundledPlugins,
261 final boolean useUrlConstructor,
262 final PluginFactory... pluginFactories)
263 throws IOException
264 {
265 final List<PluginFactory> pluginFactoryList = Arrays.asList(pluginFactories);
266 final PluginEventManager pluginEventManager = mock(PluginEventManager.class);
267 if (useUrlConstructor)
268 {
269 final URL bundledPluginsUrl = bundledPlugins.toURI().toURL();
270 return new BundledPluginLoader(bundledPluginsUrl, pluginDir, pluginFactoryList, pluginEventManager);
271 }
272 else
273 {
274 return new BundledPluginLoader(bundledPlugins, pluginFactoryList, pluginEventManager);
275 }
276 }
277
278 private void assertLoaderContains(final BundledPluginLoader loader, final String ... expectedEntries)
279 {
280 final Collection<DeploymentUnit> scanned = loader.scanner.scan();
281 final Iterable<String> actualEntries = Iterables.transform(scanned, new Function<DeploymentUnit, String>()
282 {
283 @Override
284 public String apply(final DeploymentUnit unit)
285 {
286 return unit.getPath().getName();
287 }
288 });
289 assertThat(actualEntries, containsInAnyOrder(expectedEntries));
290 }
291 }