You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@felix.apache.org by pa...@apache.org on 2018/04/03 09:40:25 UTC
svn commit: r1828212 - in /felix/trunk/osgi-r7/framework/src:
main/java/org/apache/felix/framework/ResolveContextImpl.java
main/java/org/apache/felix/framework/StatefulResolver.java
test/java/org/apache/felix/framework/ResolveTest.java
Author: pauls
Date: Tue Apr 3 09:40:24 2018
New Revision: 1828212
URL: http://svn.apache.org/viewvc?rev=1828212&view=rev
Log:
FELIX-5818: Only resolve fragments that are required on dynamic import.
Added:
felix/trunk/osgi-r7/framework/src/test/java/org/apache/felix/framework/ResolveTest.java
Modified:
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/ResolveContextImpl.java
felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java
Modified: felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/ResolveContextImpl.java
URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/ResolveContextImpl.java?rev=1828212&r1=1828211&r2=1828212&view=diff
==============================================================================
--- felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/ResolveContextImpl.java (original)
+++ felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/ResolveContextImpl.java Tue Apr 3 09:40:24 2018
@@ -81,7 +81,22 @@ public class ResolveContextImpl extends
public Collection<Resource> getOndemandResources(Resource host)
{
- return new ArrayList<Resource>(m_ondemand);
+ List<Resource> result = new ArrayList<Resource>();
+ for (BundleRevision revision : m_ondemand)
+ {
+ for (BundleRequirement req : ((BundleRevisionImpl) revision).getDeclaredRequirements(BundleRevision.HOST_NAMESPACE))
+ {
+ for (Capability cap : host.getCapabilities(BundleRevision.HOST_NAMESPACE))
+ {
+ if (req.matches((BundleCapability) cap))
+ {
+ result.add(revision);
+ break;
+ }
+ }
+ }
+ }
+ return result;
}
@Override
Modified: felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java
URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java?rev=1828212&r1=1828211&r2=1828212&view=diff
==============================================================================
--- felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java (original)
+++ felix/trunk/osgi-r7/framework/src/main/java/org/apache/felix/framework/StatefulResolver.java Tue Apr 3 09:40:24 2018
@@ -581,23 +581,7 @@ class StatefulResolver
final List<BundleCapability> candidates = findProvidersInternal(record, req, false, true);
// Try to find a dynamic requirement that matches the capabilities.
- BundleRequirementImpl dynReq = null;
- for (int dynIdx = 0;
- (candidates.size() > 0) && (dynReq == null) && (dynIdx < dynamics.size());
- dynIdx++)
- {
- for (Iterator<BundleCapability> itCand = candidates.iterator();
- (dynReq == null) && itCand.hasNext(); )
- {
- Capability cap = itCand.next();
- if (CapabilitySet.matches(
- cap,
- ((BundleRequirementImpl) dynamics.get(dynIdx)).getFilter()))
- {
- dynReq = (BundleRequirementImpl) dynamics.get(dynIdx);
- }
- }
- }
+ final BundleRequirementImpl dynReq = findDynamicRequirement(dynamics, candidates);
// If we found a matching dynamic requirement, then filter out
// any candidates that do not match it.
@@ -608,7 +592,7 @@ class StatefulResolver
{
Capability cap = itCand.next();
if (!CapabilitySet.matches(
- cap, dynReq.getFilter()))
+ cap, dynReq.getFilter()))
{
itCand.remove();
}
@@ -621,17 +605,19 @@ class StatefulResolver
Map<Resource, Wiring> wirings = getWirings();
- wireMap = wirings.containsKey(revision) ? m_resolver.resolveDynamic(
+ wireMap = dynReq != null && wirings.containsKey(revision) ? m_resolver.resolveDynamic(
new ResolveContextImpl(
this,
- wirings,
+ wirings,
record,
Collections.<BundleRevision>emptyList(),
Collections.<BundleRevision>emptyList(),
- getFragments()) {
+ getFragments())
+ {
@Override
- public List<Capability> findProviders(Requirement br) {
- return (List) candidates;
+ public List<Capability> findProviders(Requirement br)
+ {
+ return (List) (br == dynReq ? candidates : super.findProviders(br));
}
},
revision.getWiring(), dynReq) : Collections.<Resource, List<Wire>>emptyMap();
@@ -708,6 +694,24 @@ class StatefulResolver
return provider;
}
+ private BundleRequirementImpl findDynamicRequirement(List<BundleRequirement> dynamics, List<BundleCapability> candidates)
+ {
+ for (int dynIdx = 0; (candidates.size() > 0) && (dynIdx < dynamics.size()); dynIdx++)
+ {
+ for (Iterator<BundleCapability> itCand = candidates.iterator(); itCand.hasNext(); )
+ {
+ Capability cap = itCand.next();
+ if (CapabilitySet.matches(
+ cap,
+ ((BundleRequirementImpl) dynamics.get(dynIdx)).getFilter()))
+ {
+ return (BundleRequirementImpl) dynamics.get(dynIdx);
+ }
+ }
+ }
+ return null;
+ }
+
private ResolverHookRecord prepareResolverHooks(
Set<BundleRevision> mandatory, Set<BundleRevision> optional)
throws BundleException, ResolutionException
@@ -1642,41 +1646,6 @@ class StatefulResolver
return fragments;
}
- void checkNativeLibraries(BundleRevision revision) throws ResolveException
- {
- // Next, try to resolve any native code, since the revision is
- // not resolvable if its native code cannot be loaded.
- List<NativeLibrary> libs = ((BundleRevisionImpl) revision).getDeclaredNativeLibraries();
- if (libs != null)
- {
- String msg = null;
- // Verify that all native libraries exist in advance; this will
- // throw an exception if the native library does not exist.
- for (int libIdx = 0; (msg == null) && (libIdx < libs.size()); libIdx++)
- {
- String entryName = libs.get(libIdx).getEntryName();
- if (entryName != null)
- {
- if (!((BundleRevisionImpl) revision).getContent().hasEntry(entryName))
- {
- msg = "Native library does not exist: " + entryName;
- }
- }
- }
- // If we have a zero-length native library array, then
- // this means no native library class could be selected
- // so we should fail to resolve.
- if (libs.isEmpty())
- {
- msg = "No matching native libraries found.";
- }
- if (msg != null)
- {
- throw new ResolveException(msg, revision, null);
- }
- }
- }
-
private synchronized Set<BundleRevision> getUnresolvedRevisions()
{
Set<BundleRevision> unresolved = new HashSet<BundleRevision>();
Added: felix/trunk/osgi-r7/framework/src/test/java/org/apache/felix/framework/ResolveTest.java
URL: http://svn.apache.org/viewvc/felix/trunk/osgi-r7/framework/src/test/java/org/apache/felix/framework/ResolveTest.java?rev=1828212&view=auto
==============================================================================
--- felix/trunk/osgi-r7/framework/src/test/java/org/apache/felix/framework/ResolveTest.java (added)
+++ felix/trunk/osgi-r7/framework/src/test/java/org/apache/felix/framework/ResolveTest.java Tue Apr 3 09:40:24 2018
@@ -0,0 +1,300 @@
+/*
+ * 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 junit.framework.TestCase;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.Constants;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.wiring.BundleRevision;
+import org.osgi.framework.wiring.BundleWire;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.framework.wiring.FrameworkWiring;
+
+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;
+
+public class ResolveTest 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 testResolveFragmentWithHost() 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 h = felix.getBundleContext().installBundle(bundleFile.toURI().toASCIIString());
+ Bundle f = felix.getBundleContext().installBundle(fragFile.toURI().toASCIIString());
+
+ assertEquals(Bundle.INSTALLED, h.getState());
+ assertEquals(Bundle.INSTALLED, f.getState());
+
+ felix.adapt(FrameworkWiring.class).resolveBundles(Collections.singletonList(h));
+
+ assertEquals(Bundle.RESOLVED, h.getState());
+ assertEquals(Bundle.RESOLVED, f.getState());
+ }
+
+ public void testResolveOnlyMatchingFragmentWithHost() 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 bmfo = "Bundle-SymbolicName: cap.bundleo\n"
+ + "Bundle-Version: 1.2.3.Blah\n"
+ + "Bundle-ManifestVersion: 2\n"
+ + "Import-Package: org.osgi.framework\n";
+ File bundleFileO = createBundle(bmfo);
+
+ 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);
+
+ String fmfo = "Bundle-SymbolicName: cap.frago\n"
+ + "Bundle-Version: 1.0.0\n"
+ + "Fragment-Host: cap.bundleo\n"
+ + "Bundle-ManifestVersion: 2\n"
+ + "Export-Package: org.foo.bar;version=\"2.0.0\"\n"
+ + "Import-Package: org.osgi.util.tracker\n";
+ File fragFileO = createBundle(fmfo);
+
+ Bundle h = felix.getBundleContext().installBundle(bundleFile.toURI().toASCIIString());
+ Bundle f = felix.getBundleContext().installBundle(fragFile.toURI().toASCIIString());
+
+ Bundle ho = felix.getBundleContext().installBundle(bundleFileO.toURI().toASCIIString());
+ Bundle fo = felix.getBundleContext().installBundle(fragFileO.toURI().toASCIIString());
+
+ assertEquals(Bundle.INSTALLED, h.getState());
+ assertEquals(Bundle.INSTALLED, f.getState());
+ assertEquals(Bundle.INSTALLED, ho.getState());
+ assertEquals(Bundle.INSTALLED, fo.getState());
+
+ felix.adapt(FrameworkWiring.class).resolveBundles(Collections.singletonList(h));
+
+ assertEquals(Bundle.RESOLVED, h.getState());
+ assertEquals(Bundle.RESOLVED, f.getState());
+ assertEquals(Bundle.INSTALLED, ho.getState());
+ assertEquals(Bundle.INSTALLED, fo.getState());
+ }
+
+ public void testResolveDynamicWithOnlyMatchingFragmentWithHost() 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 bmfo = "Bundle-SymbolicName: cap.bundleo\n"
+ + "Bundle-Version: 1.2.3.Blah\n"
+ + "Bundle-ManifestVersion: 2\n"
+ + "Import-Package: org.osgi.framework\n";
+ File bundleFileO = createBundle(bmfo);
+
+ 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,test.baz\n";
+ File fragFile = createBundle(fmf);
+
+ String fmfo = "Bundle-SymbolicName: cap.frago\n"
+ + "Bundle-Version: 1.0.0\n"
+ + "Fragment-Host: cap.bundleo\n"
+ + "Bundle-ManifestVersion: 2\n"
+ + "Export-Package: org.foo.baz;version=\"2.0.0\"\n"
+ + "Import-Package: org.osgi.util.tracker\n";
+ File fragFileO = createBundle(fmfo);
+
+ String dynm = "Bundle-SymbolicName: cap.dyn\n"
+ + "Bundle-Version: 1.0.0\n"
+ + "Bundle-ManifestVersion: 2\n"
+ + "DynamicImport-Package: org.foo.*,org.osgi.*\n";
+ File dynFile = createBundle(dynm);
+
+ String reqm = "Bundle-SymbolicName: cap.req\n"
+ + "Bundle-Version: 1.0.0\n"
+ + "Bundle-ManifestVersion: 2\n"
+ + "Export-Package: test.baz\n";
+ File reqFile = createBundle(reqm);
+
+ String reqnm = "Bundle-SymbolicName: cap.reqn\n"
+ + "Bundle-Version: 1.0.0\n"
+ + "Bundle-ManifestVersion: 2\n"
+ + "Export-Package: test.bazz,org.foo.bar.blub\n";
+ File reqnFile = createBundle(reqnm);
+
+ Bundle h = felix.getBundleContext().installBundle(bundleFile.toURI().toASCIIString());
+ Bundle f = felix.getBundleContext().installBundle(fragFile.toURI().toASCIIString());
+
+ Bundle ho = felix.getBundleContext().installBundle(bundleFileO.toURI().toASCIIString());
+ Bundle fo = felix.getBundleContext().installBundle(fragFileO.toURI().toASCIIString());
+
+ Bundle dyn = felix.getBundleContext().installBundle(dynFile.toURI().toASCIIString());
+ Bundle req = felix.getBundleContext().installBundle(reqFile.toURI().toASCIIString());
+ Bundle reqn = felix.getBundleContext().installBundle(reqnFile.toURI().toASCIIString());
+
+ assertEquals(Bundle.INSTALLED, h.getState());
+ assertEquals(Bundle.INSTALLED, f.getState());
+ assertEquals(Bundle.INSTALLED, ho.getState());
+ assertEquals(Bundle.INSTALLED, fo.getState());
+ assertEquals(Bundle.INSTALLED, dyn.getState());
+ assertEquals(Bundle.INSTALLED, req.getState());
+ assertEquals(Bundle.INSTALLED, reqn.getState());
+
+ felix.adapt(FrameworkWiring.class).resolveBundles(Collections.singletonList(dyn));
+
+ assertEquals(Bundle.INSTALLED, h.getState());
+ assertEquals(Bundle.INSTALLED, f.getState());
+ assertEquals(Bundle.INSTALLED, ho.getState());
+ assertEquals(Bundle.INSTALLED, fo.getState());
+ assertEquals(Bundle.RESOLVED, dyn.getState());
+ assertEquals(Bundle.INSTALLED, req.getState());
+ assertEquals(Bundle.INSTALLED, reqn.getState());
+
+ try
+ {
+ dyn.loadClass("org.foo.bar.Bar");
+ fail();
+ }
+ catch (Exception ex)
+ {
+ // Expected
+ }
+ assertEquals(Bundle.RESOLVED, h.getState());
+ assertEquals(Bundle.RESOLVED, f.getState());
+ assertEquals(Bundle.INSTALLED, ho.getState());
+ assertEquals(Bundle.INSTALLED, fo.getState());
+ assertEquals(Bundle.RESOLVED, dyn.getState());
+ assertEquals(Bundle.RESOLVED, req.getState());
+ assertEquals(Bundle.INSTALLED, reqn.getState());
+ List<BundleWire> requiredWires = dyn.adapt(BundleWiring.class).getRequiredWires(BundleRevision.PACKAGE_NAMESPACE);
+ assertEquals(1, requiredWires.size());
+ assertEquals(requiredWires.get(0).getProvider().getBundle(), h);
+
+ try
+ {
+ dyn.loadClass("org.foo.baz.Bar");
+ fail();
+ }
+ catch (Exception ex)
+ {
+ // Expected
+ }
+ assertEquals(Bundle.RESOLVED, h.getState());
+ assertEquals(Bundle.RESOLVED, f.getState());
+ assertEquals(Bundle.RESOLVED, ho.getState());
+ assertEquals(Bundle.RESOLVED, fo.getState());
+ assertEquals(Bundle.RESOLVED, dyn.getState());
+ assertEquals(Bundle.RESOLVED, req.getState());
+ assertEquals(Bundle.INSTALLED, reqn.getState());
+ requiredWires = dyn.adapt(BundleWiring.class).getRequiredWires(BundleRevision.PACKAGE_NAMESPACE);
+ assertEquals(2, requiredWires.size());
+ assertEquals(requiredWires.get(0).getProvider().getBundle(), h);
+ assertEquals(requiredWires.get(1).getProvider().getBundle(), ho);
+ }
+
+ 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);
+ }
+ }
+ assertTrue(root.delete());
+ }
+}