View Javadoc

1   package com.atlassian.plugin.osgi.factory;
2   
3   import com.atlassian.plugin.Application;
4   import com.atlassian.plugin.JarPluginArtifact;
5   import com.atlassian.plugin.ModuleDescriptor;
6   import com.atlassian.plugin.ModuleDescriptorFactory;
7   import com.atlassian.plugin.Plugin;
8   import com.atlassian.plugin.PluginAccessor;
9   import com.atlassian.plugin.PluginInformation;
10  import com.atlassian.plugin.PluginParseException;
11  import com.atlassian.plugin.XmlPluginArtifact;
12  import com.atlassian.plugin.event.impl.DefaultPluginEventManager;
13  import com.atlassian.plugin.osgi.container.OsgiContainerManager;
14  import com.atlassian.plugin.osgi.container.impl.DefaultOsgiPersistentCache;
15  import com.atlassian.plugin.osgi.hostcomponents.HostComponentRegistration;
16  import com.atlassian.plugin.test.PluginJarBuilder;
17  import com.atlassian.plugin.test.PluginTestUtils;
18  
19  import com.google.common.collect.ImmutableList;
20  import com.google.common.collect.ImmutableMap;
21  import com.mockobjects.dynamic.C;
22  import com.mockobjects.dynamic.Mock;
23  import junit.framework.TestCase;
24  import org.apache.commons.io.FileUtils;
25  import org.osgi.framework.Bundle;
26  import org.osgi.framework.BundleContext;
27  import org.osgi.framework.Constants;
28  import org.osgi.service.packageadmin.PackageAdmin;
29  import org.osgi.util.tracker.ServiceTracker;
30  
31  import java.io.File;
32  import java.io.IOException;
33  import java.net.URISyntaxException;
34  import java.util.ArrayList;
35  import java.util.Collections;
36  import java.util.Dictionary;
37  import java.util.Hashtable;
38  
39  import static org.hamcrest.Matchers.instanceOf;
40  import static org.hamcrest.Matchers.sameInstance;
41  import static org.hamcrest.core.IsEqual.equalTo;
42  import static org.junit.Assert.assertThat;
43  import static org.mockito.Matchers.any;
44  import static org.mockito.Matchers.eq;
45  import static org.mockito.Mockito.mock;
46  import static org.mockito.Mockito.verify;
47  import static org.mockito.Mockito.when;
48  import static org.mockito.Mockito.withSettings;
49  
50  public class TestOsgiPluginFactory extends TestCase
51  {
52      OsgiPluginFactory factory;
53  
54      private File tmpDir;
55      private File jar;
56      OsgiContainerManager osgiContainerManager;
57      private Mock mockBundle;
58      private Mock mockSystemBundle;
59  
60      @Override
61      public void setUp() throws IOException, URISyntaxException
62      {
63          tmpDir = PluginTestUtils.createTempDirectory(TestOsgiPluginFactory.class);
64          osgiContainerManager = mock(
65                  OsgiContainerManager.class,
66                  withSettings().extraInterfaces(OsgiContainerManager.AllowsReferenceInstall.class));
67  
68          ServiceTracker tracker = mock(ServiceTracker.class);
69          when(tracker.getServices()).thenReturn(new Object[0]);
70          when(osgiContainerManager.getServiceTracker(ModuleDescriptorFactory.class.getName())).thenReturn(tracker);
71  
72          factory = new OsgiPluginFactory(PluginAccessor.Descriptor.FILENAME, Collections.<Application>emptySet(), new DefaultOsgiPersistentCache(tmpDir), osgiContainerManager, new DefaultPluginEventManager());
73          jar = new PluginJarBuilder("someplugin").addPluginInformation("plugin.key", "My Plugin", "1.0").build();
74  
75          mockBundle = new Mock(Bundle.class);
76          final Dictionary<String, String> dict = new Hashtable<String, String>();
77          dict.put(Constants.BUNDLE_DESCRIPTION, "desc");
78          dict.put(Constants.BUNDLE_VERSION, "1.0");
79          mockBundle.matchAndReturn("getHeaders", dict);
80  
81          mockSystemBundle = new Mock(Bundle.class);
82          final Dictionary<String, String> sysDict = new Hashtable<String, String>();
83          sysDict.put(Constants.BUNDLE_DESCRIPTION, "desc");
84          sysDict.put(Constants.BUNDLE_VERSION, "1.0");
85          mockSystemBundle.matchAndReturn("getHeaders", sysDict);
86          mockSystemBundle.matchAndReturn("getLastModified", System.currentTimeMillis());
87          mockSystemBundle.matchAndReturn("getSymbolicName", "system.bundle");
88  
89          Mock mockSysContext = new Mock(BundleContext.class);
90          mockSystemBundle.matchAndReturn("getBundleContext", mockSysContext.proxy());
91  
92          mockSysContext.matchAndReturn("getServiceReference", C.ANY_ARGS, null);
93          mockSysContext.matchAndReturn("getService", C.ANY_ARGS, new Mock(PackageAdmin.class).proxy());
94      }
95  
96      @Override
97      public void tearDown() throws IOException
98      {
99          factory = null;
100         FileUtils.cleanDirectory(tmpDir);
101         jar.delete();
102     }
103 
104     public void testCreateOsgiPlugin() throws PluginParseException
105     {
106         mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
107         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(new ArrayList());
108         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[] {(Bundle) mockSystemBundle.proxy()});
109         final Plugin plugin = factory.create(new JarPluginArtifact(jar), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
110         assertNotNull(plugin);
111         assertTrue(plugin instanceof OsgiPlugin);
112     }
113 
114     public void testCreateOsgiPluginWithBadVersion() throws PluginParseException, IOException
115     {
116         jar = new PluginJarBuilder("someplugin").addPluginInformation("plugin.key", "My Plugin", "beta.1.0").build();
117         mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
118         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(new ArrayList());
119         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[] {(Bundle) mockSystemBundle.proxy()});
120         try
121         {
122             factory.create(new JarPluginArtifact(jar), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
123             fail("Should have complained about osgi version");
124         }
125         catch (PluginParseException ex)
126         {
127             // expected
128         }
129     }
130 
131     public void testCreateOsgiPluginWithBadVersion2() throws PluginParseException, IOException
132     {
133         jar = new PluginJarBuilder("someplugin").addPluginInformation("plugin.key", "My Plugin", "3.2-rc1").build();
134         mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
135         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(new ArrayList());
136         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[] {(Bundle) mockSystemBundle.proxy()});
137         Plugin plugin = factory.create(new JarPluginArtifact(jar), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
138         assertTrue(plugin instanceof OsgiPlugin);
139     }
140 
141     public void testCreateOsgiPluginWithManifestKey() throws PluginParseException, IOException
142     {
143         mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
144         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(new ArrayList());
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     public void testCreateOsgiPluginWithManifestKeyAndDescriptorSkippingTransform() throws PluginParseException, IOException
163     {
164         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.<HostComponentRegistration>emptyList());
178         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[] {(Bundle) mockSystemBundle.proxy()});
179         when(((OsgiContainerManager.AllowsReferenceInstall)osgiContainerManager).installBundle(jar, false)).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.AllowsReferenceInstall)osgiContainerManager).installBundle(jar, false);
186     }
187 
188     public void testPluginInformationAndPluginNameIsExtractedFromManifest() throws IOException
189     {
190         PluginInformation expectedPluginInformation = createPluginInformation("My Description", "My Awesome Vendor Name", "1.0");
191         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(new ArrayList());
192         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[] {(Bundle) mockSystemBundle.proxy()});
193 
194         String pluginName = "My awesome plugin";
195 
196         final Plugin plugin = createPlugin(pluginName, expectedPluginInformation);
197         assertNotNull(plugin);
198         assertTrue(plugin instanceof OsgiPlugin);
199 
200         assertThat(plugin.getName(),equalTo(pluginName));
201 
202         PluginInformation actualPluginInformation = plugin.getPluginInformation();
203         assertThat(actualPluginInformation.getDescription(), equalTo(expectedPluginInformation.getDescription()));
204         assertThat(actualPluginInformation.getVendorName(),equalTo(expectedPluginInformation.getVendorName()));
205         assertThat(actualPluginInformation.getVersion(), equalTo(expectedPluginInformation.getVersion()));
206     }
207 
208     public void testShouldLoadPluginIfManifestDoesNotHaveOptionalInformation() throws IOException
209     {
210         PluginInformation pluginInformation = createPluginInformation("", "", "1.0");
211         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(new ArrayList());
212         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[] {(Bundle) mockSystemBundle.proxy()});
213 
214         String pluginName = "My Awesome Plugin";
215         Plugin plugin = createPlugin(pluginName, pluginInformation);
216 
217         assertNotNull(plugin);
218         assertTrue(plugin instanceof OsgiPlugin);
219         assertThat(plugin.getName(), equalTo(pluginName));
220     }
221 
222     private Plugin createPlugin(String pluginName, PluginInformation pluginInformation) throws IOException
223     {
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(String description, String vendorName, String version)
238     {
239         PluginInformation pluginInformation = new PluginInformation();
240         pluginInformation.setDescription(description);
241         pluginInformation.setVendorName(vendorName);
242         pluginInformation.setVersion(version);
243         return pluginInformation;
244     }
245 
246     public void testCreateOsgiPluginWithManifestKeyNoVersion() throws PluginParseException, IOException
247     {
248         mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
249         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(new ArrayList());
250         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[] {(Bundle) mockSystemBundle.proxy()});
251 
252         final File pluginFile = new PluginJarBuilder("loadwithxml")
253                 .manifest(new ImmutableMap.Builder<String, String>()
254                         .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "somekey")
255                         .build())
256                 .build();
257 
258         try
259         {
260             factory.create(new JarPluginArtifact(pluginFile), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
261             fail("Should have failed due to no version");
262         }
263         catch (NullPointerException ex)
264         {
265             // test passed
266         }
267     }
268 
269     public void testCanLoadWithXml() throws PluginParseException, IOException
270     {
271         final File plugin = new PluginJarBuilder("loadwithxml").addPluginInformation("foo.bar", "", "1.0").build();
272         final String key = factory.canCreate(new JarPluginArtifact(plugin));
273         assertEquals("foo.bar", key);
274     }
275 
276     public void testCanLoadWithXmlArtifact() throws PluginParseException, IOException
277     {
278         File xmlFile = new File(tmpDir, "plugin.xml");
279         FileUtils.writeStringToFile(xmlFile, "<somexml />");
280         final String key = factory.canCreate(new XmlPluginArtifact(xmlFile));
281         assertNull(key);
282     }
283 
284     public void testCanLoadJarWithNoManifest() throws PluginParseException, IOException
285     {
286         final File plugin = new PluginJarBuilder("loadwithxml")
287                 .addResource("foo.xml", "<foo/>")
288                 .buildWithNoManifest();
289         final String key = factory.canCreate(new JarPluginArtifact(plugin));
290         assertNull(key);
291     }
292 
293     public void testCanLoadNoXml() throws PluginParseException, IOException
294     {
295         final File plugin = new PluginJarBuilder("loadwithxml").build();
296         final String key = factory.canCreate(new JarPluginArtifact(plugin));
297         assertNull(key);
298     }
299 
300     public void testCanLoadNoXmlButWithManifestEntry() throws PluginParseException, IOException
301     {
302         final File plugin = new PluginJarBuilder("loadwithxml")
303                 .manifest(new ImmutableMap.Builder<String, String>()
304                         .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "somekey")
305                         .put(Constants.BUNDLE_VERSION, "1.0")
306                         .build())
307                 .build();
308         final String key = factory.canCreate(new JarPluginArtifact(plugin));
309         assertEquals("somekey", key);
310     }
311 
312     public void testCanLoadNoXmlButWithManifestEntryNoVersion() throws PluginParseException, IOException
313     {
314         final File plugin = new PluginJarBuilder("loadwithxml")
315                 .manifest(new ImmutableMap.Builder<String, String>()
316                         .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "somekey")
317                         .build())
318                 .build();
319         final String key = factory.canCreate(new JarPluginArtifact(plugin));
320         assertNull(key);
321     }
322 
323     public void testCanLoadWithXmlVersion3() throws Exception
324     {
325         final File plugin = new PluginJarBuilder("loadwithxml").addPluginInformation("foo.bar", "", "1.0", 3).build();
326         final String key = factory.canCreate(new JarPluginArtifact(plugin));
327         assertNull(key);
328     }
329 
330     public void testTransformedArtifactAllowsReferenceInstall()
331     {
332         mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
333         // We need to insert the Atlassian-Plugin-Key header that transformation would for installation to work
334         ((Bundle) mockBundle.proxy()).getHeaders().put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "plugin.key");
335 
336         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(ImmutableList.<HostComponentRegistration>of());
337         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[] {(Bundle) mockSystemBundle.proxy()});
338         // Have our mock OsgiContainerManager respond only to a reference install (the true in the next line)
339         when(((OsgiContainerManager.AllowsReferenceInstall) osgiContainerManager).installBundle(any(File.class), eq(true)))
340                 .thenReturn((Bundle) mockBundle.proxy());
341         final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
342         final Plugin plugin = factory.create(new JarPluginArtifact(jar), moduleDescriptorFactory);
343         plugin.install();
344         assertThat(plugin, instanceOf(OsgiPlugin.class));
345         final Bundle actualBundle = ((OsgiPlugin) plugin).getBundle();
346         assertThat(actualBundle, sameInstance(mockBundle.proxy()));
347         verify((OsgiContainerManager.AllowsReferenceInstall) osgiContainerManager).installBundle(any(File.class), eq(true));
348     }
349 
350     public void testCanLoadExtraModuleDescriptors() throws Exception
351     {
352         final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
353         final File pluginFile = new PluginJarBuilder("loadextramodules")
354                 .manifest(new ImmutableMap.Builder<String, String>()
355                         .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "pluginKey")
356                         .put(Constants.BUNDLE_VERSION, "3")
357                         .put(Constants.BUNDLE_NAME, "bundleName")
358                         .put(Constants.BUNDLE_DESCRIPTION, "bundleDescription")
359                         .put(Constants.BUNDLE_VENDOR, "Atlassian")
360                         .put("Atlassian-Scan-Folders", "META-INF/atlassian")
361                         .build())
362                 .addResource("META-INF/atlassian/foo.xml", "<atlassian-plugin><rest key=\"sample\" path=\"/sample\" version=\"1\"/></atlassian-plugin>")
363                 .addResource("META-INF/atlassian/bar.xml","<atlassian-plugin/>")
364                 .addPluginInformation("pluginKey", "1.1", "3")
365                 .build();
366         when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(ImmutableList.<HostComponentRegistration>of());
367         when(osgiContainerManager.getBundles()).thenReturn(new Bundle[] {(Bundle) mockSystemBundle.proxy()});
368         final Plugin plugin = factory.create(new JarPluginArtifact(pluginFile), moduleDescriptorFactory);
369         assertThat(plugin.getModuleDescriptor("sample"), instanceOf(ModuleDescriptor.class));
370 
371     }
372 }