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.google.common.base.Predicate;
21 import com.google.common.collect.Iterables;
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 java.util.Arrays.asList;
37 import static java.util.Collections.unmodifiableList;
38
39 public class TestDirectoryPluginLoader extends AbstractTestClassLoader
40 {
41 private PluginEventManager pluginEventManager;
42 private DirectoryPluginLoader loader;
43 private DefaultModuleDescriptorFactory moduleDescriptorFactory;
44 private static final List<PluginFactory> DEFAULT_PLUGIN_FACTORIES = unmodifiableList(asList(new LegacyDynamicPluginFactory(
45 PluginAccessor.Descriptor.FILENAME), new XmlDynamicPluginFactory("foo")));
46
47 public static final String BAD_PLUGIN_JAR = "bad-plugins/crap-plugin.jar";
48
49 @Override
50 public void setUp() throws Exception
51 {
52 super.setUp();
53 moduleDescriptorFactory = new DefaultModuleDescriptorFactory(new DefaultHostContainer());
54 pluginEventManager = new DefaultPluginEventManager();
55 createFillAndCleanTempPluginDirectory();
56 }
57
58 @Override
59 public void tearDown() throws Exception
60 {
61 FileUtils.deleteDirectory(pluginsTestDir);
62 super.tearDown();
63 }
64
65 public void testAtlassianPlugin() throws Exception
66 {
67 addTestModuleDecriptors();
68 loader = new DirectoryPluginLoader(pluginsTestDir, DEFAULT_PLUGIN_FACTORIES, pluginEventManager);
69 final Collection<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
70
71 assertEquals(2, plugins.size());
72
73 for (final Plugin plugin : plugins)
74 {
75 assertTrue(plugin.getName().equals("Test Class Loaded Plugin") || plugin.getName().equals("Test Class Loaded Plugin 2"));
76
77 if (plugin.getName().equals("Test Class Loaded Plugin"))
78 {
79 assertEquals("Test Class Loaded Plugin", plugin.getName());
80 assertEquals("test.atlassian.plugin.classloaded", plugin.getKey());
81 assertEquals(1, plugin.getModuleDescriptors().size());
82 final MockAnimalModuleDescriptor paddingtonDescriptor = (MockAnimalModuleDescriptor) plugin.getModuleDescriptor("paddington");
83 paddingtonDescriptor.enabled();
84 assertEquals("Paddington Bear", paddingtonDescriptor.getName());
85 final MockBear paddington = (MockBear) paddingtonDescriptor.getModule();
86 assertEquals("com.atlassian.plugin.mock.MockPaddington", paddington.getClass().getName());
87 }
88 else if (plugin.getName().equals("Test Class Loaded Plugin 2"))
89 {
90 assertEquals("Test Class Loaded Plugin 2", plugin.getName());
91 assertEquals("test.atlassian.plugin.classloaded2", plugin.getKey());
92 assertEquals(1, plugin.getModuleDescriptors().size());
93 final MockAnimalModuleDescriptor poohDescriptor = (MockAnimalModuleDescriptor) plugin.getModuleDescriptor("pooh");
94 poohDescriptor.enabled();
95 assertEquals("Pooh Bear", poohDescriptor.getName());
96 final MockBear pooh = (MockBear) poohDescriptor.getModule();
97 assertEquals("com.atlassian.plugin.mock.MockPooh", pooh.getClass().getName());
98 }
99 else
100 {
101 fail("What plugin name?!");
102 }
103 }
104 }
105
106 private void addTestModuleDecriptors()
107 {
108 moduleDescriptorFactory.addModuleDescriptor("animal", MockAnimalModuleDescriptor.class);
109 moduleDescriptorFactory.addModuleDescriptor("mineral", MockMineralModuleDescriptor.class);
110 }
111
112 public void testSupportsAdditionAndRemoval()
113 {
114 loader = new DirectoryPluginLoader(pluginsTestDir, DEFAULT_PLUGIN_FACTORIES, pluginEventManager);
115 assertTrue(loader.supportsAddition());
116 assertTrue(loader.supportsRemoval());
117 }
118
119 public void testNoFoundPlugins() throws PluginParseException
120 {
121 addTestModuleDecriptors();
122 loader = new DirectoryPluginLoader(pluginsTestDir, DEFAULT_PLUGIN_FACTORIES, pluginEventManager);
123 Collection<Plugin> col = loader.addFoundPlugins(moduleDescriptorFactory);
124 assertFalse(col.isEmpty());
125
126 col = loader.addFoundPlugins(moduleDescriptorFactory);
127 assertTrue(col.isEmpty());
128 }
129
130 public void testFoundPlugin() throws PluginParseException, IOException
131 {
132
133 final File paddington = new File(pluginsTestDir + File.separator + PADDINGTON_JAR);
134 paddington.delete();
135
136 addTestModuleDecriptors();
137 loader = new DirectoryPluginLoader(pluginsTestDir, DEFAULT_PLUGIN_FACTORIES, pluginEventManager);
138 loader.loadAllPlugins(moduleDescriptorFactory);
139
140
141 FileUtils.copyDirectory(pluginsDirectory, pluginsTestDir);
142
143 Collection<Plugin> col = loader.addFoundPlugins(moduleDescriptorFactory);
144 assertEquals(1, col.size());
145
146 col = loader.addFoundPlugins(moduleDescriptorFactory);
147 assertEquals(0, col.size());
148 }
149
150 public void testRemovePlugin() throws PluginException, IOException
151 {
152 addTestModuleDecriptors();
153 loader = new DirectoryPluginLoader(pluginsTestDir, DEFAULT_PLUGIN_FACTORIES, pluginEventManager);
154 final Collection<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
155
156
157
158
159 final Iterator<Plugin> iter = plugins.iterator();
160
161 Plugin paddingtonPlugin = null;
162
163 while (iter.hasNext())
164 {
165 final Plugin plugin = iter.next();
166
167 if (plugin.getName().equals("Test Class Loaded Plugin"))
168 {
169 paddingtonPlugin = plugin;
170 break;
171 }
172 }
173
174 if (paddingtonPlugin == null)
175 {
176 fail("Can't find test plugin 1 (paddington)");
177 }
178
179 loader.removePlugin(paddingtonPlugin);
180 }
181
182 public void testInvalidPluginHandled() throws IOException, PluginParseException
183 {
184 createJarFile("evilplugin.jar", PluginAccessor.Descriptor.FILENAME, pluginsTestDir.getAbsolutePath());
185
186 loader = new DirectoryPluginLoader(pluginsTestDir, DEFAULT_PLUGIN_FACTORIES, pluginEventManager);
187
188 final Collection<Plugin> plugins = loader.loadAllPlugins(moduleDescriptorFactory);
189
190 assertEquals("evil jar wasn't loaded, but other plugins were", pluginsTestDir.list(new FilenameFilter()
191 {
192
193 public boolean accept(final File directory, final String fileName)
194 {
195 return fileName.endsWith(".jar");
196 }
197 }).length, plugins.size());
198
199 assertEquals(1, Iterables.size(Iterables.filter(plugins, new Predicate<Plugin>()
200 {
201 @Override
202 public boolean apply(final Plugin input)
203 {
204 return input instanceof UnloadablePlugin;
205 }
206 })));
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 }