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