View Javadoc
1   package com.atlassian.plugin.osgi.factory;
2   
3   import com.atlassian.plugin.JarPluginArtifact;
4   import com.atlassian.plugin.ModuleDescriptor;
5   import com.atlassian.plugin.ModuleDescriptorFactory;
6   import com.atlassian.plugin.Plugin;
7   import com.atlassian.plugin.PluginAccessor;
8   import com.atlassian.plugin.PluginInformation;
9   import com.atlassian.plugin.PluginParseException;
10  import com.atlassian.plugin.XmlPluginArtifact;
11  import com.atlassian.plugin.event.impl.DefaultPluginEventManager;
12  import com.atlassian.plugin.osgi.container.OsgiContainerManager;
13  import com.atlassian.plugin.osgi.container.impl.DefaultOsgiPersistentCache;
14  import com.atlassian.plugin.osgi.hostcomponents.HostComponentRegistration;
15  import com.atlassian.plugin.test.PluginJarBuilder;
16  import com.atlassian.plugin.test.PluginTestUtils;
17  import com.google.common.collect.ImmutableList;
18  import com.google.common.collect.ImmutableMap;
19  import com.mockobjects.dynamic.C;
20  import com.mockobjects.dynamic.Mock;
21  import org.apache.commons.io.FileUtils;
22  import org.dom4j.Element;
23  import org.junit.After;
24  import org.junit.Before;
25  import org.junit.Test;
26  import org.osgi.framework.Bundle;
27  import org.osgi.framework.BundleContext;
28  import org.osgi.framework.Constants;
29  import org.osgi.service.packageadmin.PackageAdmin;
30  import org.osgi.util.tracker.ServiceTracker;
31  
32  import java.io.File;
33  import java.io.IOException;
34  import java.util.Collections;
35  import java.util.Dictionary;
36  import java.util.Hashtable;
37  
38  import static com.atlassian.plugin.ReferenceMode.FORBID_REFERENCE;
39  import static com.atlassian.plugin.ReferenceMode.PERMIT_REFERENCE;
40  import static org.hamcrest.Matchers.instanceOf;
41  import static org.hamcrest.Matchers.is;
42  import static org.hamcrest.Matchers.nullValue;
43  import static org.hamcrest.Matchers.sameInstance;
44  import static org.hamcrest.core.IsEqual.equalTo;
45  import static org.junit.Assert.assertEquals;
46  import static org.junit.Assert.assertNotNull;
47  import static org.junit.Assert.assertNull;
48  import static org.junit.Assert.assertThat;
49  import static org.junit.Assert.assertTrue;
50  import static org.junit.Assert.fail;
51  import static org.mockito.ArgumentMatchers.any;
52  import static org.mockito.ArgumentMatchers.eq;
53  import static org.mockito.Mockito.mock;
54  import static org.mockito.Mockito.verify;
55  import static org.mockito.Mockito.when;
56  
57  public class TestOsgiPluginFactory {
58      OsgiPluginFactory factory;
59  
60      private File tmpDir;
61      private File jar;
62      private OsgiContainerManager osgiContainerManager;
63      private Mock mockBundle;
64      private Mock mockSystemBundle;
65  
66      @Before
67      public void setUp() throws IOException {
68          tmpDir = PluginTestUtils.createTempDirectory(TestOsgiPluginFactory.class);
69          osgiContainerManager = mock(OsgiContainerManager.class);
70  
71          final ServiceTracker tracker = mock(ServiceTracker.class);
72          when(tracker.getServices()).thenReturn(new Object[0]);
73          when(osgiContainerManager.getServiceTracker(ModuleDescriptorFactory.class.getName())).thenReturn(tracker);
74  
75          factory = new OsgiPluginFactory(PluginAccessor.Descriptor.FILENAME, Collections.emptySet(), new DefaultOsgiPersistentCache(tmpDir), osgiContainerManager, new DefaultPluginEventManager());
76          jar = new PluginJarBuilder("someplugin").addPluginInformation("plugin.key", "My Plugin", "1.0").build();
77  
78          mockBundle = new Mock(Bundle.class);
79          final Dictionary<String, String> dict = new Hashtable<>();
80          dict.put(Constants.BUNDLE_DESCRIPTION, "desc");
81          dict.put(Constants.BUNDLE_VERSION, "1.0");
82          mockBundle.matchAndReturn("getHeaders", dict);
83  
84          mockSystemBundle = new Mock(Bundle.class);
85          final Dictionary<String, String> sysDict = new Hashtable<>();
86          sysDict.put(Constants.BUNDLE_DESCRIPTION, "desc");
87          sysDict.put(Constants.BUNDLE_VERSION, "1.0");
88          mockSystemBundle.matchAndReturn("getHeaders", sysDict);
89          mockSystemBundle.matchAndReturn("getLastModified", System.currentTimeMillis());
90          mockSystemBundle.matchAndReturn("getSymbolicName", "system.bundle");
91  
92          final Mock mockSysContext = new Mock(BundleContext.class);
93          mockSystemBundle.matchAndReturn("getBundleContext", mockSysContext.proxy());
94  
95          mockSysContext.matchAndReturn("getServiceReference", C.ANY_ARGS, null);
96          mockSysContext.matchAndReturn("getService", C.ANY_ARGS, new Mock(PackageAdmin.class).proxy());
97      }
98  
99      @After
100     public void tearDown() throws IOException {
101         factory = null;
102         FileUtils.cleanDirectory(tmpDir);
103         jar.delete();
104     }
105 
106     @Test
107     public void createOsgiPlugin() throws PluginParseException {
108         mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
109         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(ImmutableList.of());
110         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[]{(Bundle) mockSystemBundle.proxy()});
111         final Plugin plugin = factory.create(new JarPluginArtifact(jar), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
112         assertNotNull(plugin);
113         assertTrue(plugin instanceof OsgiPlugin);
114     }
115 
116     @Test
117     public void createOsgiPluginWithBadVersion() throws PluginParseException, IOException {
118         jar = new PluginJarBuilder("someplugin").addPluginInformation("plugin.key", "My Plugin", "beta.1.0").build();
119         mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
120         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(ImmutableList.of());
121         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[]{(Bundle) mockSystemBundle.proxy()});
122         try {
123             factory.create(new JarPluginArtifact(jar), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
124             fail("Should have complained about osgi version");
125         } catch (final PluginParseException ex) {
126             // expected
127         }
128     }
129 
130     @Test
131     public void createOsgiPluginWithBadVersion2() throws PluginParseException, IOException {
132         jar = new PluginJarBuilder("someplugin").addPluginInformation("plugin.key", "My Plugin", "3.2-rc1").build();
133         mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
134         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(ImmutableList.of());
135         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[]{(Bundle) mockSystemBundle.proxy()});
136         final Plugin plugin = factory.create(
137                 new JarPluginArtifact(jar), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
138         assertTrue(plugin instanceof OsgiPlugin);
139     }
140 
141     @Test
142     public void createOsgiPluginWithManifestKey() throws PluginParseException, IOException {
143         mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
144         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(ImmutableList.of());
145         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[]{(Bundle) mockSystemBundle.proxy()});
146 
147         final File pluginFile = new PluginJarBuilder("loadwithxml")
148                 .manifest(new ImmutableMap.Builder<String, String>()
149                         .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "somekey")
150                         .put(Constants.BUNDLE_VERSION, "1.0")
151                         .put(Constants.BUNDLE_NAME, "My awesome plugin")
152                         .put(Constants.BUNDLE_DESCRIPTION, "Bundle Description")
153                         .put(Constants.BUNDLE_VENDOR, "My vendor Name")
154                         .build())
155                 .build();
156 
157         final Plugin plugin = factory.create(new JarPluginArtifact(pluginFile), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
158         assertNotNull(plugin);
159         assertTrue(plugin instanceof OsgiPlugin);
160     }
161 
162     @Test
163     public void createOsgiPluginWithManifestKeyAndDescriptorSkippingTransform() throws PluginParseException, IOException {
164         final File jar = new PluginJarBuilder("someplugin")
165                 .addPluginInformation("plugin.key", "My Plugin", "1.0")
166                 .manifest(new ImmutableMap.Builder<String, String>()
167                         .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "somekey")
168                         .put(Constants.BUNDLE_VERSION, "1.0")
169                         .build())
170                 .build();
171         mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
172         mockBundle.expectAndReturn("getHeaders", new Hashtable() {{
173             put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "plugin.key");
174             put(Constants.BUNDLE_VERSION, "1.0");
175         }});
176         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(
177                 Collections.emptyList());
178         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[]{(Bundle) mockSystemBundle.proxy()});
179         when(osgiContainerManager.installBundle(jar, FORBID_REFERENCE)).thenReturn((Bundle) mockBundle.proxy());
180         final Plugin plugin = factory.create(new JarPluginArtifact(jar), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
181         assertNotNull(plugin);
182         assertTrue(plugin instanceof OsgiPlugin);
183 
184         plugin.install();
185         verify(osgiContainerManager).installBundle(jar, FORBID_REFERENCE);
186     }
187 
188     @Test
189     public void pluginInformationAndPluginNameIsExtractedFromManifest() throws IOException {
190         final PluginInformation expectedPluginInformation = createPluginInformation(
191                 "My Description", "My Awesome Vendor Name", "1.0");
192         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(ImmutableList.of());
193         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[]{(Bundle) mockSystemBundle.proxy()});
194 
195         final String pluginName = "My awesome plugin";
196 
197         final Plugin plugin = createPlugin(pluginName, expectedPluginInformation);
198         assertNotNull(plugin);
199         assertTrue(plugin instanceof OsgiPlugin);
200 
201         assertThat(plugin.getName(), equalTo(pluginName));
202 
203         final PluginInformation actualPluginInformation = plugin.getPluginInformation();
204         assertThat(actualPluginInformation.getDescription(), equalTo(expectedPluginInformation.getDescription()));
205         assertThat(actualPluginInformation.getVendorName(), equalTo(expectedPluginInformation.getVendorName()));
206         assertThat(actualPluginInformation.getVersion(), equalTo(expectedPluginInformation.getVersion()));
207     }
208 
209     @Test
210     public void shouldLoadPluginIfManifestDoesNotHaveOptionalInformation() throws IOException {
211         final PluginInformation pluginInformation = createPluginInformation("", "", "1.0");
212         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(ImmutableList.of());
213         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[]{(Bundle) mockSystemBundle.proxy()});
214 
215         final String pluginName = "My Awesome Plugin";
216         final Plugin plugin = createPlugin(pluginName, pluginInformation);
217 
218         assertNotNull(plugin);
219         assertTrue(plugin instanceof OsgiPlugin);
220         assertThat(plugin.getName(), equalTo(pluginName));
221     }
222 
223     private Plugin createPlugin(final String pluginName, final PluginInformation pluginInformation) throws IOException {
224         final File pluginFile = new PluginJarBuilder("loadwithxml")
225                 .manifest(new ImmutableMap.Builder<String, String>()
226                         .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, pluginName)
227                         .put(Constants.BUNDLE_VERSION, pluginInformation.getVersion())
228                         .put(Constants.BUNDLE_NAME, pluginName)
229                         .put(Constants.BUNDLE_DESCRIPTION, pluginInformation.getDescription())
230                         .put(Constants.BUNDLE_VENDOR, pluginInformation.getVendorName())
231                         .build())
232                 .build();
233 
234         return factory.create(new JarPluginArtifact(pluginFile), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
235     }
236 
237     private PluginInformation createPluginInformation(final String description, final String vendorName, final String version) {
238         final PluginInformation pluginInformation = new PluginInformation();
239         pluginInformation.setDescription(description);
240         pluginInformation.setVendorName(vendorName);
241         pluginInformation.setVersion(version);
242         return pluginInformation;
243     }
244 
245     @Test
246     public void createOsgiPluginWithManifestKeyNoVersion() throws PluginParseException, IOException {
247         mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
248         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(ImmutableList.of());
249         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[]{(Bundle) mockSystemBundle.proxy()});
250 
251         final File pluginFile = new PluginJarBuilder("loadwithxml")
252                 .manifest(new ImmutableMap.Builder<String, String>()
253                         .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "somekey")
254                         .build())
255                 .build();
256 
257         try {
258             factory.create(new JarPluginArtifact(pluginFile), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
259             fail("Should have failed due to no version");
260         } catch (final NullPointerException ex) {
261             // test passed
262         }
263     }
264 
265     @Test
266     public void canLoadWithXml() throws PluginParseException, IOException {
267         final File plugin = new PluginJarBuilder("loadwithxml").addPluginInformation("foo.bar", "", "1.0").build();
268         final String key = factory.canCreate(new JarPluginArtifact(plugin));
269         assertEquals("foo.bar", key);
270     }
271 
272     @Test
273     public void canLoadWithXmlArtifact() throws PluginParseException, IOException {
274         final File xmlFile = new File(tmpDir, "plugin.xml");
275         FileUtils.writeStringToFile(xmlFile, "<somexml />");
276         final String key = factory.canCreate(new XmlPluginArtifact(xmlFile));
277         assertNull(key);
278     }
279 
280     @Test
281     public void canLoadJarWithNoManifest() throws PluginParseException, IOException {
282         final File plugin = new PluginJarBuilder("loadwithxml")
283                 .addResource("foo.xml", "<foo/>")
284                 .buildWithNoManifest();
285         final String key = factory.canCreate(new JarPluginArtifact(plugin));
286         assertNull(key);
287     }
288 
289     @Test
290     public void canLoadNoXml() throws PluginParseException, IOException {
291         final File plugin = new PluginJarBuilder("loadwithxml").build();
292         final String key = factory.canCreate(new JarPluginArtifact(plugin));
293         assertNull(key);
294     }
295 
296     @Test
297     public void canLoadNoXmlButWithManifestEntry() throws PluginParseException, IOException {
298         final File plugin = new PluginJarBuilder("loadwithxml")
299                 .manifest(new ImmutableMap.Builder<String, String>()
300                         .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "somekey")
301                         .put(Constants.BUNDLE_VERSION, "1.0")
302                         .put("Spring-Context", "*")
303                         .build())
304                 .build();
305         final String key = factory.canCreate(new JarPluginArtifact(plugin));
306         assertEquals("somekey", key);
307     }
308 
309     @Test
310     public void canLoadNoXmlButWithManifestEntryNoVersion() throws PluginParseException, IOException {
311         final File plugin = new PluginJarBuilder("loadwithxml")
312                 .manifest(new ImmutableMap.Builder<String, String>()
313                         .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "somekey")
314                         .build())
315                 .build();
316         final String key = factory.canCreate(new JarPluginArtifact(plugin));
317         assertNull(key);
318     }
319 
320     @Test
321     public void canLoadWithXmlVersion3() throws Exception {
322         final File plugin = new PluginJarBuilder("loadwithxml").addPluginInformation("foo.bar", "", "1.0", 3).build();
323         final String key = factory.canCreate(new JarPluginArtifact(plugin));
324         assertNull(key);
325     }
326 
327     @Test
328     public void transformedArtifactAllowsReferenceInstall() {
329         mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
330         // We need to insert the Atlassian-Plugin-Key header that transformation would for installation to work
331         ((Bundle) mockBundle.proxy()).getHeaders().put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "plugin.key");
332 
333         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(ImmutableList.of());
334         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[]{(Bundle) mockSystemBundle.proxy()});
335         // Have our mock OsgiContainerManager respond only to a reference install (the PERMIT_REFERENCE in the next line)
336         when(osgiContainerManager.installBundle(any(File.class), eq(PERMIT_REFERENCE)))
337                 .thenReturn((Bundle) mockBundle
338                         .proxy());
339         final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
340         final Plugin plugin = factory.create(new JarPluginArtifact(jar), moduleDescriptorFactory);
341         plugin.install();
342         assertThat(plugin, instanceOf(OsgiPlugin.class));
343         final Bundle actualBundle = ((OsgiPlugin) plugin).getBundle();
344         assertThat(actualBundle, sameInstance(mockBundle.proxy()));
345         // The verify here needs to be any(File.class) not jar, because the artifact is transformed, so the file isn't jar
346         verify(osgiContainerManager).installBundle(any(File.class), eq(PERMIT_REFERENCE));
347     }
348 
349     @Test
350     public void canLoadExtraModuleDescriptors() throws Exception {
351         final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
352         final File pluginFile = new PluginJarBuilder("loadextramodules")
353                 .manifest(new ImmutableMap.Builder<String, String>()
354                         .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "pluginKey")
355                         .put(Constants.BUNDLE_VERSION, "3")
356                         .put(Constants.BUNDLE_NAME, "bundleName")
357                         .put(Constants.BUNDLE_DESCRIPTION, "bundleDescription")
358                         .put(Constants.BUNDLE_VENDOR, "Atlassian")
359                         .put("Atlassian-Scan-Folders", "META-INF/atlassian")
360                         .build())
361                 .addResource("META-INF/atlassian/foo.xml", "<atlassian-plugin><rest key=\"sample\" path=\"/sample\" version=\"1\"/></atlassian-plugin>")
362                 .addResource("META-INF/atlassian/bar.xml", "<atlassian-plugin/>")
363                 .addPluginInformation("pluginKey", "1.1", "3")
364                 .build();
365         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(ImmutableList.of());
366         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[]{(Bundle) mockSystemBundle.proxy()});
367         final Plugin plugin = factory.create(new JarPluginArtifact(pluginFile), moduleDescriptorFactory);
368         assertThat(plugin.getModuleDescriptor("sample"), instanceOf(ModuleDescriptor.class));
369 
370     }
371 
372     @Test
373     public void createModuleIncorrectPluginType() {
374         final Plugin plugin = mock(Plugin.class);
375         final Element module = mock(Element.class);
376         final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
377 
378         assertThat(factory.createModule(plugin, module, moduleDescriptorFactory), nullValue());
379     }
380 
381     @Test
382     public void createModule() throws IllegalAccessException, ClassNotFoundException, InstantiationException {
383         final OsgiPlugin plugin = mock(OsgiPlugin.class);
384         final Element module = mock(Element.class);
385         final ModuleDescriptor moduleDescriptor = mock(ModuleDescriptor.class);
386         final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
387 
388         when(module.getName()).thenReturn("william");
389         when(moduleDescriptorFactory.hasModuleDescriptor("william")).thenReturn(true);
390         when(moduleDescriptorFactory.getModuleDescriptor("william")).thenReturn(moduleDescriptor);
391 
392         assertThat(factory.createModule(plugin, module, moduleDescriptorFactory), is(moduleDescriptor));
393     }
394 }