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