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