Implement osgi.identity namespace for fragments. Fixes FELIX-4324.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1555700 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
index e139fbd..796bc83 100644
--- a/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
+++ b/framework/src/main/java/org/apache/felix/framework/BundleWiringImpl.java
@@ -40,6 +40,7 @@
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.TreeSet;
+
import org.apache.felix.framework.cache.Content;
import org.apache.felix.framework.cache.JarContent;
import org.apache.felix.framework.capabilityset.SimpleFilter;
@@ -63,6 +64,7 @@
import org.osgi.framework.ServiceReference;
import org.osgi.framework.hooks.weaving.WeavingException;
import org.osgi.framework.hooks.weaving.WeavingHook;
+import org.osgi.framework.namespace.IdentityNamespace;
import org.osgi.framework.wiring.BundleCapability;
import org.osgi.framework.wiring.BundleRequirement;
import org.osgi.framework.wiring.BundleRevision;
@@ -254,19 +256,32 @@
// Calculate resolved list of capabilities, which includes:
// 1. All capabilities from host and any fragments except for exported
// packages that we have an import (i.e., the export was substituted).
- // And nothing else at this time. Fragments currently have no capabilities.
+ // 2. For fragments the identity capability only.
+ // And nothing else at this time.
boolean isFragment = Util.isFragment(revision);
- List<BundleCapability> capList = (isFragment)
- ? Collections.EMPTY_LIST
- : new ArrayList<BundleCapability>();
+ List<BundleCapability> capList = new ArrayList<BundleCapability>();
// Also keep track of whether any resolved package capabilities are filtered.
Map<String, List<List<String>>> includedPkgFilters =
new HashMap<String, List<List<String>>>();
Map<String, List<List<String>>> excludedPkgFilters =
new HashMap<String, List<List<String>>>();
-// TODO: OSGi R4.4 - Fragments currently have no capabilities, but they may
-// have an identity capability in the future.
- if (!isFragment)
+
+ if (isFragment)
+ {
+ // This is a fragment, add its identity capability
+ for (BundleCapability cap : m_revision.getDeclaredCapabilities(null))
+ {
+ if (IdentityNamespace.IDENTITY_NAMESPACE.equals(cap.getNamespace()))
+ {
+ String effective = cap.getDirectives().get(Constants.EFFECTIVE_DIRECTIVE);
+ if ((effective == null) || (effective.equals(Constants.EFFECTIVE_RESOLVE)))
+ {
+ capList.add(cap);
+ }
+ }
+ }
+ }
+ else
{
for (BundleCapability cap : m_revision.getDeclaredCapabilities(null))
{
@@ -308,8 +323,11 @@
{
for (BundleCapability cap : fragment.getDeclaredCapabilities(null))
{
-// TODO: OSGi R4.4 - OSGi R4.4 may introduce an identity capability, if so
-// that will need to be excluded from here.
+ if (IdentityNamespace.IDENTITY_NAMESPACE.equals(cap.getNamespace())) {
+ // The identity capability is not transferred from the fragment to the bundle
+ continue;
+ }
+
if (!cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
|| (cap.getNamespace().equals(BundleRevision.PACKAGE_NAMESPACE)
&& !imports.contains(cap.getAttributes()
diff --git a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
index 482bfad..1742c02 100644
--- a/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
+++ b/framework/src/main/java/org/apache/felix/framework/util/manifestparser/ManifestParser.java
@@ -133,15 +133,12 @@
bundleCap.getDirectives(),
hostAttrs));
}
-
- //
- // Add the osgi.identity capability.
- // TODO support this for fragments. The main thing with supporting this
- // for fragments is that the identity capability should not be exposed
- // through the host's bundle wiring.
- //
- capList.add(addIdentityCapability(owner, headerMap, bundleCap));
}
+
+ //
+ // Add the osgi.identity capability.
+ //
+ capList.add(addIdentityCapability(owner, headerMap, bundleCap));
}
// Verify that bundle symbolic name is specified.
diff --git a/framework/src/test/java/org/apache/felix/framework/RequirementsCapabilitiesTest.java b/framework/src/test/java/org/apache/felix/framework/RequirementsCapabilitiesTest.java
new file mode 100644
index 0000000..8cc34e6
--- /dev/null
+++ b/framework/src/test/java/org/apache/felix/framework/RequirementsCapabilitiesTest.java
@@ -0,0 +1,267 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.framework;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.jar.JarOutputStream;
+import java.util.jar.Manifest;
+
+import junit.framework.TestCase;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.Version;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.resource.Capability;
+import org.osgi.resource.Resource;
+
+public class RequirementsCapabilitiesTest extends TestCase
+{
+ private File tempDir;
+ private Framework felix;
+ private File cacheDir;
+
+ @Override
+ protected void setUp() throws Exception
+ {
+ super.setUp();
+ tempDir = File.createTempFile("felix-temp", ".dir");
+ assertTrue("precondition", tempDir.delete());
+ assertTrue("precondition", tempDir.mkdirs());
+
+ cacheDir = new File(tempDir, "felix-cache");
+ assertTrue("precondition", cacheDir.mkdir());
+
+ String cache = cacheDir.getPath();
+
+ Map<String,String> params = new HashMap<String, String>();
+ params.put("felix.cache.profiledir", cache);
+ params.put("felix.cache.dir", cache);
+ params.put(Constants.FRAMEWORK_STORAGE, cache);
+
+ felix = new Felix(params);
+ felix.init();
+ felix.start();
+ }
+
+ @Override
+ protected void tearDown() throws Exception
+ {
+ super.tearDown();
+
+ felix.stop(); // Note that this method is async
+ felix = null;
+
+ deleteDir(tempDir);
+ tempDir = null;
+ cacheDir = null;
+ }
+
+ public void testIdentityCapabilityBundleFragment() throws Exception
+ {
+ String bmf = "Bundle-SymbolicName: cap.bundle\n"
+ + "Bundle-Version: 1.2.3.Blah\n"
+ + "Bundle-ManifestVersion: 2\n"
+ + "Import-Package: org.osgi.framework\n";
+ File bundleFile = createBundle(bmf);
+
+ String fmf = "Bundle-SymbolicName: cap.frag\n"
+ + "Bundle-Version: 1.0.0\n"
+ + "Fragment-Host: cap.bundle\n"
+ + "Bundle-ManifestVersion: 2\n"
+ + "Export-Package: org.foo.bar;version=\"2.0.0\"\n"
+ + "Import-Package: org.osgi.util.tracker\n";
+ File fragFile = createBundle(fmf);
+
+ Bundle b = felix.getBundleContext().installBundle(bundleFile.toURI().toASCIIString());
+ Bundle f = felix.getBundleContext().installBundle(fragFile.toURI().toASCIIString());
+
+ // Check the bundle capabilities.
+ // First check the capabilities on the Bundle Revision, which is available on installed bundles
+ BundleRevision bbr = b.adapt(BundleRevision.class);
+ List<Capability> bwbCaps = bbr.getCapabilities("osgi.wiring.bundle");
+ assertEquals(1, bwbCaps.size());
+
+ Map<String, Object> expectedBWBAttrs = new HashMap<String, Object>();
+ expectedBWBAttrs.put("osgi.wiring.bundle", "cap.bundle");
+ expectedBWBAttrs.put("bundle-version", Version.parseVersion("1.2.3.Blah"));
+ Capability expectedBWBCap = new TestCapability("osgi.wiring.bundle",
+ expectedBWBAttrs, Collections.<String, String>emptyMap());
+ assertCapsEquals(expectedBWBCap, bwbCaps.get(0));
+
+ List<Capability> bwhCaps = bbr.getCapabilities("osgi.wiring.host");
+ assertEquals(1, bwhCaps.size());
+
+ Map<String, Object> expectedBWHAttrs = new HashMap<String, Object>();
+ expectedBWHAttrs.put("osgi.wiring.host", "cap.bundle");
+ expectedBWHAttrs.put("bundle-version", Version.parseVersion("1.2.3.Blah"));
+ Capability expectedBWHCap = new TestCapability("osgi.wiring.host",
+ expectedBWHAttrs, Collections.<String, String>emptyMap());
+ assertCapsEquals(expectedBWHCap, bwhCaps.get(0));
+
+ List<Capability> bwiCaps = bbr.getCapabilities("osgi.identity");
+ assertEquals(1, bwiCaps.size());
+
+ Map<String, Object> expectedBWIAttrs = new HashMap<String, Object>();
+ expectedBWIAttrs.put("osgi.identity", "cap.bundle");
+ expectedBWIAttrs.put("type", "osgi.bundle");
+ expectedBWIAttrs.put("version", Version.parseVersion("1.2.3.Blah"));
+ Capability expectedBWICap = new TestCapability("osgi.identity",
+ expectedBWIAttrs, Collections.<String, String>emptyMap());
+ assertCapsEquals(expectedBWICap, bwiCaps.get(0));
+
+ assertEquals("The Bundle should not directly expose osgi.wiring.package",
+ 0, bbr.getCapabilities("osgi.wiring.package").size());
+
+ // Check the fragment's capabilities.
+ // First check the capabilities on the Bundle Revision, which is available on installed fragments
+ BundleRevision fbr = f.adapt(BundleRevision.class);
+ List<Capability> fwpCaps = fbr.getCapabilities("osgi.wiring.package");
+ assertEquals(1, fwpCaps.size());
+
+ Map<String, Object> expectedFWAttrs = new HashMap<String, Object>();
+ expectedFWAttrs.put("osgi.wiring.package", "org.foo.bar");
+ expectedFWAttrs.put("version", Version.parseVersion("2"));
+ expectedFWAttrs.put("bundle-symbolic-name", "cap.frag");
+ expectedFWAttrs.put("bundle-version", Version.parseVersion("1.0.0"));
+ Capability expectedFWCap = new TestCapability("osgi.wiring.package",
+ expectedFWAttrs, Collections.<String, String>emptyMap());
+ assertCapsEquals(expectedFWCap, fwpCaps.get(0));
+
+ List<Capability> fiCaps = fbr.getCapabilities("osgi.identity");
+ assertEquals(1, fiCaps.size());
+ Map<String, Object> expectedFIAttrs = new HashMap<String, Object>();
+ expectedFIAttrs.put("osgi.identity", "cap.frag");
+ expectedFIAttrs.put("type", "osgi.fragment");
+ expectedFIAttrs.put("version", Version.parseVersion("1.0.0"));
+ Capability expectedFICap = new TestCapability("osgi.identity",
+ expectedFIAttrs, Collections.<String, String>emptyMap());
+ assertCapsEquals(expectedFICap, fiCaps.get(0));
+
+ // Start the bundle. This will make the BundleWiring available on both the bundle and the fragment
+ b.start();
+
+ // Check the Bundle Wiring on the fragment. It should only contain the osgi.identity capability
+ // All the other capabilities should have migrated to the bundle's BundleWiring.
+ BundleWiring fbw = f.adapt(BundleWiring.class);
+ List<BundleCapability> fbwCaps = fbw.getCapabilities(null);
+ assertEquals("Fragment should only have 1 capability: it's osgi.identity", 1, fbwCaps.size());
+ assertCapsEquals(expectedFICap, fbwCaps.get(0));
+
+ // Check the Bundle Wiring on the bundle. It should contain all the capabilities originally on the
+ // bundle and also contain the osgi.wiring.package capability from the fragment.
+ BundleWiring bbw = b.adapt(BundleWiring.class);
+ List<BundleCapability> bwbCaps2 = bbw.getCapabilities("osgi.wiring.bundle");
+ assertEquals(1, bwbCaps2.size());
+ assertCapsEquals(expectedBWBCap, bwbCaps2.get(0));
+ List<BundleCapability> bwhCaps2 = bbw.getCapabilities("osgi.wiring.host");
+ assertEquals(1, bwhCaps2.size());
+ assertCapsEquals(expectedBWHCap, bwhCaps2.get(0));
+ List<BundleCapability> bwiCaps2 = bbw.getCapabilities("osgi.identity");
+ assertEquals(1, bwiCaps2.size());
+ assertCapsEquals(expectedBWICap, bwiCaps2.get(0));
+ List<BundleCapability> bwpCaps2 = bbw.getCapabilities("osgi.wiring.package");
+ assertEquals("Bundle should have inherited the osgi.wiring.package capability from the fragment",
+ 1, bwpCaps2.size());
+ assertCapsEquals(expectedFWCap, bwpCaps2.get(0));
+ }
+
+ private File createBundle(String manifest) throws IOException
+ {
+ File f = File.createTempFile("felix-bundle", ".jar", tempDir);
+
+ Manifest mf = new Manifest(new ByteArrayInputStream(manifest.getBytes("utf-8")));
+ mf.getMainAttributes().putValue("Manifest-Version", "1.0");
+ JarOutputStream os = new JarOutputStream(new FileOutputStream(f), mf);
+ os.close();
+ return f;
+ }
+
+ private static void assertCapsEquals(Capability expected, Capability actual)
+ {
+ assertEquals(expected.getNamespace(), actual.getNamespace());
+ assertSubMap(expected.getAttributes(), actual.getAttributes());
+ assertSubMap(expected.getDirectives(), actual.getDirectives());
+ // We ignore the resource in the comparison
+ }
+
+ private static void assertSubMap(Map<?,?> subMap, Map<?,?> fullMap)
+ {
+ for (Map.Entry<?,?> entry : subMap.entrySet())
+ {
+ assertEquals(entry.getValue(), fullMap.get(entry.getKey()));
+ }
+ }
+
+ private static void deleteDir(File root) throws IOException
+ {
+ if (root.isDirectory())
+ {
+ for (File file : root.listFiles())
+ {
+ deleteDir(file);
+ }
+ }
+ assertTrue(root.delete());
+ }
+
+ static class TestCapability implements Capability
+ {
+ private final String namespace;
+ private final Map<String, Object> attributes;
+ private final Map<String, String> directives;
+
+ TestCapability(String ns, Map<String,Object> attrs, Map<String,String> dirs)
+ {
+ namespace = ns;
+ attributes = attrs;
+ directives = dirs;
+ }
+
+ public String getNamespace()
+ {
+ return namespace;
+ }
+
+ public Map<String, Object> getAttributes()
+ {
+ return attributes;
+ }
+
+ public Map<String, String> getDirectives()
+ {
+ return directives;
+ }
+
+ public Resource getResource()
+ {
+ return null;
+ }
+ }
+}
diff --git a/framework/src/test/java/org/apache/felix/framework/StartStopBundleTest.java b/framework/src/test/java/org/apache/felix/framework/StartStopBundleTest.java
index af50734..315606c 100644
--- a/framework/src/test/java/org/apache/felix/framework/StartStopBundleTest.java
+++ b/framework/src/test/java/org/apache/felix/framework/StartStopBundleTest.java
@@ -25,12 +25,12 @@
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
-import java.util.concurrent.CountDownLatch;
import java.util.jar.JarOutputStream;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import junit.framework.TestCase;
+
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
@@ -41,7 +41,6 @@
public class StartStopBundleTest extends TestCase
{
public static final int DELAY = 1000;
- private File cacheDir;
public void testStartStopBundle() throws Exception
{
@@ -52,7 +51,7 @@
+ "org.osgi.service.startlevel; version=1.1.0,"
+ "org.osgi.util.tracker; version=1.3.3,"
+ "org.osgi.service.url; version=1.0.0");
- cacheDir = File.createTempFile("felix-cache", ".dir");
+ File cacheDir = File.createTempFile("felix-cache", ".dir");
cacheDir.delete();
cacheDir.mkdirs();
String cache = cacheDir.getPath();
@@ -64,66 +63,69 @@
+ "Bundle-Version: 1.1.0\n"
+ "Bundle-ManifestVersion: 2\n"
+ "Import-Package: org.osgi.framework\n";
- File bundleFile = createBundle(mf);
+ File bundleFile = createBundle(mf, cacheDir);
Framework f = new Felix(params);
f.init();
f.start();
- final Bundle bundle = f.getBundleContext().installBundle(bundleFile.toURI().toString());
- final CountDownLatch latch = new CountDownLatch(1);
+ try {
+ final Bundle bundle = f.getBundleContext().installBundle(bundleFile.toURI().toString());
- new Thread()
- {
- public void run()
+ new Thread()
{
- try
+ public void run()
{
- bundle.start();
+ try
+ {
+ bundle.start();
+ }
+ catch (BundleException e)
+ {
+ e.printStackTrace();
+ }
}
- catch (BundleException e)
- {
- e.printStackTrace();
- }
- }
- }.start();
- Thread.sleep(DELAY / 4);
- long t0 = System.currentTimeMillis();
- bundle.stop();
- long t1 = System.currentTimeMillis();
+ }.start();
+ Thread.sleep(DELAY / 4);
+ long t0 = System.currentTimeMillis();
+ bundle.stop();
+ long t1 = System.currentTimeMillis();
- assertEquals(Bundle.RESOLVED, bundle.getState());
- assertTrue((t1 - t0) > DELAY / 2);
+ assertEquals(Bundle.RESOLVED, bundle.getState());
+ assertTrue((t1 - t0) > DELAY / 2);
- bundle.start();
+ bundle.start();
- new Thread()
- {
- public void run()
+ new Thread()
{
- try
+ public void run()
{
- bundle.stop();
+ try
+ {
+ bundle.stop();
+ }
+ catch (BundleException e)
+ {
+ e.printStackTrace();
+ }
}
- catch (BundleException e)
- {
- e.printStackTrace();
- }
- }
- }.start();
- Thread.sleep(DELAY / 4);
- t0 = System.currentTimeMillis();
- bundle.start();
- t1 = System.currentTimeMillis();
+ }.start();
+ Thread.sleep(DELAY / 4);
+ t0 = System.currentTimeMillis();
+ bundle.start();
+ t1 = System.currentTimeMillis();
- assertEquals(Bundle.ACTIVE, bundle.getState());
- assertTrue((t1 - t0) > DELAY / 2);
+ assertEquals(Bundle.ACTIVE, bundle.getState());
+ assertTrue((t1 - t0) > DELAY / 2);
+ } finally {
+ f.stop();
+ deleteDir(cacheDir);
+ }
}
- private static File createBundle(String manifest) throws IOException
+ private static File createBundle(String manifest, File tempDir) throws IOException
{
- File f = File.createTempFile("felix-bundle", ".jar");
- f.deleteOnExit();
+ File f = File.createTempFile("felix-bundle", ".jar", tempDir);
Manifest mf = new Manifest(new ByteArrayInputStream(manifest.getBytes("utf-8")));
mf.getMainAttributes().putValue("Manifest-Version", "1.0");
@@ -143,7 +145,18 @@
return f;
}
- public static class TestBundleActivator implements BundleActivator
+ private static void deleteDir(File root) throws IOException
+ {
+ if (root.isDirectory())
+ {
+ for (File file : root.listFiles())
+ {
+ deleteDir(file);
+ }
+ }
+ assertTrue(root.delete());
+ }
+ public static class TestBundleActivator implements BundleActivator
{
public void start(BundleContext context) throws Exception
{