View Javadoc

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