View Javadoc

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")) // asserts for first 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")) // asserts for second plugin
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         //delete paddington for the timebeing
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         //restore paddington to test plugins dir
141         FileUtils.copyDirectory(pluginsDirectory, pluginsTestDir);
142 
143         Collection<Plugin> col = loader.addFoundPlugins(moduleDescriptorFactory);
144         assertEquals(1, col.size());
145         // next time we shouldn't find any new plugins
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         //duplicate the paddington plugin before removing the original
157         //the duplicate will be used to restore the deleted original after the test
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         // sleep to ensure the new plugin is picked up
224         Thread.sleep(1000);
225 
226         plugin.delete(); //delete the old plugin artifact to make windows happy
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 }