View Javadoc
1   package com.atlassian.plugin.parsers;
2   
3   import com.atlassian.plugin.Application;
4   import com.atlassian.plugin.InstallationMode;
5   import com.atlassian.plugin.ModuleDescriptorFactory;
6   import com.atlassian.plugin.Plugin;
7   import com.atlassian.plugin.PluginArtifact;
8   import com.atlassian.plugin.PluginInformation;
9   import com.atlassian.plugin.PluginParseException;
10  import com.atlassian.plugin.PluginPermission;
11  import com.atlassian.plugin.classloader.PluginClassLoader;
12  import com.atlassian.plugin.impl.DefaultDynamicPlugin;
13  import com.atlassian.plugin.mock.MockAnimalModuleDescriptor;
14  import com.atlassian.plugin.util.ClassLoaderUtils;
15  import com.google.common.collect.ImmutableSet;
16  import com.google.common.collect.Iterables;
17  import com.mockobjects.dynamic.C;
18  import com.mockobjects.dynamic.Mock;
19  import org.junit.Test;
20  
21  import java.io.ByteArrayInputStream;
22  import java.io.File;
23  import java.io.FileInputStream;
24  import java.io.FileNotFoundException;
25  import java.io.InputStream;
26  import java.net.URL;
27  import java.util.Collections;
28  import java.util.Optional;
29  import java.util.Set;
30  
31  import static org.hamcrest.Matchers.empty;
32  import static org.hamcrest.Matchers.hasItem;
33  import static org.hamcrest.Matchers.hasItems;
34  import static org.hamcrest.Matchers.is;
35  import static org.junit.Assert.assertEquals;
36  import static org.junit.Assert.assertFalse;
37  import static org.junit.Assert.assertNotNull;
38  import static org.junit.Assert.assertNull;
39  import static org.junit.Assert.assertSame;
40  import static org.junit.Assert.assertThat;
41  import static org.junit.Assert.assertTrue;
42  import static org.junit.Assert.fail;
43  
44  @SuppressWarnings({"deprecation"}) //suppress deprecation warnings because we still need to test deprecated methods.
45  public class TestXmlDescriptorParser {
46  
47      private static final String MISSING_INFO_TEST_FILE = "test-missing-plugin-info.xml";
48      private static final String DUMMY_PLUGIN_FILE = "pooh-test-plugin.jar";
49  
50      // CONF-12680 Test for missing plugin-info
51      @Test
52      public void testMissingPluginInfo() {
53          // mock up some supporting objects
54          PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
55          Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
56          mockFactory.expect("getModuleDescriptorClass", "unknown-plugin");
57  
58          // create a Plugin for testing
59          Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
60  
61          try {
62              XmlDescriptorParser parser = new XmlDescriptorParser(
63                      new FileInputStream(getTestFile(MISSING_INFO_TEST_FILE)), Collections.emptySet());
64              parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
65  
66              PluginInformation info = testPlugin.getPluginInformation();
67              assertNotNull("Info should not be null", info);
68          } catch (PluginParseException e) {
69              e.printStackTrace();
70              fail("Plugin information parsing should not fail.");
71          } catch (FileNotFoundException e) {
72              e.printStackTrace();
73              // This shouldn't happen
74              fail("Error setting up test");
75          }
76      }
77  
78      // Also CONF-12680 test for missing "essential metadata"
79      @Test
80      public void testPluginsVersion() {
81          String xml = "<atlassian-plugin key=\"foo\" pluginsVersion=\"2\" />";
82          XmlDescriptorParser parser = new XmlDescriptorParser(new ByteArrayInputStream(xml.getBytes()), Collections.emptySet());
83          assertEquals(2, parser.getPluginsVersion());
84      }
85  
86      @Test
87      public void testPluginsVersionAfterConfigure() {
88          XmlDescriptorParser parser = new XmlDescriptorParser(new ByteArrayInputStream("<atlassian-plugin key=\"foo\" plugins-version=\"2\" />".getBytes()), Collections.emptySet());
89          // mock up some supporting objects
90          PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
91          Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
92          mockFactory.expect("getModuleDescriptorClass", "unknown-plugin");
93  
94          // create a Plugin for testing
95          Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
96          parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
97          assertEquals(2, testPlugin.getPluginsVersion());
98      }
99  
100     @Test
101     public void testPluginWithModules() {
102         XmlDescriptorParser parser = parse(null,
103                 "<atlassian-plugin key='foo'>",
104                 "  <animal key='bear' />",
105                 "</atlassian-plugin>");
106         // mock up some supporting objects
107         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
108         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
109         mockFactory.expectAndReturn("getModuleDescriptorClass", C.args(C.eq("animal")), MockAnimalModuleDescriptor.class);
110 
111         // create a Plugin for testing
112         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
113         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
114         assertNotNull(testPlugin.getModuleDescriptor("bear"));
115     }
116 
117     @Test
118     public void testPluginWithModulesNoApplicationKey() {
119         XmlDescriptorParser parser = parse(null,
120                 "<atlassian-plugin key='foo'>",
121                 "  <animal key='bear' application='foo'/>",
122                 "</atlassian-plugin>");
123         // mock up some supporting objects
124         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
125         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
126         mockFactory.expectAndReturn("getModuleDescriptorClass", C.args(C.eq("animal")), MockAnimalModuleDescriptor.class);
127 
128         // create a Plugin for testing
129         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
130         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
131         assertNull(testPlugin.getModuleDescriptor("bear"));
132     }
133 
134     @Test
135     public void testPluginWithSomeNonApplicationModules() {
136         XmlDescriptorParser parser = parse(newApplication("myapp"),
137                 "<atlassian-plugin key='foo'>",
138                 "  <animal key='bear' application='myapp'/>",
139                 "  <animal key='bear2' application='otherapp'/>",
140                 "</atlassian-plugin>");
141         // mock up some supporting objects
142         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
143         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
144         mockFactory.expectAndReturn("getModuleDescriptorClass", C.args(C.eq("animal")), MockAnimalModuleDescriptor.class);
145 
146         // create a Plugin for testing
147         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
148         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
149         assertNotNull(testPlugin.getModuleDescriptor("bear"));
150         assertNull(testPlugin.getModuleDescriptor("bear2"));
151     }
152 
153     @Test
154     public void testPluginWithSomeRestrictionOnModulesNoVersion() {
155         XmlDescriptorParser parser = parse(newApplication("myapp"),
156                 "<atlassian-plugin key='foo'>",
157                 "  <animal key='bear'>",
158                 "    <restrict application='myapp'/>",
159                 "  </animal>",
160                 "  <animal key='bear2' application='otherapp'/>",
161                 "</atlassian-plugin>");
162         // mock up some supporting objects
163         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
164         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
165         mockFactory.expectAndReturn("getModuleDescriptorClass", C.args(C.eq("animal")), MockAnimalModuleDescriptor.class);
166 
167         // create a Plugin for testing
168         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
169         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
170         assertNotNull(testPlugin.getModuleDescriptor("bear"));
171         assertNull(testPlugin.getModuleDescriptor("bear2"));
172     }
173 
174     @Test
175     public void testPluginWithSomeRestrictionOnModulesVersionAsAttribute() {
176         XmlDescriptorParser parser = parse(newApplication("myapp", "2.0"),
177                 "<atlassian-plugin key='foo'>",
178                 "  <animal key='bear'>",
179                 "    <restrict application='myapp' version='[2.0]' />",
180                 "  </animal>",
181                 "  <animal key='bear2'>",
182                 "    <restrict application='myapp' version='[1.0]' />",
183                 "  </animal>",
184                 "</atlassian-plugin>");
185         // mock up some supporting objects
186         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
187         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
188         mockFactory.matchAndReturn("getModuleDescriptorClass", C.args(C.eq("animal")), MockAnimalModuleDescriptor.class);
189 
190         // create a Plugin for testing
191         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
192         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
193         assertNotNull(testPlugin.getModuleDescriptor("bear"));
194         assertNull(testPlugin.getModuleDescriptor("bear2"));
195     }
196 
197     @Test
198     public void testPluginWithSomeRestrictionOnModulesVersionAsElement() {
199         XmlDescriptorParser parser = parse(newApplication("myapp", "2.0"),
200                 "<atlassian-plugin key='foo'>",
201                 "  <animal key='bear'>",
202                 "    <restrict application='myapp'>",
203                 "      <version>(,3.0)</version>",
204                 "      <version>[5.0,)</version>",
205                 "    </restrict>",
206                 "  </animal>",
207                 "  <animal key='bear2'>",
208                 "    <restrict application='myapp'>",
209                 "      <version>(,2.0)</version>",
210                 "      <version>[5.0,)</version>",
211                 "    </restrict>",
212                 "  </animal>",
213                 "</atlassian-plugin>");
214         // mock up some supporting objects
215         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
216         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
217         mockFactory.matchAndReturn("getModuleDescriptorClass", C.args(C.eq("animal")), MockAnimalModuleDescriptor.class);
218 
219         // create a Plugin for testing
220         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
221         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
222         assertNotNull(testPlugin.getModuleDescriptor("bear"));
223         assertNull(testPlugin.getModuleDescriptor("bear2"));
224     }
225 
226     @Test
227     public void testPluginWithSystemAttribute() {
228         XmlDescriptorParser parser = parse(null,
229                 "<atlassian-plugin key='foo' system='true'>",
230                 "</atlassian-plugin>");
231 
232         // mock up some supporting objects
233         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
234         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
235         mockFactory.expectAndReturn("getModuleDescriptorClass", C.args(C.eq("animal")), MockAnimalModuleDescriptor.class);
236 
237         // create a Plugin for testing
238         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
239         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
240         // PLUG-415 Plugins2 plugins now need to be able to be declared as system.
241         assertTrue("This plugin should be a system plugin - bundled plugins2 plugins are system plugins.", testPlugin.isSystemPlugin());
242     }
243 
244     @Test
245     public void testPluginWithoutSystemAttribute() {
246         XmlDescriptorParser parser = parse(null,
247                 "<atlassian-plugin key='foo' >",
248                 "</atlassian-plugin>");
249 
250         // mock up some supporting objects
251         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
252         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
253         mockFactory.expectAndReturn("getModuleDescriptorClass", C.args(C.eq("animal")), MockAnimalModuleDescriptor.class);
254 
255         // create a Plugin for testing
256         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
257         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
258         assertFalse("This plugin should not be a system plugin.", testPlugin.isSystemPlugin());
259     }
260 
261     @Test
262     public void testPluginsVersionWithDash() {
263         String xml = "<atlassian-plugin key=\"foo\" plugins-version=\"2\" />";
264         XmlDescriptorParser parser = new XmlDescriptorParser(new ByteArrayInputStream(xml.getBytes()), Collections.emptySet());
265         assertEquals(2, parser.getPluginsVersion());
266     }
267 
268     @Test
269     public void testPluginsVersionMissing() {
270         String xml = "<atlassian-plugin key=\"foo\" />";
271         XmlDescriptorParser parser = new XmlDescriptorParser(new ByteArrayInputStream(xml.getBytes()), Collections.emptySet());
272         assertEquals(1, parser.getPluginsVersion());
273     }
274 
275     @Test
276     public void testPluginsResourcesAvailableToModuleDescriptors() {
277         XmlDescriptorParser parser = parse(null,
278                 "<atlassian-plugin key='foo'>",
279                 "  <resource type='velocity' name='edit'>Show an input box here.</resource>",
280                 "  <animal key='bear' />",
281                 "</atlassian-plugin>");
282         // mock up some supporting objects
283         PluginClassLoader classLoader = new PluginClassLoader(new File(getTestFile("ap-plugins") + "/" + DUMMY_PLUGIN_FILE));
284         Mock mockFactory = new Mock(ModuleDescriptorFactory.class);
285         MockAnimalModuleDescriptor descriptor = new MockAnimalModuleDescriptor("velocity", "edit");
286         mockFactory.expectAndReturn("getModuleDescriptor", C.args(C.eq("animal")), descriptor);
287 
288         // create a Plugin for testing
289         Plugin testPlugin = new DefaultDynamicPlugin((PluginArtifact) new Mock(PluginArtifact.class).proxy(), classLoader);
290         parser.configurePlugin((ModuleDescriptorFactory) mockFactory.proxy(), testPlugin);
291         assertNotNull(testPlugin.getModuleDescriptor("bear"));
292 
293         mockFactory.verify();
294     }
295 
296     @Test
297     public void testPluginPermissionsIsAllPermissionsForPluginsWithNoVersionByDefault() {
298         XmlDescriptorParser parser = parse(null,
299                 "<atlassian-plugin key='foo'>",
300                 "  <plugin-info>",
301                 "  </plugin-info>",
302                 "</atlassian-plugin>");
303 
304         final Set<PluginPermission> parsedPermissions = parser.getPluginInformation().getPermissions();
305         assertEquals(1, parsedPermissions.size());
306         assertSame(PluginPermission.ALL, Iterables.get(parsedPermissions, 0));
307     }
308 
309     @Test
310     public void testPluginPermissionsIsAllPermissionsForPlugins1ByDefault() {
311         XmlDescriptorParser parser = parse(null,
312                 "<atlassian-plugin key='foo' plugins-version='1'>",
313                 "  <plugin-info>",
314                 "  </plugin-info>",
315                 "</atlassian-plugin>");
316 
317         final Set<PluginPermission> parsedPermissions = parser.getPluginInformation().getPermissions();
318         assertEquals(1, parsedPermissions.size());
319         assertSame(PluginPermission.ALL, Iterables.get(parsedPermissions, 0));
320     }
321 
322     @Test
323     public void testPluginPermissionsIsAllPermissionsForPlugins2ByDefault() {
324         XmlDescriptorParser parser = parse(null,
325                 "<atlassian-plugin key='foo' plugins-version='2'>",
326                 "  <plugin-info>",
327                 "  </plugin-info>",
328                 "</atlassian-plugin>");
329 
330         final Set<PluginPermission> parsedPermissions = parser.getPluginInformation().getPermissions();
331         assertEquals(1, parsedPermissions.size());
332         assertSame(PluginPermission.ALL, Iterables.get(parsedPermissions, 0));
333     }
334 
335     @Test
336     public void testPluginPermissionsIsFilteredByApplications() {
337         XmlDescriptorParser parser = parse(newApplication("my-app"),
338                 "<atlassian-plugin key='foo' plugins-version='2'>",
339                 "  <plugin-info>",
340                 "    <permissions>",
341                 "      <permission application='my-other-app'>some_permission</permission>",
342                 "      <permission application='my-app'>some_other_permission</permission>",
343                 "      <permission application='my-other-app' installation-mode='local'>yet_another_permission</permission>",
344                 "    </permissions>",
345                 "  </plugin-info>",
346                 "</atlassian-plugin>");
347 
348         final Set<PluginPermission> parsedPermissions = parser.getPluginInformation().getPermissions();
349         assertEquals(1, parsedPermissions.size());
350         assertEquals("some_other_permission", Iterables.get(parsedPermissions, 0).getName());
351     }
352 
353     @Test
354     public void testPluginPermissions() {
355         XmlDescriptorParser parser = parse(newApplication("my-other-app"),
356                 "<atlassian-plugin key='foo' plugins-version='2'>",
357                 "  <plugin-info>",
358                 "    <permissions>",
359                 "      <permission>some_permission</permission>",
360                 "      <permission application='my-app'>some_other_permission</permission>",
361                 "      <permission installation-mode='local'>yet_another_permission</permission>",
362                 "    </permissions>",
363                 "  </plugin-info>",
364                 "</atlassian-plugin>");
365 
366         final Set<PluginPermission> parsedPermissions = parser.getPluginInformation().getPermissions();
367         assertEquals(2, parsedPermissions.size());
368 
369         final PluginPermission firstPermission = Iterables.get(parsedPermissions, 0);
370         assertEquals("some_permission", firstPermission.getName());
371         assertEquals(Optional.empty(), firstPermission.getInstallationMode());
372 
373         final PluginPermission secondPermission = Iterables.get(parsedPermissions, 1);
374         assertEquals("yet_another_permission", secondPermission.getName());
375         assertEquals(Optional.of(InstallationMode.LOCAL), secondPermission.getInstallationMode());
376     }
377 
378     @Test
379     public void testPluginPermissionsIsEmptyForRemotablePluginByDefault() {
380         XmlDescriptorParser parser = parse(null,
381                 "<atlassian-plugin key='foo' plugins-version='3'>",
382                 "  <plugin-info>",
383                 "  </plugin-info>",
384                 "</atlassian-plugin>");
385 
386         assertTrue(parser.getPluginInformation().getPermissions().isEmpty());
387     }
388 
389     @Test
390     public void testPluginPermissionsIsEmptyForBlankPermissions() {
391         XmlDescriptorParser parser = parse(null,
392                 "<atlassian-plugin key='foo' plugins-version='3'>",
393                 "  <plugin-info>",
394                 "    <permissions>",
395                 "      <permission> \t </permission>",
396                 "      <permission></permission>",
397                 "    </permissions>",
398                 "  </plugin-info>",
399                 "</atlassian-plugin>");
400 
401         assertTrue(parser.getPluginInformation().getPermissions().isEmpty());
402     }
403 
404     @Test
405     public void testStartupIsParsed() {
406         final XmlDescriptorParser parser = parse(null,
407                 "<atlassian-plugin>",
408                 "  <plugin-info>",
409                 "    <startup>early</startup>",
410                 "  </plugin-info>",
411                 "</atlassian-plugin>");
412 
413         assertEquals(parser.getPluginInformation().getStartup(), "early");
414     }
415 
416     @Test
417     public void testNoScanFolderWhenNoScanModulesElementIsProvided() {
418         final XmlDescriptorParser parser = parse(null,
419                 "<atlassian-plugin>",
420                 "  <plugin-info>",
421                 "  </plugin-info>",
422                 "</atlassian-plugin>");
423 
424         assertThat(parser.getPluginInformation().getModuleScanFolders(), is(empty()));
425     }
426 
427     @Test
428     public void testDefaultScanFolderWhenNoFolderProvided() {
429         final XmlDescriptorParser parser = parse(null,
430                 "<atlassian-plugin>",
431                 "  <plugin-info>",
432                 "    <scan-modules/>",
433                 "  </plugin-info>",
434                 "</atlassian-plugin>");
435 
436         assertThat(parser.getPluginInformation().getModuleScanFolders(), hasItem("META-INF/atlassian"));
437     }
438 
439     @Test
440     public void testDefaultScanFolderWhenFoldersAreProvided() {
441         final XmlDescriptorParser parser = parse(null,
442                 "<atlassian-plugin>",
443                 "  <plugin-info>",
444                 "    <scan-modules>",
445                 "      <folder>folder1</folder>",
446                 "      <folder>folder2</folder>",
447                 "    </scan-modules>",
448                 "  </plugin-info>",
449                 "</atlassian-plugin>");
450 
451         assertThat(parser.getPluginInformation().getModuleScanFolders(), hasItems("folder1", "folder2"));
452     }
453 
454 
455     private String getTestFile(String filename) {
456         final URL url = ClassLoaderUtils.getResource(filename, this.getClass());
457         return url.getFile();
458     }
459 
460     private static XmlDescriptorParser parse(Application application, String... lines) {
461         StringBuilder sb = new StringBuilder();
462         for (String line : lines) {
463             sb.append(line.replace('\'', '"')).append('\n');
464         }
465         InputStream in = new ByteArrayInputStream(sb.toString().getBytes());
466         return new XmlDescriptorParser(in, Optional.ofNullable(application)
467                 .map(ImmutableSet::of)
468                 .orElseGet(ImmutableSet::of));
469     }
470 
471     private Application newApplication(final String appKey) {
472         return newApplication(appKey, null);
473     }
474 
475     private Application newApplication(final String appKey, final String version) {
476         return new Application() {
477             @Override
478             public String getKey() {
479                 return appKey;
480             }
481 
482             @Override
483             public String getVersion() {
484                 return version;
485             }
486 
487             @Override
488             public String getBuildNumber() {
489                 return null;
490             }
491         };
492     }
493 }