1 package com.atlassian.plugin.loaders;
2
3 import com.atlassian.plugin.DefaultModuleDescriptorFactory;
4 import com.atlassian.plugin.Plugin;
5 import com.atlassian.plugin.PluginAccessor;
6 import com.atlassian.plugin.PluginException;
7 import com.atlassian.plugin.PluginParseException;
8 import com.atlassian.plugin.event.PluginEventManager;
9 import com.atlassian.plugin.event.impl.DefaultPluginEventManager;
10 import com.atlassian.plugin.factories.LegacyDynamicPluginFactory;
11 import com.atlassian.plugin.factories.PluginFactory;
12 import com.atlassian.plugin.hostcontainer.DefaultHostContainer;
13 import com.atlassian.plugin.impl.UnloadablePlugin;
14 import com.atlassian.plugin.loaders.classloading.DirectoryPluginLoaderUtils;
15 import com.atlassian.plugin.mock.MockAnimalModuleDescriptor;
16 import com.atlassian.plugin.mock.MockBear;
17 import com.atlassian.plugin.mock.MockMineralModuleDescriptor;
18 import com.atlassian.plugin.test.PluginJarBuilder;
19 import com.google.common.base.Predicate;
20 import com.google.common.collect.ImmutableList;
21 import com.google.common.collect.Iterables;
22 import org.apache.commons.io.FileUtils;
23 import org.junit.After;
24 import org.junit.Before;
25 import org.junit.Test;
26
27 import java.io.File;
28 import java.io.FileOutputStream;
29 import java.io.FilenameFilter;
30 import java.io.IOException;
31 import java.io.OutputStream;
32 import java.net.URISyntaxException;
33 import java.util.Iterator;
34 import java.util.List;
35 import java.util.jar.JarEntry;
36 import java.util.jar.JarOutputStream;
37
38 import static com.atlassian.plugin.loaders.classloading.DirectoryPluginLoaderUtils.PADDINGTON_JAR;
39 import static org.junit.Assert.assertEquals;
40 import static org.junit.Assert.assertFalse;
41 import static org.junit.Assert.assertNotNull;
42 import static org.junit.Assert.assertNull;
43 import static org.junit.Assert.assertTrue;
44 import static org.junit.Assert.fail;
45
46 public class TestDirectoryPluginLoader {
47 private static final List<PluginFactory> DEFAULT_PLUGIN_FACTORIES = ImmutableList.of(
48 new LegacyDynamicPluginFactory(PluginAccessor.Descriptor.FILENAME));
49
50 private PluginEventManager pluginEventManager;
51 private DirectoryPluginLoader loader;
52 private DefaultModuleDescriptorFactory moduleDescriptorFactory;
53 private File pluginsDirectory;
54 private File pluginsTestDir;
55
56 public void createFillAndCleanTempPluginDirectory() throws IOException {
57 final DirectoryPluginLoaderUtils.ScannerDirectories directories = DirectoryPluginLoaderUtils.createFillAndCleanTempPluginDirectory();
58 pluginsDirectory = directories.pluginsDirectory;
59 pluginsTestDir = directories.pluginsTestDir;
60 }
61
62 @Before
63 public void setUp() throws Exception {
64 moduleDescriptorFactory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
65 pluginEventManager = new DefaultPluginEventManager();
66 createFillAndCleanTempPluginDirectory();
67 }
68
69 @After
70 public void tearDown() throws Exception {
71 FileUtils.deleteDirectory(pluginsTestDir);
72 }
73
74 @Test
75 public void testAtlassianPlugin() throws Exception {
76 addTestModuleDecriptors();
77 loader = new DirectoryPluginLoader(pluginsTestDir, DEFAULT_PLUGIN_FACTORIES, pluginEventManager);
78 final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
79
80 assertEquals(2, Iterables.size(plugins));
81
82 for (final Plugin plugin : plugins) {
83 assertTrue(plugin.getName().equals("Test Class Loaded Plugin") || plugin.getName().equals("Test Class Loaded Plugin 2"));
84
85 if (plugin.getName().equals("Test Class Loaded Plugin"))
86 {
87 assertEquals("Test Class Loaded Plugin", plugin.getName());
88 assertEquals("test.atlassian.plugin.classloaded", plugin.getKey());
89 assertEquals(1, plugin.getModuleDescriptors().size());
90 final MockAnimalModuleDescriptor paddingtonDescriptor = (MockAnimalModuleDescriptor) plugin.getModuleDescriptor("paddington");
91 paddingtonDescriptor.enabled();
92 assertEquals("Paddington Bear", paddingtonDescriptor.getName());
93 final MockBear paddington = (MockBear) paddingtonDescriptor.getModule();
94 assertEquals("com.atlassian.plugin.mock.MockPaddington", paddington.getClass().getName());
95 } else if (plugin.getName().equals("Test Class Loaded Plugin 2"))
96 {
97 assertEquals("Test Class Loaded Plugin 2", plugin.getName());
98 assertEquals("test.atlassian.plugin.classloaded2", plugin.getKey());
99 assertEquals(1, plugin.getModuleDescriptors().size());
100 final MockAnimalModuleDescriptor poohDescriptor = (MockAnimalModuleDescriptor) plugin.getModuleDescriptor("pooh");
101 poohDescriptor.enabled();
102 assertEquals("Pooh Bear", poohDescriptor.getName());
103 final MockBear pooh = (MockBear) poohDescriptor.getModule();
104 assertEquals("com.atlassian.plugin.mock.MockPooh", pooh.getClass().getName());
105 } else {
106 fail("What plugin name?!");
107 }
108 }
109 }
110
111 private void addTestModuleDecriptors() {
112 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
113 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
114 }
115
116 @Test
117 public void testSupportsAdditionAndRemoval() {
118 loader = new DirectoryPluginLoader(pluginsTestDir, DEFAULT_PLUGIN_FACTORIES, pluginEventManager);
119 assertTrue(loader.supportsAddition());
120 assertTrue(loader.supportsRemoval());
121 }
122
123 @Test
124 public void testNoFoundPlugins() throws PluginParseException {
125 addTestModuleDecriptors();
126 loader = new DirectoryPluginLoader(pluginsTestDir, DEFAULT_PLUGIN_FACTORIES, pluginEventManager);
127 Iterable<Plugin> col = loader.loadFoundPlugins(moduleDescriptorFactory);
128 assertFalse(Iterables.isEmpty(col));
129
130 col = loader.loadFoundPlugins(moduleDescriptorFactory);
131 assertTrue(Iterables.isEmpty(col));
132 }
133
134 @Test
135 public void testFoundPlugin() throws PluginParseException, IOException {
136
137 final File paddington = new File(pluginsTestDir + File.separator + PADDINGTON_JAR);
138 paddington.delete();
139
140 addTestModuleDecriptors();
141 loader = new DirectoryPluginLoader(pluginsTestDir, DEFAULT_PLUGIN_FACTORIES, pluginEventManager);
142 loader.loadAllPlugins(moduleDescriptorFactory);
143
144
145 FileUtils.copyDirectory(pluginsDirectory, pluginsTestDir);
146
147 Iterable<Plugin> col = loader.loadFoundPlugins(moduleDescriptorFactory);
148 assertEquals(1, Iterables.size(col));
149
150 col = loader.loadFoundPlugins(moduleDescriptorFactory);
151 assertEquals(0, Iterables.size(col));
152 }
153
154 @Test
155 public void testRemovePlugin() throws PluginException, IOException {
156 addTestModuleDecriptors();
157 loader = new DirectoryPluginLoader(pluginsTestDir, DEFAULT_PLUGIN_FACTORIES, pluginEventManager);
158 final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
159
160
161
162
163 final Iterator<Plugin> iter = plugins.iterator();
164
165 Plugin paddingtonPlugin = null;
166
167 while (iter.hasNext()) {
168 final Plugin plugin = iter.next();
169
170 if (plugin.getName().equals("Test Class Loaded Plugin")) {
171 paddingtonPlugin = plugin;
172 break;
173 }
174 }
175
176 if (paddingtonPlugin == null) {
177 fail("Can't find test plugin 1 (paddington)");
178 }
179
180 loader.removePlugin(paddingtonPlugin);
181 }
182
183 @Test
184 public void testInvalidPluginHandled() throws IOException, PluginParseException {
185 createJarFile("evilplugin.jar", PluginAccessor.Descriptor.FILENAME, pluginsTestDir.getAbsolutePath());
186
187 loader = new DirectoryPluginLoader(pluginsTestDir, DEFAULT_PLUGIN_FACTORIES, pluginEventManager);
188
189 final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
190
191 assertEquals("evil jar wasn't loaded, but other plugins were", pluginsTestDir.list(new FilenameFilter() {
192
193 public boolean accept(final File directory, final String fileName) {
194 return fileName.endsWith(".jar");
195 }
196 }).length, Iterables.size(plugins));
197
198 assertEquals(1, Iterables.size(Iterables.filter(plugins, new Predicate<Plugin>() {
199 @Override
200 public boolean apply(final Plugin input) {
201 return input instanceof UnloadablePlugin;
202 }
203 })));
204 }
205
206 @Test
207 public void testInstallPluginTwice() throws URISyntaxException, IOException, PluginParseException, InterruptedException {
208 FileUtils.cleanDirectory(pluginsTestDir);
209 final File plugin = new File(pluginsTestDir, "some-plugin.jar");
210 new PluginJarBuilder("plugin").addPluginInformation("some.key", "My name", "1.0", 1).addResource("foo.txt", "foo").build().renameTo(plugin);
211
212 loader = new DirectoryPluginLoader(pluginsTestDir, DEFAULT_PLUGIN_FACTORIES, pluginEventManager);
213
214 Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
215 assertEquals(1, Iterables.size(plugins));
216 assertNotNull((plugins.iterator().next()).getResource("foo.txt"));
217 assertNull((plugins.iterator().next()).getResource("bar.txt"));
218
219 Thread.currentThread();
220
221 Thread.sleep(1000);
222
223 plugin.delete();
224 new PluginJarBuilder("plugin").addPluginInformation("some.key", "My name", "1.0", 1).addResource("bar.txt", "bar").build().renameTo(plugin);
225 plugins = loader.loadFoundPlugins(moduleDescriptorFactory);
226 assertEquals(1, Iterables.size(plugins));
227 assertNull((plugins.iterator().next()).getResource("foo.txt"));
228 assertNotNull((plugins.iterator().next()).getResource("bar.txt"));
229 assertTrue(plugin.exists());
230
231 }
232
233 @Test
234 public void testMixedFactories() throws URISyntaxException, IOException, PluginParseException, InterruptedException {
235 FileUtils.cleanDirectory(pluginsTestDir);
236 final File plugin = new File(pluginsTestDir, "some-plugin.jar");
237 new PluginJarBuilder("plugin").addPluginInformation("some.key", "My name", "1.0", 1).addResource("foo.txt", "foo").build().renameTo(plugin);
238 FileUtils.writeStringToFile(new File(pluginsTestDir, "foo.xml"), "<atlassian-plugin key=\"jim\"></atlassian-plugin>");
239
240 loader = new DirectoryPluginLoader(pluginsTestDir, DEFAULT_PLUGIN_FACTORIES, pluginEventManager);
241
242 final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
243 assertEquals(2, Iterables.size(plugins));
244 }
245
246 @Test
247 public void testUnknownPluginArtifact() throws URISyntaxException, IOException, PluginParseException, InterruptedException {
248 FileUtils.cleanDirectory(pluginsTestDir);
249 FileUtils.writeStringToFile(new File(pluginsTestDir, "foo.bob"), "<an>");
250
251 loader = new DirectoryPluginLoader(pluginsTestDir, DEFAULT_PLUGIN_FACTORIES, pluginEventManager);
252
253 final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
254 assertEquals(1, Iterables.size(plugins));
255 assertTrue(plugins.iterator().next() instanceof UnloadablePlugin);
256 }
257
258 @Test
259 public void testPluginWithModuleDescriptorWithNoKey() throws Exception, IOException, PluginParseException, InterruptedException {
260 FileUtils.cleanDirectory(pluginsTestDir);
261 new PluginJarBuilder("first")
262 .addFormattedResource("atlassian-plugin.xml",
263 "<atlassian-plugin name='Test' key='test.plugin'>",
264 " <plugin-info>",
265 " <version>1.0</version>",
266 " </plugin-info>",
267 " <object/>",
268 "</atlassian-plugin>")
269 .build(pluginsTestDir);
270
271 loader = new DirectoryPluginLoader(pluginsTestDir, DEFAULT_PLUGIN_FACTORIES, pluginEventManager);
272
273 final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
274 assertEquals(1, Iterables.size(plugins));
275 assertTrue(plugins.iterator().next() instanceof UnloadablePlugin);
276 assertEquals("test.plugin", plugins.iterator().next().getKey());
277 }
278
279 @Test
280 public void testPluginWithBadDescriptor() throws Exception, IOException, PluginParseException, InterruptedException {
281 FileUtils.cleanDirectory(pluginsTestDir);
282 File pluginJar = new PluginJarBuilder("first")
283 .addFormattedResource("atlassian-plugin.xml",
284 "<atlassian-pluasdfasdf")
285 .build(pluginsTestDir);
286
287 loader = new DirectoryPluginLoader(pluginsTestDir, DEFAULT_PLUGIN_FACTORIES, pluginEventManager);
288
289 final Iterable<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
290 assertEquals(1, Iterables.size(plugins));
291 assertTrue(plugins.iterator().next() instanceof UnloadablePlugin);
292 assertEquals(pluginJar.getName(), plugins.iterator().next().getKey());
293 }
294
295 private void createJarFile(final String jarname, final String jarEntry, final String saveDir) throws IOException {
296 final OutputStream os = new FileOutputStream(saveDir + File.separator + jarname);
297 final JarOutputStream plugin1 = new JarOutputStream(os);
298 final JarEntry jarEntry1 = new JarEntry(jarEntry);
299
300 plugin1.putNextEntry(jarEntry1);
301 plugin1.closeEntry();
302 plugin1.flush();
303 plugin1.close();
304 }
305
306 }