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