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
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
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
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
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 }