You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2015/02/13 18:23:03 UTC

karaf git commit: [KARAF-3529] Make sure the framework resolver uses the already computed resolution Improve the computation of bundles in need of a refresh

Repository: karaf
Updated Branches:
  refs/heads/master 6d2989580 -> 7ab5cda1c


[KARAF-3529] Make sure the framework resolver uses the already computed resolution
Improve the computation of bundles in need of a refresh

Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/7ab5cda1
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/7ab5cda1
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/7ab5cda1

Branch: refs/heads/master
Commit: 7ab5cda1c5a1e721aaa4383eab3b549c566ee87e
Parents: 6d29895
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Fri Feb 13 18:08:38 2015 +0100
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Fri Feb 13 18:08:38 2015 +0100

----------------------------------------------------------------------
 .../features/internal/service/Deployer.java     | 101 ++++++++++++++-----
 .../internal/service/FeaturesServiceImpl.java   |  78 +++++++++++++-
 .../features/internal/service/DeployerTest.java |  23 ++++-
 .../features/VerifyFeatureResolutionMojo.java   |   4 +-
 4 files changed, 172 insertions(+), 34 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/7ab5cda1/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
index 61dd5c7..3b7d18d 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
@@ -56,10 +56,15 @@ import org.osgi.framework.Constants;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.Version;
+import org.osgi.framework.namespace.BundleNamespace;
+import org.osgi.framework.namespace.HostNamespace;
+import org.osgi.framework.namespace.PackageNamespace;
 import org.osgi.framework.startlevel.BundleStartLevel;
 import org.osgi.framework.wiring.BundleRevision;
 import org.osgi.framework.wiring.BundleWire;
 import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.resource.Namespace;
+import org.osgi.resource.Requirement;
 import org.osgi.resource.Resource;
 import org.osgi.resource.Wire;
 import org.osgi.service.repository.Repository;
@@ -115,7 +120,7 @@ public class Deployer {
         void setBundleStartLevel(Bundle bundle, int startLevel);
 
         void refreshPackages(Collection<Bundle> bundles) throws InterruptedException;
-        void resolveBundles(Set<Bundle> bundles);
+        void resolveBundles(Set<Bundle> bundles, Map<Resource, List<Wire>> wiring, Map<Resource, Bundle> resToBnd);
 
         void replaceDigraph(Map<String, Map<String,Map<String,Set<String>>>> policies, Map<String, Set<Long>> bundles) throws BundleException, InvalidSyntaxException;
     }
@@ -757,7 +762,7 @@ public class Deployer {
         toResolve.addAll(toStart);
         toResolve.addAll(toRefresh.keySet());
         removeFragmentsAndBundlesInState(toResolve, UNINSTALLED);
-        callback.resolveBundles(toResolve);
+        callback.resolveBundles(toResolve, resolver.getWiring(), deployment.resToBnd);
 
         // Compute bundles to start
         removeFragmentsAndBundlesInState(toStart, UNINSTALLED | ACTIVE | STARTING);
@@ -870,9 +875,18 @@ public class Deployer {
         }
         // Main loop
         int size;
+        Map<Bundle, Resource> bndToRes = new HashMap<>();
+        for (Map.Entry<Resource, Bundle> entry : resources.entrySet()) {
+            bndToRes.put(entry.getValue(), entry.getKey());
+        }
         do {
             size = toRefresh.size();
-            for (Bundle bundle : bundles) {
+            main: for (Bundle bundle : bundles) {
+                Resource resource = bndToRes.get(bundle);
+                // This bundle is not managed
+                if (resource == null) {
+                    continue;
+                }
                 // Continue if we already know about this bundle
                 if (toRefresh.containsKey(bundle)) {
                     continue;
@@ -882,6 +896,11 @@ public class Deployer {
                 if (wiring == null) {
                     continue;
                 }
+                // Ignore bundles that won't be wired
+                List<Wire> newWires = resolution.get(resource);
+                if (newWires == null) {
+                    continue;
+                }
                 // Check if this bundle is a host and its fragments changed
                 Set<Resource> oldFragments = new HashSet<>();
                 for (BundleWire wire : wiring.getProvidedWires(null)) {
@@ -893,37 +912,67 @@ public class Deployer {
                     toRefresh.put(bundle, "Attached fragments changed: " + new ArrayList<>(newFragments.get(bundle)));
                     break;
                 }
-                // Get through the old resolution and flag this bundle
-                // if it was wired to a bundle to be refreshed
+                // Compare the old and new resolutions
+                Set<Resource> wiredBundles = new HashSet<>();
                 for (BundleWire wire : wiring.getRequiredWires(null)) {
-                    Bundle provider = wire.getProvider().getBundle();
+                    BundleRevision rev = wire.getProvider();
+                    Bundle provider = rev.getBundle();
                     if (toRefresh.containsKey(provider)) {
+                        // The bundle is wired to a bundle being refreshed,
+                        // so we need to refresh it too
                         toRefresh.put(bundle, "Wired to " + provider.getSymbolicName() + "/" + provider.getVersion() + " which is being refreshed");
-                        break;
+                        continue main;
                     }
+                    Resource res = bndToRes.get(provider);
+                    wiredBundles.add(res != null ? res : rev);
                 }
-                // Get through the new resolution and flag this bundle
-                // if it's wired to any new bundle
-                List<Wire> newWires = resolution.get(wiring.getRevision());
-                if (newWires != null) {
-                    for (Wire wire : newWires) {
-                        if (!isBundle(wire.getProvider())) {
-                            continue;
-                        }
-                        Bundle b;
-                        if (wire.getProvider() instanceof BundleRevision) {
-                            b = ((BundleRevision) wire.getProvider()).getBundle();
+                Map<Resource, Requirement> wiredResources = new HashMap<>();
+                for (Wire wire : newWires) {
+                    // Handle only packages, hosts, and required bundles
+                    String namespace = wire.getRequirement().getNamespace();
+                    if (!namespace.equals(BundleNamespace.BUNDLE_NAMESPACE)
+                            && !namespace.equals(PackageNamespace.PACKAGE_NAMESPACE)
+                            && !namespace.equals(HostNamespace.HOST_NAMESPACE)) {
+                        continue;
+                    }
+                    // Ignore non-resolution time requirements
+                    String effective = wire.getRequirement().getDirectives().get(Namespace.CAPABILITY_EFFECTIVE_DIRECTIVE);
+                    if (effective != null && !Namespace.EFFECTIVE_RESOLVE.equals(effective)) {
+                        continue;
+                    }
+                    // Ignore non bundle resources
+                    if (!isBundle(wire.getProvider())) {
+                        continue;
+                    }
+                    // Ignore bundles that won't get deployed for some reason
+                    if (!resources.containsKey(wire.getProvider())
+                            && !(wire.getProvider() instanceof BundleRevision)) {
+                        continue;
+                    }
+                    if (!wiredResources.containsKey(wire.getProvider())) {
+                        wiredResources.put(wire.getProvider(), wire.getRequirement());
+                    }
+                }
+                if (!wiredBundles.containsAll(wiredResources.keySet())) {
+                    Map<Resource, Requirement> newResources = new HashMap<>(wiredResources);
+                    newResources.keySet().removeAll(wiredBundles);
+                    StringBuilder sb = new StringBuilder();
+                    sb.append("Should be wired to: ");
+                    boolean first = true;
+                    for (Map.Entry<Resource, Requirement> entry : newResources.entrySet()) {
+                        if (!first) {
+                            sb.append(", ");
                         } else {
-                            b = resources.get(wire.getProvider());
-                        }
-                        if (b == null) {
-                            toRefresh.put(bundle, "Wired to a new bundle " + wire.getProvider());
-                            break;
-                        } else if (toRefresh.containsKey(b)) {
-                            toRefresh.put(bundle, "Wired to " + b.getSymbolicName() + "/" + b.getVersion() + " which is being refreshed");
-                            break;
+                            first = false;
                         }
+                        Resource res = entry.getKey();
+                        Requirement req = entry.getValue();
+                        sb.append(getSymbolicName(res)).append("/").append(getVersion(res));
+                        sb.append(" (through ");
+                        sb.append(req);
+                        sb.append(")");
                     }
+                    toRefresh.put(bundle, sb.toString());
                 }
             }
         } while (toRefresh.size() > size);

http://git-wip-us.apache.org/repos/asf/karaf/blob/7ab5cda1/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
----------------------------------------------------------------------
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
index 608bf78..42776a1 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeaturesServiceImpl.java
@@ -31,6 +31,7 @@ import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Hashtable;
+import java.util.Iterator;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
@@ -72,10 +73,19 @@ import org.osgi.framework.BundleException;
 import org.osgi.framework.FrameworkEvent;
 import org.osgi.framework.FrameworkListener;
 import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceRegistration;
 import org.osgi.framework.Version;
+import org.osgi.framework.hooks.resolver.ResolverHook;
+import org.osgi.framework.hooks.resolver.ResolverHookFactory;
 import org.osgi.framework.startlevel.BundleStartLevel;
 import org.osgi.framework.startlevel.FrameworkStartLevel;
+import org.osgi.framework.wiring.BundleCapability;
+import org.osgi.framework.wiring.BundleRequirement;
+import org.osgi.framework.wiring.BundleRevision;
 import org.osgi.framework.wiring.FrameworkWiring;
+import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wire;
 import org.osgi.service.cm.Configuration;
 import org.osgi.service.cm.ConfigurationAdmin;
 import org.slf4j.Logger;
@@ -1150,8 +1160,72 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
     }
 
     @Override
-    public void resolveBundles(Set<Bundle> bundles) {
-        systemBundleContext.getBundle().adapt(FrameworkWiring.class).resolveBundles(bundles);
+    public void resolveBundles(Set<Bundle> bundles, final Map<Resource, List<Wire>> wiring, Map<Resource, Bundle> resToBnd) {
+        // Make sure it's only used for us
+        final Thread thread = Thread.currentThread();
+        // Translate wiring
+        final Map<Bundle, Resource> bndToRes = new HashMap<>();
+        for (Resource res : resToBnd.keySet()) {
+            bndToRes.put(resToBnd.get(res), res);
+        }
+        // Hook
+        final ResolverHook hook = new ResolverHook() {
+            @Override
+            public void filterResolvable(Collection<BundleRevision> candidates) {
+            }
+            @Override
+            public void filterSingletonCollisions(BundleCapability singleton, Collection<BundleCapability> collisionCandidates) {
+            }
+            @Override
+            public void filterMatches(BundleRequirement requirement, Collection<BundleCapability> candidates) {
+                if (Thread.currentThread() == thread) {
+                    Bundle sourceBundle = requirement.getRevision().getBundle();
+                    Resource sourceResource = bndToRes.get(sourceBundle);
+                    for (Wire wire : wiring.get(sourceResource)) {
+                        Requirement req = wire.getRequirement();
+                        if (req.getNamespace().equals(requirement.getNamespace())
+                                && req.getAttributes().equals(requirement.getAttributes())
+                                && req.getDirectives().equals(requirement.getDirectives())) {
+                            for (Iterator<BundleCapability> capIter = candidates.iterator(); capIter.hasNext(); ) {
+                                BundleCapability cap = capIter.next();
+                                BundleRevision br = cap.getRevision();
+                                if (br == wire.getCapability().getResource()) {
+                                    continue;
+                                }
+                                Resource res = bndToRes.get(br.getBundle());
+                                if (res != wire.getCapability().getResource()) {
+                                    capIter.remove();
+                                }
+                            }
+                            break;
+                        }
+                    }
+                }
+            }
+            @Override
+            public void end() {
+            }
+        };
+        ResolverHookFactory factory = new ResolverHookFactory() {
+            @Override
+            public ResolverHook begin(Collection<BundleRevision> triggers) {
+                return hook;
+            }
+        };
+        ServiceRegistration<ResolverHookFactory> registration = systemBundleContext.registerService(ResolverHookFactory.class, factory, null);
+        try {
+            // Given we already have computed the wiring,
+            // there's no need to resolve the bundles all at the same time.
+            // It's much more efficient to resolve them by small chunks.
+            // We could be even smarter and order the bundles according to the
+            // order given by RequirementSort to minimize the size of needed chunks.
+            FrameworkWiring frameworkWiring = systemBundleContext.getBundle().adapt(FrameworkWiring.class);
+            for (Bundle bundle : bundles) {
+                frameworkWiring.resolveBundles(Collections.singleton(bundle));
+            }
+        } finally {
+            registration.unregister();
+        }
     }
 
     @Override

http://git-wip-us.apache.org/repos/asf/karaf/blob/7ab5cda1/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java
----------------------------------------------------------------------
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java
index 3a43bda..067f84f 100644
--- a/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java
+++ b/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java
@@ -23,6 +23,7 @@ import java.util.Collections;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.Hashtable;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 import java.util.jar.Manifest;
@@ -39,6 +40,8 @@ import org.easymock.IArgumentMatcher;
 import org.junit.Test;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wire;
 
 import static org.apache.karaf.features.FeaturesService.*;
 import static org.apache.karaf.features.internal.util.MapUtils.addToMapSet;
@@ -72,7 +75,9 @@ public class DeployerTest {
         EasyMock.expectLastCall();
         callback.installFeatureConfigs(f100);
         EasyMock.expectLastCall();
-        callback.resolveBundles(EasyMock.<Set<Bundle>>anyObject());
+        callback.resolveBundles(EasyMock.<Set<Bundle>>anyObject(),
+                                EasyMock.<Map<Resource, List<Wire>>>anyObject(),
+                                EasyMock.<Map<Resource, Bundle>>anyObject());
         EasyMock.expectLastCall();
         callback.callListeners(EasyMock.<FeatureEvent>anyObject());
         EasyMock.expectLastCall();
@@ -161,7 +166,9 @@ public class DeployerTest {
         EasyMock.expectLastCall();
         callback.installFeatureConfigs(f101);
         EasyMock.expectLastCall();
-        callback.resolveBundles(EasyMock.eq(Collections.<Bundle>singleton(bundleA)));
+        callback.resolveBundles(EasyMock.eq(Collections.<Bundle>singleton(bundleA)),
+                                EasyMock.<Map<Resource, List<Wire>>>anyObject(),
+                                EasyMock.<Map<Resource, Bundle>>anyObject());
         EasyMock.expectLastCall();
         callback.refreshPackages(EasyMock.eq(Collections.<Bundle>singleton(bundleA)));
         EasyMock.expectLastCall();
@@ -227,7 +234,9 @@ public class DeployerTest {
         EasyMock.expectLastCall();
         callback.installFeatureConfigs(f1);
         EasyMock.expectLastCall();
-        callback.resolveBundles(EasyMock.<Set<Bundle>>anyObject());
+        callback.resolveBundles(EasyMock.<Set<Bundle>>anyObject(),
+                                EasyMock.<Map<Resource, List<Wire>>>anyObject(),
+                                EasyMock.<Map<Resource, Bundle>>anyObject());
         EasyMock.expectLastCall();
         callback.callListeners(EasyMock.<FeatureEvent>anyObject());
         EasyMock.expectLastCall();
@@ -289,7 +298,9 @@ public class DeployerTest {
         EasyMock.expectLastCall();
         callback.installFeatureConfigs(f1);
         EasyMock.expectLastCall();
-        callback.resolveBundles(EasyMock.<Set<Bundle>>anyObject());
+        callback.resolveBundles(EasyMock.<Set<Bundle>>anyObject(),
+                                EasyMock.<Map<Resource, List<Wire>>>anyObject(),
+                                EasyMock.<Map<Resource, Bundle>>anyObject());
         EasyMock.expectLastCall();
         callback.callListeners(EasyMock.<FeatureEvent>anyObject());
         EasyMock.expectLastCall();
@@ -339,7 +350,9 @@ public class DeployerTest {
         EasyMock.expectLastCall();
         callback.installFeatureConfigs(f2);
         EasyMock.expectLastCall();
-        callback.resolveBundles(EasyMock.<Set<Bundle>>anyObject());
+        callback.resolveBundles(EasyMock.<Set<Bundle>>anyObject(),
+                                EasyMock.<Map<Resource, List<Wire>>>anyObject(),
+                                EasyMock.<Map<Resource, Bundle>>anyObject());
         EasyMock.expectLastCall();
         callback.callListeners(EasyMock.<FeatureEvent>anyObject());
         EasyMock.expectLastCall();

http://git-wip-us.apache.org/repos/asf/karaf/blob/7ab5cda1/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/VerifyFeatureResolutionMojo.java
----------------------------------------------------------------------
diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/VerifyFeatureResolutionMojo.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/VerifyFeatureResolutionMojo.java
index a68c6b0..d196c44 100644
--- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/VerifyFeatureResolutionMojo.java
+++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/features/VerifyFeatureResolutionMojo.java
@@ -108,6 +108,8 @@ import org.osgi.framework.wiring.BundleRequirement;
 import org.osgi.framework.wiring.BundleRevision;
 import org.osgi.framework.wiring.BundleWiring;
 import org.osgi.resource.Requirement;
+import org.osgi.resource.Resource;
+import org.osgi.resource.Wire;
 import org.osgi.service.resolver.ResolutionException;
 import shaded.org.ops4j.util.property.PropertiesPropertyResolver;
 
@@ -742,7 +744,7 @@ public class VerifyFeatureResolutionMojo extends MojoSupport {
         }
 
         @Override
-        public void resolveBundles(Set<Bundle> bundles) {
+        public void resolveBundles(Set<Bundle> bundles, Map<Resource, List<Wire>> wiring, Map<Resource, Bundle> resToBnd) {
         }
 
         @Override