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