View Javadoc
1   /*
2    * Created by IntelliJ IDEA.
3    * User: Mike
4    * Date: Jul 29, 2004
5    * Time: 4:28:18 PM
6    */
7   package com.atlassian.plugin.descriptors;
8   
9   import com.atlassian.plugin.ModuleDescriptor;
10  import com.atlassian.plugin.ModulePermissionException;
11  import com.atlassian.plugin.Permissions;
12  import com.atlassian.plugin.Plugin;
13  import com.atlassian.plugin.PluginParseException;
14  import com.atlassian.plugin.RequirePermission;
15  import com.atlassian.plugin.StateAware;
16  import com.atlassian.plugin.elements.ResourceDescriptor;
17  import com.atlassian.plugin.impl.StaticPlugin;
18  import com.atlassian.plugin.mock.MockAnimal;
19  import com.atlassian.plugin.mock.MockAnimalModuleDescriptor;
20  import com.atlassian.plugin.mock.MockMineral;
21  import com.atlassian.plugin.module.ModuleFactory;
22  import com.atlassian.plugin.util.ClassLoaderUtils;
23  import com.google.common.collect.ImmutableSet;
24  import org.dom4j.DocumentException;
25  import org.dom4j.DocumentHelper;
26  import org.dom4j.Element;
27  import org.junit.Test;
28  
29  import java.util.List;
30  import java.util.Optional;
31  import java.util.Set;
32  import java.util.concurrent.atomic.AtomicBoolean;
33  
34  import static java.lang.Boolean.FALSE;
35  import static org.hamcrest.CoreMatchers.equalTo;
36  import static org.hamcrest.CoreMatchers.is;
37  import static org.junit.Assert.assertEquals;
38  import static org.junit.Assert.assertNotNull;
39  import static org.junit.Assert.assertNull;
40  import static org.junit.Assert.assertSame;
41  import static org.junit.Assert.assertThat;
42  import static org.junit.Assert.assertTrue;
43  import static org.junit.Assert.fail;
44  import static org.mockito.Mockito.mock;
45  import static org.mockito.Mockito.never;
46  import static org.mockito.Mockito.spy;
47  import static org.mockito.Mockito.verify;
48  import static org.mockito.Mockito.when;
49  
50  public class TestAbstractModuleDescriptor {
51      public static final String ANNOTATION_PERMISSION = "annotation_permission";
52  
53      @Test
54      public void testAssertModuleClassImplements() throws DocumentException, PluginParseException {
55          ModuleDescriptor descriptor = new AbstractModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY) {
56              public void init(Plugin plugin, Element element) throws PluginParseException {
57                  super.init(plugin, element);
58                  enabled();
59                  assertModuleClassImplements(MockMineral.class);
60              }
61  
62              public Object getModule() {
63                  return null;
64              }
65          };
66  
67          try {
68              descriptor.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\" />").getRootElement());
69              ((StateAware) descriptor).enabled();
70              fail("Should have blown up.");
71          } catch (PluginParseException e) {
72              assertEquals("Given module class: com.atlassian.plugin.mock.MockBear does not implement com.atlassian.plugin.mock.MockMineral", e.getMessage());
73          }
74  
75          // now succeed
76          descriptor.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockGold\" />").getRootElement());
77      }
78  
79      @Test
80      public void testLoadClassFromNewModuleFactory() {
81          ModuleFactory moduleFactory = mock(ModuleFactory.class);
82          AbstractModuleDescriptor moduleDescriptor = new StringModuleDescriptor(moduleFactory, "foo");
83          Plugin plugin = mock(Plugin.class);
84          moduleDescriptor.loadClass(plugin, "foo");
85          assertEquals(String.class, moduleDescriptor.getModuleClass());
86      }
87  
88      @Test
89      public void testLoadClassFromNewModuleFactoryWithExtendsNumberType() {
90          ModuleFactory moduleFactory = mock(ModuleFactory.class);
91          AbstractModuleDescriptor moduleDescriptor = new ExtendsNumberModuleDescriptor(moduleFactory, "foo");
92          Plugin plugin = mock(Plugin.class);
93  
94          try {
95              moduleDescriptor.loadClass(plugin, "foo");
96              fail("Should have complained about extends type");
97          } catch (IllegalStateException ex) {
98              // success
99          }
100     }
101 
102     @Test
103     public void testLoadClassFromNewModuleFactoryWithExtendsNothingType() {
104         ModuleFactory moduleFactory = mock(ModuleFactory.class);
105         AbstractModuleDescriptor moduleDescriptor = new ExtendsNothingModuleDescriptor(moduleFactory, "foo");
106         Plugin plugin = mock(Plugin.class);
107 
108         try {
109             moduleDescriptor.loadClass(plugin, "foo");
110             fail("Should have complained about extends type");
111         } catch (IllegalStateException ex) {
112             // success
113         }
114     }
115 
116     @Test
117     public void testGetModuleReturnClass() {
118         AbstractModuleDescriptor desc = new MockAnimalModuleDescriptor();
119         assertEquals(MockAnimal.class, desc.getModuleReturnClass());
120     }
121 
122     @Test
123     public void testGetModuleReturnClassWithExtendsNumber() {
124         ModuleFactory moduleFactory = mock(ModuleFactory.class);
125         AbstractModuleDescriptor moduleDescriptor = new ExtendsNothingModuleDescriptor(moduleFactory, "foo");
126         assertEquals(Object.class, moduleDescriptor.getModuleReturnClass());
127     }
128 
129     @Test
130     public void testLoadClassFromNewModuleFactoryButUnknownType() {
131         ModuleFactory moduleFactory = mock(ModuleFactory.class);
132         AbstractModuleDescriptor moduleDescriptor = new AbstractModuleDescriptor(moduleFactory) {
133             public AbstractModuleDescriptor init() {
134                 moduleClassName = "foo";
135                 return this;
136             }
137 
138             @Override
139             public Object getModule() {
140                 return null;
141             }
142         }.init();
143         try {
144             Plugin plugin = mock(Plugin.class);
145             moduleDescriptor.loadClass(plugin, "foo");
146             fail("Should have complained about unknown type");
147         } catch (IllegalStateException ex) {
148             // success
149         }
150     }
151 
152     @Test
153     public void testSingletonness() throws DocumentException, PluginParseException {
154         ModuleDescriptor descriptor = makeSingletonDescriptor();
155 
156         // try a default descriptor with no singleton="" element. Should _be_ a singleton
157         descriptor.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\" />").getRootElement());
158         ((StateAware) descriptor).enabled();
159         Object module = descriptor.getModule();
160         assertTrue(module == descriptor.getModule());
161 
162         // now try a default descriptor with singleton="true" element. Should still be a singleton
163         descriptor = makeSingletonDescriptor();
164         descriptor.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\" singleton=\"true\" />").getRootElement());
165         ((StateAware) descriptor).enabled();
166         module = descriptor.getModule();
167         assertTrue(module == descriptor.getModule());
168 
169         // now try reiniting as a non-singleton
170         descriptor = makeSingletonDescriptor();
171         descriptor.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\" singleton=\"false\" />").getRootElement());
172         ((StateAware) descriptor).enabled();
173         module = descriptor.getModule();
174         assertTrue(module != descriptor.getModule());
175     }
176 
177     @Test
178     public void testGetResourceDescriptor() throws DocumentException, PluginParseException {
179         ModuleDescriptor descriptor = makeSingletonDescriptor();
180         descriptor.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\">" +
181                 "<resource type='velocity' name='view' location='foo' />" +
182                 "</animal>").getRootElement());
183 
184         assertNull(descriptor.getResourceLocation("foo", "bar"));
185         assertNull(descriptor.getResourceLocation("velocity", "bar"));
186         assertNull(descriptor.getResourceLocation("foo", "view"));
187         assertEquals(new ResourceDescriptor(DocumentHelper.parseText("<resource type='velocity' name='view' location='foo' />").getRootElement()).getResourceLocationForName("view").getLocation(), descriptor.getResourceLocation("velocity", "view").getLocation());
188     }
189 
190     @Test
191     public void testGetResourceDescriptorByType() throws DocumentException, PluginParseException {
192         ModuleDescriptor descriptor = makeSingletonDescriptor();
193         descriptor.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\">" +
194                 "<resource type='velocity' name='view' location='foo' />" +
195                 "<resource type='velocity' name='input-params' location='bar' />" +
196                 "</animal>").getRootElement());
197 
198         final List resourceDescriptors = descriptor.getResourceDescriptors("velocity");
199         assertNotNull(resourceDescriptors);
200         assertEquals(2, resourceDescriptors.size());
201 
202         ResourceDescriptor resourceDescriptor = (ResourceDescriptor) resourceDescriptors.get(0);
203         assertEquals(new ResourceDescriptor(DocumentHelper.parseText("<resource type='velocity' name='view' location='foo' />").getRootElement()), resourceDescriptor);
204 
205         resourceDescriptor = (ResourceDescriptor) resourceDescriptors.get(1);
206         assertEquals(new ResourceDescriptor(DocumentHelper.parseText("<resource type='velocity' name='input-params' location='bar' />").getRootElement()), resourceDescriptor);
207     }
208 
209     @Test
210     public void testDestroy() {
211         AbstractModuleDescriptor descriptor = spy(new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, null));
212         Plugin plugin = mock(Plugin.class);
213         descriptor.setPlugin(plugin);
214         // enable the module descriptor first; otherwise, disabled() won't be called from destroy()
215         descriptor.enabled();
216 
217         descriptor.destroy();
218 
219         verify(descriptor).disabled();
220         assertSame(plugin, descriptor.getPlugin());
221     }
222 
223     @Test
224     public void testDestroyWhenDisabled() {
225         AbstractModuleDescriptor descriptor = spy(new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, null));
226         Plugin plugin = mock(Plugin.class);
227         descriptor.setPlugin(plugin);
228 
229         descriptor.destroy();
230 
231         // it's never enabled, so disabled() won't be called
232         verify(descriptor, never()).disabled();
233         assertSame(plugin, descriptor.getPlugin());
234     }
235 
236     @Test
237     public void testDestroyDelegatesToDeprecatedOverride() {
238         final AtomicBoolean called = new AtomicBoolean();
239 
240         AbstractModuleDescriptor descriptor = new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, null) {
241             @Override
242             @Deprecated
243             public void destroy(Plugin plugin) {
244                 called.set(true);
245             }
246         };
247 
248         Plugin plugin = mock(Plugin.class);
249         descriptor.setPlugin(plugin);
250 
251         descriptor.destroy();
252 
253         assertTrue(called.get());
254     }
255 
256     @Test
257     public void testCheckPermissionsForModuleDescriptorAndPluginDefinedWithNoPermission() {
258         final AbstractModuleDescriptor moduleDescriptor = makeSingletonDescriptor();
259         moduleDescriptor.setPlugin(mock(Plugin.class));
260 
261         try {
262             moduleDescriptor.checkPermissions();
263         } catch (ModulePermissionException e) {
264             fail("Didn't expected the exception here" + e);
265         }
266     }
267 
268     @Test
269     public void testCheckPermissionsForModuleDescriptorAndPluginWithSamePermission() {
270         final String somePermission = "some_permission";
271         final AbstractModuleDescriptor moduleDescriptor = new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, this.getClass().getName()) {
272             @Override
273             protected Set<String> getRequiredPermissions() {
274                 return ImmutableSet.of(somePermission);
275             }
276         };
277 
278         final Plugin plugin = mock(Plugin.class);
279         moduleDescriptor.setPlugin(plugin);
280         when(plugin.getActivePermissions()).thenReturn(ImmutableSet.of(somePermission, ANNOTATION_PERMISSION));
281 
282         try {
283             moduleDescriptor.checkPermissions();
284         } catch (ModulePermissionException e) {
285             fail("Didn't expected the exception here" + e);
286         }
287     }
288 
289     @Test
290     public void testCheckPermissionsForModuleDescriptorWithPermissionsAndPluginWithAllPermission() {
291         final Set<String> permissions = ImmutableSet.of("some_permission");
292         final AbstractModuleDescriptor moduleDescriptor = new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, this.getClass().getName()) {
293             @Override
294             protected Set<String> getRequiredPermissions() {
295                 return permissions;
296             }
297         };
298 
299         final Plugin plugin = mock(Plugin.class);
300         moduleDescriptor.setPlugin(plugin);
301         when(plugin.hasAllPermissions()).thenReturn(true);
302         when(plugin.getActivePermissions()).thenReturn(ImmutableSet.of(Permissions.ALL_PERMISSIONS));
303 
304         try {
305             moduleDescriptor.checkPermissions();
306         } catch (ModulePermissionException e) {
307             fail("Didn't expected the exception here" + e);
308         }
309     }
310 
311     @Test
312     public void testCheckPermissionsForModuleDescriptorWithPermissionsNotInPluginPermissions() {
313         final Set<String> permissions = ImmutableSet.of("some_module_permission", "one_plugin_permission");
314         final AbstractModuleDescriptor moduleDescriptor = new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, this.getClass().getName()) {
315             @Override
316             protected Set<String> getRequiredPermissions() {
317                 return permissions;
318             }
319         };
320 
321         final Plugin plugin = mock(Plugin.class);
322         moduleDescriptor.setPlugin(plugin);
323         ImmutableSet<String> pluginPermissions = ImmutableSet.of("one_plugin_permission", "two_plugin_permission", ANNOTATION_PERMISSION);
324         when(plugin.getActivePermissions()).thenReturn(pluginPermissions);
325 
326         try {
327             moduleDescriptor.checkPermissions();
328             fail("We expected some exception here");
329         } catch (ModulePermissionException e) {
330             assertEquals(ImmutableSet.of("some_module_permission"), e.getPermissions());
331         }
332     }
333 
334     @Test
335     public void testCheckPermissionsForModuleDescriptorWithPermissionsAllInPluginPermissions() {
336         final Set<String> permissions = ImmutableSet.of("two_plugin_permission", "one_plugin_permission");
337         final AbstractModuleDescriptor moduleDescriptor = new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, this.getClass().getName()) {
338             @Override
339             protected Set<String> getRequiredPermissions() {
340                 return permissions;
341             }
342         };
343 
344         final Plugin plugin = mock(Plugin.class);
345         moduleDescriptor.setPlugin(plugin);
346         ImmutableSet<String> pluginPermissions = ImmutableSet.of("one_plugin_permission", "two_plugin_permission", "three_plugin_permission", ANNOTATION_PERMISSION);
347         when(plugin.getActivePermissions()).thenReturn(pluginPermissions);
348 
349         moduleDescriptor.checkPermissions();
350     }
351 
352     @Test
353     public void setModuleIsNotBrokenAfterConstruction() {
354         final ModuleDescriptor<String> md = newStringModuleDescriptor();
355         assertThat(md.isBroken(), is(false));
356     }
357 
358     @Test
359     public void setBrokenMakesModuleBroken() {
360         final ModuleDescriptor<String> md = newStringModuleDescriptor();
361         md.setBroken();
362         assertThat(md.isBroken(), is(true));
363     }
364 
365     @Test
366     public void enableClearesBrokenState() throws Exception {
367         final StringModuleDescriptor md = newStringModuleDescriptor();
368         md.init(new StaticPlugin(), DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\" />").getRootElement());
369         md.setBroken();
370         md.enabled();
371         assertThat(md.isBroken(), is(false));
372     }
373 
374     @Test
375     public void verifyInheritingEmptyScopeFromPlugin() throws Exception {
376         StaticPlugin plugin = new StaticPlugin() {
377             public Optional<String> getScopeKey() {
378                 return Optional.empty();
379             }
380         };
381         ModuleDescriptor descriptor = makeSingletonDescriptor();
382         descriptor.init(plugin, DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\"/>").getRootElement());
383 
384         assertThat(descriptor.getScopeKey().isPresent(), equalTo(FALSE));
385     }
386 
387     @Test
388     public void verifyInheritingPresentScopeFromPlugin() throws Exception {
389         StaticPlugin plugin = new StaticPlugin() {
390             public Optional<String> getScopeKey() {
391                 return Optional.of("licenses/jira-service-desk");
392             }
393         };
394         ModuleDescriptor descriptor = makeSingletonDescriptor();
395         descriptor.init(plugin, DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" class=\"com.atlassian.plugin.mock.MockBear\"/>").getRootElement());
396 
397         assertThat(descriptor.getScopeKey().get(), equalTo("licenses/jira-service-desk"));
398     }
399 
400     @Test
401     public void scopeIsEmptyWhenModuleDescopedAndPluginIsScoped() throws Exception {
402         StaticPlugin plugin = new StaticPlugin() {
403             public Optional<String> getScopeKey() {
404                 return Optional.of("licenses/jira-service-desk");
405             }
406         };
407         ModuleDescriptor descriptor = makeSingletonDescriptor();
408         descriptor.init(plugin, DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" scoped=\"false\" class=\"com.atlassian.plugin.mock.MockBear\"/>").getRootElement());
409 
410         assertThat(descriptor.getScopeKey().isPresent(), equalTo(FALSE));
411     }
412 
413     @Test
414     public void scopeIsDerivedFromPluginWhenModuleIsScoped() throws Exception {
415         StaticPlugin plugin = new StaticPlugin() {
416             public Optional<String> getScopeKey() {
417                 return Optional.of("licenses/jira-service-desk");
418             }
419         };
420         ModuleDescriptor descriptor = makeSingletonDescriptor();
421         descriptor.init(plugin, DocumentHelper.parseText("<animal key=\"key\" name=\"bear\" scoped=\"true\" class=\"com.atlassian.plugin.mock.MockBear\"/>").getRootElement());
422 
423         assertThat(descriptor.getScopeKey().get(), equalTo("licenses/jira-service-desk"));
424     }
425 
426     private StringModuleDescriptor newStringModuleDescriptor() {
427         return new StringModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY, this.getClass().getName());
428     }
429 
430     private AbstractModuleDescriptor makeSingletonDescriptor() {
431         AbstractModuleDescriptor descriptor = new AbstractModuleDescriptor(ModuleFactory.LEGACY_MODULE_FACTORY) {
432             Object module;
433 
434             public void init(Plugin plugin, Element element) throws PluginParseException {
435                 super.init(plugin, element);
436             }
437 
438             public Object getModule() {
439                 try {
440                     if (!isSingleton()) {
441                         return ClassLoaderUtils.loadClass(getModuleClass().getName(), TestAbstractModuleDescriptor.class).newInstance();
442                     } else {
443                         if (module == null) {
444                             module = ClassLoaderUtils.loadClass(getModuleClass().getName(), TestAbstractModuleDescriptor.class).newInstance();
445                         }
446 
447                         return module;
448                     }
449                 } catch (Exception e) {
450                     throw new RuntimeException("What happened Dave?");
451                 }
452             }
453         };
454         return descriptor;
455     }
456 
457     @RequirePermission(ANNOTATION_PERMISSION)
458     private static class StringModuleDescriptor extends AbstractModuleDescriptor<String> {
459         public StringModuleDescriptor(ModuleFactory moduleFactory, String className) {
460             super(moduleFactory);
461             moduleClassName = className;
462         }
463 
464         @Override
465         public String getModule() {
466             return null;
467         }
468 
469     }
470 
471     private static class ExtendsNumberModuleDescriptor<T extends Number> extends AbstractModuleDescriptor<T> {
472         public ExtendsNumberModuleDescriptor(ModuleFactory moduleFactory, String className) {
473             super(moduleFactory);
474             moduleClassName = className;
475         }
476 
477         @Override
478         public T getModule() {
479             return null;
480         }
481     }
482 
483     private static class ExtendsNothingModuleDescriptor<T> extends AbstractModuleDescriptor<T> {
484         public ExtendsNothingModuleDescriptor(ModuleFactory moduleFactory, String className) {
485             super(moduleFactory);
486             moduleClassName = className;
487         }
488 
489         @Override
490         public T getModule() {
491             return null;
492         }
493     }
494 }