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(); + } +}