Author: davidb
Date: Sat May 24 12:33:56 2014
New Revision: 1597276

URL: http://svn.apache.org/r1597276
Log:
[FELIX-3239] PackageAdmin#getExportedPackages does not work on packages 
exported by attached fragments

This commit fixes the issue.
Unit test included.

Added:
    
felix/trunk/framework/src/test/java/org/apache/felix/framework/PackageAdminImplTest.java
Modified:
    felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java

Modified: 
felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java?rev=1597276&r1=1597275&r2=1597276&view=diff
==============================================================================
--- felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java 
(original)
+++ felix/trunk/framework/src/main/java/org/apache/felix/framework/Felix.java 
Sat May 24 12:33:56 2014
@@ -64,10 +64,12 @@ import org.osgi.framework.ServicePermiss
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.ServiceRegistration;
 import org.osgi.framework.launch.Framework;
+import org.osgi.framework.namespace.HostNamespace;
 import org.osgi.framework.startlevel.FrameworkStartLevel;
 import org.osgi.framework.wiring.BundleCapability;
 import org.osgi.framework.wiring.BundleRevision;
 import org.osgi.framework.wiring.BundleRevisions;
+import org.osgi.framework.wiring.BundleWire;
 import org.osgi.framework.wiring.BundleWiring;
 import org.osgi.framework.wiring.FrameworkWiring;
 import org.osgi.service.packageadmin.ExportedPackage;
@@ -3747,22 +3749,40 @@ public class Felix extends BundleImpl im
                 // newest to oldest. We assume that the first revision found to
                 // be exporting the package is the provider of the package,
                 // which makes sense since it must have been resolved first.
-                List<BundleRevision> revisions =
+                List<BundleRevision> originRevisions =
                     bundle.adapt(BundleRevisions.class).getRevisions();
-                for (int i = revisions.size() - 1; i >= 0; i--)
+                for (int i = originRevisions.size() - 1; i >= 0; i--)
                 {
-                    BundleRevision br = revisions.get(i);
-                    List<BundleCapability> caps = (br.getWiring() == null)
-                        ? br.getDeclaredCapabilities(null)
-                        : br.getWiring().getCapabilities(null);
-                    for (BundleCapability cap : caps)
+                    BundleRevision originBr = originRevisions.get(i);
+                    List<BundleRevision> revisions = 
Collections.singletonList(originBr);
+
+                    if ((originBr.getTypes() & BundleRevision.TYPE_FRAGMENT) 
!= 0)
                     {
-                        if (cap.getNamespace().equals(req.getNamespace())
-                            && CapabilitySet.matches(cap, req.getFilter()))
+                        // If it's a fragment, find the revisions of the 
attached
+                        // bundle(s) and work with those instead. Note that 
fragments
+                        // can be attached to multiple hosts...
+                        revisions = new ArrayList<BundleRevision>();
+
+                        for (BundleWire bw : 
originBr.getWiring().getRequiredWires(HostNamespace.HOST_NAMESPACE))
                         {
-                            pkgs.add(
-                                new ExportedPackageImpl(
-                                    this, (BundleImpl) bundle, br, cap));
+                            
revisions.add(bw.getProviderWiring().getRevision());
+                        }
+                    }
+
+                    for (BundleRevision br : revisions)
+                    {
+                        List<BundleCapability> caps = (br.getWiring() == null)
+                            ? br.getDeclaredCapabilities(null)
+                            : br.getWiring().getCapabilities(null);
+                        for (BundleCapability cap : caps)
+                        {
+                            if (cap.getNamespace().equals(req.getNamespace())
+                                && CapabilitySet.matches(cap, req.getFilter()))
+                            {
+                                pkgs.add(
+                                    new ExportedPackageImpl(
+                                        this, (BundleImpl) br.getBundle(), br, 
cap));
+                            }
                         }
                     }
                 }

Added: 
felix/trunk/framework/src/test/java/org/apache/felix/framework/PackageAdminImplTest.java
URL: 
http://svn.apache.org/viewvc/felix/trunk/framework/src/test/java/org/apache/felix/framework/PackageAdminImplTest.java?rev=1597276&view=auto
==============================================================================
--- 
felix/trunk/framework/src/test/java/org/apache/felix/framework/PackageAdminImplTest.java
 (added)
+++ 
felix/trunk/framework/src/test/java/org/apache/felix/framework/PackageAdminImplTest.java
 Sat May 24 12:33:56 2014
@@ -0,0 +1,159 @@
+/*
+ * 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.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+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.service.packageadmin.ExportedPackage;
+
+public class PackageAdminImplTest extends TestCase
+{
+    private File tempDir;
+    private Felix 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 testExportedPackages() throws Exception
+    {
+        String bmf = "Bundle-SymbolicName: pkg.bundle\n"
+                + "Bundle-Version: 1\n"
+                + "Bundle-ManifestVersion: 2\n"
+                + "Export-Package: org.foo.bundle\n";
+        File bundleFile = createBundle(bmf);
+
+        String fmf = "Bundle-SymbolicName: pkg.frag\n"
+                + "Bundle-Version: 1\n"
+                + "Fragment-Host: pkg.bundle\n"
+                + "Bundle-ManifestVersion: 2\n"
+                + "Export-Package: org.foo.fragment;version=\"2.0.0\"\n";
+        File fragFile = createBundle(fmf);
+
+        Bundle b = 
felix.getBundleContext().installBundle(bundleFile.toURI().toASCIIString());
+        Bundle f = 
felix.getBundleContext().installBundle(fragFile.toURI().toASCIIString());
+        b.start();
+
+        try
+        {
+            PackageAdminImpl pa = new PackageAdminImpl(felix);
+            assertEquals(b, 
pa.getExportedPackage("org.foo.bundle").getExportingBundle());
+            assertEquals(b, 
pa.getExportedPackage("org.foo.fragment").getExportingBundle());
+
+            Set<String> expected = new HashSet<String>();
+            expected.addAll(Arrays.asList("org.foo.bundle", 
"org.foo.fragment"));
+
+            Set<String> actual = new HashSet<String>();
+            for (ExportedPackage ep : pa.getExportedPackages(b))
+            {
+                actual.add(ep.getName());
+                assertEquals(b, ep.getExportingBundle());
+            }
+            assertEquals(expected, actual);
+
+            ExportedPackage[] bundlePkgs = 
pa.getExportedPackages("org.foo.bundle");
+            assertEquals(1, bundlePkgs.length);
+            assertEquals(b, bundlePkgs[0].getExportingBundle());
+            assertEquals(new Version("0"), bundlePkgs[0].getVersion());
+
+            ExportedPackage[] fragPkgs = 
pa.getExportedPackages("org.foo.fragment");
+            assertEquals(1, fragPkgs.length);
+            assertEquals("The fragment package should be exposed through the 
bundle",
+                    b, fragPkgs[0].getExportingBundle());
+            assertEquals(new Version("2"), fragPkgs[0].getVersion());
+        }
+        finally
+        {
+            b.stop();
+            b.uninstall();
+            f.uninstall();
+        }
+    }
+
+    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 deleteDir(File root) throws IOException
+    {
+        if (root.isDirectory())
+        {
+            for (File file : root.listFiles())
+            {
+                deleteDir(file);
+            }
+        }
+        root.delete();
+    }
+}


Reply via email to