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.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
18 import com.google.common.collect.ImmutableList;
19 import com.google.common.collect.ImmutableMap;
20 import com.mockobjects.dynamic.C;
21 import com.mockobjects.dynamic.Mock;
22 import junit.framework.TestCase;
23 import org.apache.commons.io.FileUtils;
24 import org.osgi.framework.Bundle;
25 import org.osgi.framework.BundleContext;
26 import org.osgi.framework.Constants;
27 import org.osgi.service.packageadmin.PackageAdmin;
28 import org.osgi.util.tracker.ServiceTracker;
29
30 import java.io.File;
31 import java.io.IOException;
32 import java.net.URISyntaxException;
33 import java.util.ArrayList;
34 import java.util.Collections;
35 import java.util.Dictionary;
36 import java.util.Hashtable;
37
38 import static org.hamcrest.Matchers.instanceOf;
39 import static org.hamcrest.Matchers.sameInstance;
40 import static org.hamcrest.core.IsEqual.equalTo;
41 import static org.junit.Assert.assertThat;
42 import static org.mockito.Matchers.any;
43 import static org.mockito.Matchers.eq;
44 import static org.mockito.Mockito.mock;
45 import static org.mockito.Mockito.verify;
46 import static org.mockito.Mockito.when;
47 import static org.mockito.Mockito.withSettings;
48
49 public class TestOsgiPluginFactory extends TestCase
50 {
51 OsgiPluginFactory factory;
52
53 private File tmpDir;
54 private File jar;
55 OsgiContainerManager osgiContainerManager;
56 private Mock mockBundle;
57 private Mock mockSystemBundle;
58
59 @Override
60 public void setUp() throws IOException, URISyntaxException
61 {
62 tmpDir = PluginTestUtils.createTempDirectory(TestOsgiPluginFactory.class);
63 osgiContainerManager = mock(
64 OsgiContainerManager.class,
65 withSettings().extraInterfaces(OsgiContainerManager.AllowsReferenceInstall.class));
66
67 ServiceTracker tracker = mock(ServiceTracker.class);
68 when(tracker.getServices()).thenReturn(new Object[0]);
69 when(osgiContainerManager.getServiceTracker(ModuleDescriptorFactory.class.getName())).thenReturn(tracker);
70
71 factory = new OsgiPluginFactory(PluginAccessor.Descriptor.FILENAME, Collections.<Application>emptySet(), new DefaultOsgiPersistentCache(tmpDir), osgiContainerManager, new DefaultPluginEventManager());
72 jar = new PluginJarBuilder("someplugin").addPluginInformation("plugin.key", "My Plugin", "1.0").build();
73
74 mockBundle = new Mock(Bundle.class);
75 final Dictionary<String, String> dict = new Hashtable<String, String>();
76 dict.put(Constants.BUNDLE_DESCRIPTION, "desc");
77 dict.put(Constants.BUNDLE_VERSION, "1.0");
78 mockBundle.matchAndReturn("getHeaders", dict);
79
80 mockSystemBundle = new Mock(Bundle.class);
81 final Dictionary<String, String> sysDict = new Hashtable<String, String>();
82 sysDict.put(Constants.BUNDLE_DESCRIPTION, "desc");
83 sysDict.put(Constants.BUNDLE_VERSION, "1.0");
84 mockSystemBundle.matchAndReturn("getHeaders", sysDict);
85 mockSystemBundle.matchAndReturn("getLastModified", System.currentTimeMillis());
86 mockSystemBundle.matchAndReturn("getSymbolicName", "system.bundle");
87
88 Mock mockSysContext = new Mock(BundleContext.class);
89 mockSystemBundle.matchAndReturn("getBundleContext", mockSysContext.proxy());
90
91 mockSysContext.matchAndReturn("getServiceReference", C.ANY_ARGS, null);
92 mockSysContext.matchAndReturn("getService", C.ANY_ARGS, new Mock(PackageAdmin.class).proxy());
93 }
94
95 @Override
96 public void tearDown() throws IOException
97 {
98 factory = null;
99 FileUtils.cleanDirectory(tmpDir);
100 jar.delete();
101 }
102
103 public void testCreateOsgiPlugin() throws PluginParseException
104 {
105 mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
106 when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(new ArrayList());
107 when(osgiContainerManager.getBundles()).thenReturn(new Bundle[] {(Bundle) mockSystemBundle.proxy()});
108 final Plugin plugin = factory.create(new JarPluginArtifact(jar), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
109 assertNotNull(plugin);
110 assertTrue(plugin instanceof OsgiPlugin);
111 }
112
113 public void testCreateOsgiPluginWithBadVersion() throws PluginParseException, IOException
114 {
115 jar = new PluginJarBuilder("someplugin").addPluginInformation("plugin.key", "My Plugin", "beta.1.0").build();
116 mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
117 when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(new ArrayList());
118 when(osgiContainerManager.getBundles()).thenReturn(new Bundle[] {(Bundle) mockSystemBundle.proxy()});
119 try
120 {
121 factory.create(new JarPluginArtifact(jar), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
122 fail("Should have complained about osgi version");
123 }
124 catch (PluginParseException ex)
125 {
126
127 }
128 }
129
130 public void testCreateOsgiPluginWithBadVersion2() throws PluginParseException, IOException
131 {
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(new ArrayList());
135 when(osgiContainerManager.getBundles()).thenReturn(new Bundle[] {(Bundle) mockSystemBundle.proxy()});
136 Plugin plugin = factory.create(new JarPluginArtifact(jar), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
137 assertTrue(plugin instanceof OsgiPlugin);
138 }
139
140 public void testCreateOsgiPluginWithManifestKey() throws PluginParseException, IOException
141 {
142 mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
143 when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(new ArrayList());
144 when(osgiContainerManager.getBundles()).thenReturn(new Bundle[] {(Bundle) mockSystemBundle.proxy()});
145
146 final File pluginFile = new PluginJarBuilder("loadwithxml")
147 .manifest(new ImmutableMap.Builder<String, String>()
148 .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "somekey")
149 .put(Constants.BUNDLE_VERSION, "1.0")
150 .put(Constants.BUNDLE_NAME,"My awesome plugin")
151 .put(Constants.BUNDLE_DESCRIPTION,"Bundle Description")
152 .put(Constants.BUNDLE_VENDOR,"My vendor Name")
153 .build())
154 .build();
155
156 final Plugin plugin = factory.create(new JarPluginArtifact(pluginFile), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
157 assertNotNull(plugin);
158 assertTrue(plugin instanceof OsgiPlugin);
159 }
160
161 public void testCreateOsgiPluginWithManifestKeyAndDescriptorSkippingTransform() throws PluginParseException, IOException
162 {
163 File jar = new PluginJarBuilder("someplugin")
164 .addPluginInformation("plugin.key", "My Plugin", "1.0")
165 .manifest(new ImmutableMap.Builder<String, String>()
166 .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "somekey")
167 .put(Constants.BUNDLE_VERSION, "1.0")
168 .build())
169 .build();
170 mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
171 mockBundle.expectAndReturn("getHeaders", new Hashtable() {{
172 put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "plugin.key");
173 put(Constants.BUNDLE_VERSION, "1.0");
174 }});
175 when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(
176 Collections.<HostComponentRegistration>emptyList());
177 when(osgiContainerManager.getBundles()).thenReturn(new Bundle[] {(Bundle) mockSystemBundle.proxy()});
178 when(((OsgiContainerManager.AllowsReferenceInstall)osgiContainerManager).installBundle(jar, false)).thenReturn((Bundle) mockBundle.proxy());
179 final Plugin plugin = factory.create(new JarPluginArtifact(jar), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
180 assertNotNull(plugin);
181 assertTrue(plugin instanceof OsgiPlugin);
182
183 plugin.install();
184 verify((OsgiContainerManager.AllowsReferenceInstall)osgiContainerManager).installBundle(jar, false);
185 }
186
187 public void testPluginInformationAndPluginNameIsExtractedFromManifest() throws IOException
188 {
189 PluginInformation expectedPluginInformation = createPluginInformation("My Description", "My Awesome Vendor Name", "1.0");
190 when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(new ArrayList());
191 when(osgiContainerManager.getBundles()).thenReturn(new Bundle[] {(Bundle) mockSystemBundle.proxy()});
192
193 String pluginName = "My awesome plugin";
194
195 final Plugin plugin = createPlugin(pluginName, expectedPluginInformation);
196 assertNotNull(plugin);
197 assertTrue(plugin instanceof OsgiPlugin);
198
199 assertThat(plugin.getName(),equalTo(pluginName));
200
201 PluginInformation actualPluginInformation = plugin.getPluginInformation();
202 assertThat(actualPluginInformation.getDescription(), equalTo(expectedPluginInformation.getDescription()));
203 assertThat(actualPluginInformation.getVendorName(),equalTo(expectedPluginInformation.getVendorName()));
204 assertThat(actualPluginInformation.getVersion(), equalTo(expectedPluginInformation.getVersion()));
205 }
206
207 public void testShouldLoadPluginIfManifestDoesNotHaveOptionalInformation() throws IOException
208 {
209 PluginInformation pluginInformation = createPluginInformation("", "", "1.0");
210 when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(new ArrayList());
211 when(osgiContainerManager.getBundles()).thenReturn(new Bundle[] {(Bundle) mockSystemBundle.proxy()});
212
213 String pluginName = "My Awesome Plugin";
214 Plugin plugin = createPlugin(pluginName, pluginInformation);
215
216 assertNotNull(plugin);
217 assertTrue(plugin instanceof OsgiPlugin);
218 assertThat(plugin.getName(), equalTo(pluginName));
219 }
220
221 private Plugin createPlugin(String pluginName, PluginInformation pluginInformation) throws IOException
222 {
223 final File pluginFile = new PluginJarBuilder("loadwithxml")
224 .manifest(new ImmutableMap.Builder<String, String>()
225 .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, pluginName)
226 .put(Constants.BUNDLE_VERSION, pluginInformation.getVersion())
227 .put(Constants.BUNDLE_NAME, pluginName)
228 .put(Constants.BUNDLE_DESCRIPTION, pluginInformation.getDescription())
229 .put(Constants.BUNDLE_VENDOR, pluginInformation.getVendorName())
230 .build())
231 .build();
232
233 return factory.create(new JarPluginArtifact(pluginFile), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
234 }
235
236 private PluginInformation createPluginInformation(String description, String vendorName, String version)
237 {
238 PluginInformation pluginInformation = new PluginInformation();
239 pluginInformation.setDescription(description);
240 pluginInformation.setVendorName(vendorName);
241 pluginInformation.setVersion(version);
242 return pluginInformation;
243 }
244
245 public void testCreateOsgiPluginWithManifestKeyNoVersion() throws PluginParseException, IOException
246 {
247 mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
248 when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(new ArrayList());
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 {
259 factory.create(new JarPluginArtifact(pluginFile), (ModuleDescriptorFactory) new Mock(ModuleDescriptorFactory.class).proxy());
260 fail("Should have failed due to no version");
261 }
262 catch (NullPointerException ex)
263 {
264
265 }
266 }
267
268 public void testCanLoadWithXml() throws PluginParseException, IOException
269 {
270 final File plugin = new PluginJarBuilder("loadwithxml").addPluginInformation("foo.bar", "", "1.0").build();
271 final String key = factory.canCreate(new JarPluginArtifact(plugin));
272 assertEquals("foo.bar", key);
273 }
274
275 public void testCanLoadWithXmlArtifact() throws PluginParseException, IOException
276 {
277 File xmlFile = new File(tmpDir, "plugin.xml");
278 FileUtils.writeStringToFile(xmlFile, "<somexml />");
279 final String key = factory.canCreate(new XmlPluginArtifact(xmlFile));
280 assertNull(key);
281 }
282
283 public void testCanLoadJarWithNoManifest() throws PluginParseException, IOException
284 {
285 final File plugin = new PluginJarBuilder("loadwithxml")
286 .addResource("foo.xml", "<foo/>")
287 .buildWithNoManifest();
288 final String key = factory.canCreate(new JarPluginArtifact(plugin));
289 assertNull(key);
290 }
291
292 public void testCanLoadNoXml() throws PluginParseException, IOException
293 {
294 final File plugin = new PluginJarBuilder("loadwithxml").build();
295 final String key = factory.canCreate(new JarPluginArtifact(plugin));
296 assertNull(key);
297 }
298
299 public void testCanLoadNoXmlButWithManifestEntry() throws PluginParseException, IOException
300 {
301 final File plugin = new PluginJarBuilder("loadwithxml")
302 .manifest(new ImmutableMap.Builder<String, String>()
303 .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "somekey")
304 .put(Constants.BUNDLE_VERSION, "1.0")
305 .build())
306 .build();
307 final String key = factory.canCreate(new JarPluginArtifact(plugin));
308 assertEquals("somekey", key);
309 }
310
311 public void testCanLoadNoXmlButWithManifestEntryNoVersion() throws PluginParseException, IOException
312 {
313 final File plugin = new PluginJarBuilder("loadwithxml")
314 .manifest(new ImmutableMap.Builder<String, String>()
315 .put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "somekey")
316 .build())
317 .build();
318 final String key = factory.canCreate(new JarPluginArtifact(plugin));
319 assertNull(key);
320 }
321
322 public void testCanLoadWithXmlVersion3() throws Exception
323 {
324 final File plugin = new PluginJarBuilder("loadwithxml").addPluginInformation("foo.bar", "", "1.0", 3).build();
325 final String key = factory.canCreate(new JarPluginArtifact(plugin));
326 assertNull(key);
327 }
328
329 public void testTransformedArtifactAllowsReferenceInstall()
330 {
331 mockBundle.expectAndReturn("getSymbolicName", "plugin.key");
332
333 ((Bundle) mockBundle.proxy()).getHeaders().put(OsgiPlugin.ATLASSIAN_PLUGIN_KEY, "plugin.key");
334
335 when(osgiContainerManager.getHostComponentRegistrations()).thenReturn(ImmutableList.<HostComponentRegistration>of());
336 when(osgiContainerManager.getBundles()).thenReturn(new Bundle[] {(Bundle) mockSystemBundle.proxy()});
337
338 when(((OsgiContainerManager.AllowsReferenceInstall) osgiContainerManager).installBundle(any(File.class), eq(true)))
339 .thenReturn((Bundle) mockBundle.proxy());
340 final ModuleDescriptorFactory moduleDescriptorFactory = mock(ModuleDescriptorFactory.class);
341 final Plugin plugin = factory.create(new JarPluginArtifact(jar), moduleDescriptorFactory);
342 plugin.install();
343 assertThat(plugin, instanceOf(OsgiPlugin.class));
344 final Bundle actualBundle = ((OsgiPlugin) plugin).getBundle();
345 assertThat(actualBundle, sameInstance(mockBundle.proxy()));
346 verify((OsgiContainerManager.AllowsReferenceInstall) osgiContainerManager).installBundle(any(File.class), eq(true));
347 }
348 }