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