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