1 package com.atlassian.plugin.osgi.container.felix;
2
3 import java.io.File;
4 import java.io.FilenameFilter;
5 import java.io.IOException;
6 import java.net.URISyntaxException;
7 import java.net.URL;
8 import java.util.ArrayList;
9 import java.util.List;
10 import java.util.concurrent.CountDownLatch;
11 import java.util.concurrent.TimeUnit;
12
13 import com.atlassian.plugin.event.impl.DefaultPluginEventManager;
14 import com.atlassian.plugin.osgi.container.OsgiContainerException;
15 import com.atlassian.plugin.osgi.container.impl.DefaultOsgiPersistentCache;
16 import com.atlassian.plugin.osgi.container.impl.DefaultPackageScannerConfiguration;
17 import com.atlassian.plugin.osgi.hostcomponents.HostComponentRegistration;
18 import com.atlassian.plugin.test.PluginJarBuilder;
19 import com.atlassian.plugin.test.PluginTestUtils;
20
21 import com.atlassian.plugin.util.PluginUtils;
22 import com.google.common.collect.ImmutableList;
23
24 import org.apache.commons.io.FileUtils;
25 import org.osgi.framework.Bundle;
26 import org.osgi.framework.BundleException;
27 import org.osgi.framework.Constants;
28 import org.osgi.framework.ServiceReference;
29 import org.osgi.service.packageadmin.PackageAdmin;
30 import org.osgi.util.tracker.ServiceTracker;
31 import org.osgi.util.tracker.ServiceTrackerCustomizer;
32
33 import junit.framework.TestCase;
34
35 public class TestFelixOsgiContainerManager extends TestCase
36 {
37 private File tmpdir;
38 private FelixOsgiContainerManager felix;
39 private URL frameworkBundlesUrl = getClass().getResource("/nothing.zip");
40
41 @Override
42 public void setUp() throws Exception
43 {
44 super.setUp();
45 tmpdir = PluginTestUtils.createTempDirectory(TestFelixOsgiContainerManager.class);
46 felix = new FelixOsgiContainerManager(frameworkBundlesUrl, new DefaultOsgiPersistentCache(tmpdir), new DefaultPackageScannerConfiguration(),
47 null, new DefaultPluginEventManager());
48 }
49
50 @Override
51 public void tearDown() throws Exception
52 {
53 if (felix != null && felix.isRunning())
54 {
55 for (Bundle bundle : felix.getBundles())
56 {
57 try
58 {
59 bundle.uninstall();
60 }
61 catch (BundleException ignored) {}
62 }
63 }
64 if (felix != null)
65 {
66 felix.stop();
67 felix.clearExportCache();
68 }
69 felix = null;
70 tmpdir = null;
71 super.tearDown();
72 }
73
74 public void testDetectXercesOverride()
75 {
76 felix.detectXercesOverride("foo.bar,baz.jim");
77 felix.detectXercesOverride("foo.bar,org.apache.xerces.util;version=\"1.0\",baz.jim");
78 felix.detectXercesOverride("foo.bar,org.apache.xerces.util;version=\"1.0\"");
79 felix.detectXercesOverride("foo.bar,repackaged.org.apache.xerces.util,bar.baz");
80
81
82 try
83 {
84 felix.detectXercesOverride("foo.bar,org.apache.xerces.util");
85 fail("Should fail validation");
86 }
87 catch (OsgiContainerException ex)
88 {
89
90 }
91
92 try
93 {
94 felix.detectXercesOverride("org.apache.xerces.util");
95 fail("Should fail validation");
96 }
97 catch (OsgiContainerException ex)
98 {
99
100 }
101
102 try
103 {
104 felix.detectXercesOverride("org.apache.xerces.util,bar.baz");
105 fail("Should fail validation");
106 }
107 catch (OsgiContainerException ex)
108 {
109
110 }
111
112 }
113
114 public void testDeleteDirectory() throws IOException
115 {
116 File dir = new File(tmpdir, "base");
117 dir.mkdir();
118 File subdir = new File(dir, "subdir");
119 subdir.mkdir();
120 File kid = File.createTempFile("foo", "bar", subdir);
121
122 FileUtils.deleteDirectory(dir);
123 assertTrue(!kid.exists());
124 assertTrue(!subdir.exists());
125 assertTrue(!dir.exists());
126 }
127
128 public void testStartStop()
129 {
130 FilenameFilter filter = new FilenameFilter()
131 {
132 public boolean accept(File file, String s)
133 {
134 return s.startsWith("felix");
135 }
136 };
137 int filesNamedFelix = tmpdir.listFiles(filter).length;
138 felix.start();
139 assertTrue(felix.isRunning());
140 assertEquals(1, felix.getBundles().length);
141 felix.stop();
142 assertEquals(filesNamedFelix, tmpdir.listFiles(filter).length);
143 }
144
145 public void testInstallBundle() throws URISyntaxException
146 {
147 felix.start();
148 assertEquals(1, felix.getBundles().length);
149 File jar = new File(getClass().getResource("/myapp-1.0.jar").toURI());
150 felix.installBundle(jar);
151 assertEquals(2, felix.getBundles().length);
152 }
153
154 public void testBootDelegation() throws Exception
155 {
156
157 File pluginServer = new PluginJarBuilder("plugin")
158 .addResource("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" +
159 "Bundle-Version: 1.0\n" +
160 "Bundle-SymbolicName: my.server\n" +
161 "Bundle-ManifestVersion: 2\n" +
162 "Export-Package: my.server\n")
163 .addJava("my.server.ServerClass", "package my.server; public class ServerClass extends junit.framework.TestCase {}")
164 .build();
165
166
167
168
169 File pluginClient = new PluginJarBuilder("plugin")
170 .addResource("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" +
171 "Bundle-Version: 1.0\n" +
172 "Bundle-SymbolicName: my.client\n" +
173 "Bundle-ManifestVersion: 2\n" +
174 "Import-Package: my.server\n")
175 .addJava("my.client.ClientClass", "package my.client; public class ClientClass {" +
176 "public ClientClass() throws ClassNotFoundException {" +
177 "getClass().getClassLoader().loadClass(\"my.server.ServerClass\");" +
178 "}}")
179 .build();
180
181 felix.start();
182 Bundle serverBundle = felix.installBundle(pluginServer);
183 serverBundle.start();
184 Bundle clientBundle = felix.installBundle(pluginClient);
185 clientBundle.start();
186 try
187 {
188 clientBundle.loadClass("my.client.ClientClass").newInstance();
189 fail("Expected exception: NoClassDefFoundError for junit.framework.TestCase");
190 }
191 catch (NoClassDefFoundError expected)
192 {
193 }
194 felix.stop();
195
196
197 System.setProperty("atlassian.org.osgi.framework.bootdelegation", "junit.framework,junit.framework.*");
198 try
199 {
200 felix.start();
201 serverBundle = felix.installBundle(pluginServer);
202 serverBundle.start();
203 clientBundle = felix.installBundle(pluginClient);
204 clientBundle.start();
205 clientBundle.loadClass("my.client.ClientClass").newInstance();
206 felix.stop();
207 }
208 finally
209 {
210 System.clearProperty("atlassian.org.osgi.framework.bootdelegation");
211 }
212 }
213
214 public void testInstallBundleTwice() throws URISyntaxException, IOException, BundleException
215 {
216 File plugin = new PluginJarBuilder("plugin")
217 .addResource("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" +
218 "Import-Package: javax.swing\n" +
219 "Bundle-Version: 1.0\n" +
220 "Bundle-SymbolicName: my.foo.symbolicName\n" +
221 "Bundle-ManifestVersion: 2\n")
222 .addResource("foo.txt", "foo")
223 .build();
224
225 File pluginUpdate = new PluginJarBuilder("plugin")
226 .addResource("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" +
227 "Import-Package: javax.swing\n" +
228 "Bundle-Version: 1.0\n" +
229 "Bundle-SymbolicName: my.foo.symbolicName\n" +
230 "Bundle-ManifestVersion: 2\n")
231 .addResource("bar.txt", "bar")
232 .build();
233
234 felix.start();
235 assertEquals(1, felix.getBundles().length);
236 Bundle bundle = felix.installBundle(plugin);
237 assertEquals(2, felix.getBundles().length);
238 assertEquals("my.foo.symbolicName", bundle.getSymbolicName());
239 assertEquals("1.0", bundle.getHeaders().get(Constants.BUNDLE_VERSION));
240 assertEquals(Bundle.INSTALLED, bundle.getState());
241 assertNotNull(bundle.getResource("foo.txt"));
242 assertNull(bundle.getResource("bar.txt"));
243 bundle.start();
244 assertEquals(Bundle.ACTIVE, bundle.getState());
245 Bundle bundleUpdate = felix.installBundle(pluginUpdate);
246 assertEquals(2, felix.getBundles().length);
247 assertEquals(Bundle.INSTALLED, bundleUpdate.getState());
248 bundleUpdate.start();
249 assertEquals(Bundle.ACTIVE, bundleUpdate.getState());
250
251 assertNotNull(bundleUpdate.getResource("bar.txt"));
252 }
253
254 public void testInstallBundleTwiceDifferentSymbolicNames() throws URISyntaxException, IOException, BundleException
255 {
256 File plugin = new PluginJarBuilder("plugin")
257 .addResource("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" +
258 "Import-Package: javax.swing\n" +
259 "Bundle-Version: 1.0\n" +
260 "Bundle-SymbolicName: my.foo\n" +
261 "Atlassian-Plugin-Key: my.foo.symbolicName\n" +
262 "Bundle-ManifestVersion: 2\n")
263 .addResource("foo.txt", "foo")
264 .build();
265
266 File pluginUpdate = new PluginJarBuilder("plugin")
267 .addResource("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" +
268 "Import-Package: javax.swing\n" +
269 "Bundle-Version: 1.0\n" +
270 "Atlassian-Plugin-Key: my.foo.symbolicName\n" +
271 "Bundle-SymbolicName: my.bar\n" +
272 "Bundle-ManifestVersion: 2\n")
273 .addResource("bar.txt", "bar")
274 .build();
275
276 felix.start();
277 assertEquals(1, felix.getBundles().length);
278 Bundle bundle = felix.installBundle(plugin);
279 assertEquals(2, felix.getBundles().length);
280 assertEquals("1.0", bundle.getHeaders().get(Constants.BUNDLE_VERSION));
281 assertEquals(Bundle.INSTALLED, bundle.getState());
282 assertNotNull(bundle.getResource("foo.txt"));
283 assertNull(bundle.getResource("bar.txt"));
284 bundle.start();
285 assertEquals(Bundle.ACTIVE, bundle.getState());
286 Bundle bundleUpdate = felix.installBundle(pluginUpdate);
287 assertEquals(2, felix.getBundles().length);
288 assertEquals(Bundle.INSTALLED, bundleUpdate.getState());
289 bundleUpdate.start();
290 assertEquals(Bundle.ACTIVE, bundleUpdate.getState());
291
292 assertNotNull(bundleUpdate.getResource("bar.txt"));
293 }
294
295 public void testInstallFailure() throws Exception
296 {
297 File plugin = new PluginJarBuilder("plugin")
298 .addResource("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" +
299 "Bundle-Version: 1.0\n" +
300 "Import-Package: foo.missing.package\n" +
301 "Bundle-SymbolicName: my.foo.symbolicName\n" +
302 "Bundle-ManifestVersion: 2\n" )
303 .build();
304 felix.start();
305
306 Bundle bundle = felix.installBundle(plugin);
307 try {
308 bundle.loadClass("foo.bar");
309 fail("Should have thrown exception");
310 } catch (ClassNotFoundException ex) {
311
312 }
313 }
314
315 public void testServiceTrackerIsClosed() throws URISyntaxException, IOException, BundleException
316 {
317 felix.start();
318
319 ServiceTracker tracker = felix.getServiceTracker(PackageAdmin.class.getCanonicalName());
320 Object[] trackedServices = tracker.getServices();
321 assertNotNull("Unable to perform the test: no service to track", trackedServices);
322
323 tracker.close();
324
325 trackedServices = tracker.getServices();
326 assertNull(trackedServices);
327 }
328
329 public void testServiceTrackerActuallyTracksStuff() throws Exception
330 {
331 startFelixWithListAsOSGiService();
332
333 ServiceTracker tracker = felix.getServiceTracker(List.class.getName());
334 Object[] trackedServices = tracker.getServices();
335 assertEquals(1, trackedServices.length);
336 assertEquals(ImmutableList.of("blah"), trackedServices[0]);
337 }
338
339 public void testServiceTrackerCutomizerIsInPlace() throws Exception
340 {
341 startFelixWithListAsOSGiService();
342 final CountDownLatch latch = new CountDownLatch(1);
343
344 felix.getServiceTracker(List.class.getName(), new ServiceTrackerCustomizer()
345 {
346 public Object addingService(ServiceReference reference)
347 {
348 latch.countDown();
349 return reference;
350 }
351 public void modifiedService(ServiceReference reference, Object service)
352 {
353 }
354 public void removedService(ServiceReference reference, Object service)
355 {
356 }
357 });
358 assertTrue(latch.await(10, TimeUnit.SECONDS));
359 }
360
361 private void startFelixWithListAsOSGiService() throws Exception
362 {
363 File plugin = new PluginJarBuilder("plugin")
364 .addResource("META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n" +
365 "Bundle-Version: 1.0\n" +
366 "Atlassian-Plugin-Key: my.foo.symbolicName\n" +
367 "Bundle-Name: my.bar\n" +
368 "Bundle-SymbolicName: my.bar\n" +
369 "Bundle-ManifestVersion: 2\n" +
370 "Bundle-Activator: my.MyActivator\n" +
371 "Import-Package: org.osgi.framework\n")
372 .addJava("my.MyActivator", "package my;" +
373 "import org.osgi.framework.ServiceRegistration;\n" +
374 "import org.osgi.framework.BundleActivator;\n" +
375 "import org.osgi.framework.BundleContext;\n" +
376 "import java.util.*;\n" +
377 "public class MyActivator implements BundleActivator {\n" +
378 " private ServiceRegistration registration;\n" +
379 " public void start(BundleContext context) throws Exception\n" +
380 " {\n" +
381 " context.registerService(List.class.getName(), Arrays.asList(new Object[]{ \"blah\" }), new Properties());\n" +
382 " }\n" +
383 " public void stop(BundleContext context) throws Exception {}\n" +
384 "}")
385 .build();
386 felix.start();
387 Bundle bundle = felix.installBundle(plugin);
388 bundle.start();
389 }
390
391 public void testRuntimeEnvironment()
392 {
393 try
394 {
395 String formatStr = String.format("java.version=%s,plugin.enable.timeout=%%d", System.getProperty("java.version"));
396
397 assertEquals(String.format(formatStr, setPluginTimeout(3883)), felix.getRuntimeEnvironment());
398 }
399 finally
400 {
401 setPluginTimeout(-1);
402 }
403 }
404
405 public void testRuntimeEnvironmentIntegration() throws Exception
406 {
407 try
408 {
409 int timeout = 678;
410 setPluginTimeout(timeout);
411
412 startFelixWithListAsOSGiService();
413
414 File versionFile = new File(new File(tmpdir, "transformed-plugins"), "cache.key");
415 assertTrue(versionFile.exists());
416 String txt = FileUtils.readFileToString(versionFile);
417
418 ExportsBuilder eBuilder = new ExportsBuilder();
419 String systemExports = eBuilder.getExports(new ArrayList<HostComponentRegistration>(), new DefaultPackageScannerConfiguration());
420
421
422 String expectedKey = String.format("java.version=%s,plugin.enable.timeout=%d,%s", System.getProperty("java.version"), timeout, systemExports);
423 assertEquals(expectedKey.hashCode(), Integer.parseInt(txt));
424 }
425 finally
426 {
427 setPluginTimeout(-1);
428 }
429 }
430
431 private int setPluginTimeout(int timeout)
432 {
433 if (timeout <= 0)
434 {
435 System.clearProperty(PluginUtils.ATLASSIAN_PLUGINS_ENABLE_WAIT);
436 return 60;
437 }
438 else
439 {
440 System.setProperty(PluginUtils.ATLASSIAN_PLUGINS_ENABLE_WAIT, String.valueOf(timeout));
441 return timeout;
442 }
443 }
444 }