Repository: karaf Updated Branches: refs/heads/master 23bbe1882 -> d8e6ae44f
[KARAF-2923] Fix BundleInfo computation for regions Project: http://git-wip-us.apache.org/repos/asf/karaf/repo Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/d8e6ae44 Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/d8e6ae44 Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/d8e6ae44 Branch: refs/heads/master Commit: d8e6ae44fd8c9fa1678416dcd8b2ccaf156a9a44 Parents: 23bbe18 Author: Guillaume Nodet <gno...@gmail.com> Authored: Thu Apr 24 16:46:56 2014 +0200 Committer: Guillaume Nodet <gno...@gmail.com> Committed: Thu Apr 24 16:47:22 2014 +0200 ---------------------------------------------------------------------- .../features/internal/region/Subsystem.java | 51 ++- .../internal/region/SubsystemResolver.java | 27 +- .../internal/service/FeaturesServiceImpl.java | 389 +++++++++---------- .../karaf/features/internal/service/State.java | 22 +- .../features/internal/service/StateStorage.java | 6 +- .../karaf/features/internal/util/MapUtils.java | 139 ++++++- .../features/internal/region/SubsystemTest.java | 17 +- .../internal/service/StateStorageTest.java | 4 +- 8 files changed, 413 insertions(+), 242 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/karaf/blob/d8e6ae44/features/core/src/main/java/org/apache/karaf/features/internal/region/Subsystem.java ---------------------------------------------------------------------- diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/region/Subsystem.java b/features/core/src/main/java/org/apache/karaf/features/internal/region/Subsystem.java index c49d4af..651c388 100644 --- a/features/core/src/main/java/org/apache/karaf/features/internal/region/Subsystem.java +++ b/features/core/src/main/java/org/apache/karaf/features/internal/region/Subsystem.java @@ -52,6 +52,7 @@ import org.osgi.resource.Resource; import static org.apache.karaf.features.internal.resolver.ResourceUtils.TYPE_FEATURE; import static org.apache.karaf.features.internal.resolver.ResourceUtils.TYPE_SUBSYSTEM; import static org.apache.karaf.features.internal.resolver.ResourceUtils.addIdentityRequirement; +import static org.apache.karaf.features.internal.resolver.ResourceUtils.getUri; import static org.apache.karaf.features.internal.util.MapUtils.addToMapSet; import static org.eclipse.equinox.region.RegionFilter.VISIBLE_ALL_NAMESPACE; import static org.osgi.framework.namespace.IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE; @@ -203,6 +204,14 @@ public class Subsystem extends ResourceImpl { ResourceUtils.addIdentityRequirement(this, name, TYPE_FEATURE, range); } + public Map<String, BundleInfo> getBundleInfos() { + Map<String, BundleInfo> infos = new HashMap<String, BundleInfo>(); + for (DependencyInfo di : dependencies.values()) { + infos.put(di.getLocation(), di); + } + return infos; + } + @SuppressWarnings("InfiniteLoopStatement") public void preResolve(Collection<Feature> features, DownloadManager manager, @@ -283,9 +292,9 @@ public class Subsystem extends ResourceImpl { final boolean mandatory = entry.getValue(); ResourceImpl res = bundles.get(loc); if (bi.isDependency()) { - addDependency(res, false); + addDependency(res, false, bi.isStart(), bi.getStartLevel()); } else { - doAddDependency(res, mandatory); + doAddDependency(res, mandatory, bi.isStart(), bi.getStartLevel()); } } for (Dependency dep : feature.getDependencies()) { @@ -312,15 +321,15 @@ public class Subsystem extends ResourceImpl { } } - void addDependency(ResourceImpl resource, boolean mandatory) { + void addDependency(ResourceImpl resource, boolean mandatory, boolean start, int startLevel) { if (isAcceptDependencies()) { - doAddDependency(resource, mandatory); + doAddDependency(resource, mandatory, start, startLevel); } else { - parent.addDependency(resource, mandatory); + parent.addDependency(resource, mandatory, start, startLevel); } } - private void doAddDependency(ResourceImpl resource, boolean mandatory) { + private void doAddDependency(ResourceImpl resource, boolean mandatory, boolean start, int startLevel) { String id = Util.getSymbolicName(resource) + "|" + Util.getVersion(resource); DependencyInfo info = dependencies.get(id); if (info == null) { @@ -329,11 +338,39 @@ public class Subsystem extends ResourceImpl { } info.resource = resource; info.mandatory |= mandatory; + info.start |= start; + if (info.startLevel > 0 && startLevel > 0) { + info.startLevel = Math.min(info.startLevel, startLevel); + } else { + info.startLevel = Math.max(info.startLevel, startLevel); + } } - class DependencyInfo { + class DependencyInfo implements BundleInfo { ResourceImpl resource; boolean mandatory; + boolean start; + int startLevel; + + @Override + public boolean isStart() { + return start; + } + + @Override + public int getStartLevel() { + return startLevel; + } + + @Override + public String getLocation() { + return getUri(resource); + } + + @Override + public boolean isDependency() { + return !mandatory; + } } Map<String, Set<String>> createPolicy(List<? extends ScopeFilter> filters) { http://git-wip-us.apache.org/repos/asf/karaf/blob/d8e6ae44/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java ---------------------------------------------------------------------- diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java b/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java index 8f42dda..86d8e1b 100644 --- a/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java +++ b/features/core/src/main/java/org/apache/karaf/features/internal/region/SubsystemResolver.java @@ -27,6 +27,7 @@ import java.util.Set; import org.apache.felix.resolver.ResolverImpl; import org.apache.felix.resolver.Util; +import org.apache.karaf.features.BundleInfo; import org.apache.karaf.features.Feature; import org.apache.karaf.features.Repository; import org.apache.karaf.features.internal.download.DownloadManager; @@ -77,7 +78,7 @@ public class SubsystemResolver { } public Map<Resource, List<Wire>> resolve( - List<Repository> repositories, + Collection<Feature> allFeatures, Map<String, Set<String>> features, Map<String, Set<BundleRevision>> system, Set<String> overrides, @@ -113,10 +114,6 @@ public class SubsystemResolver { return Collections.emptyMap(); } // Pre-resolve - List<Feature> allFeatures = new ArrayList<Feature>(); - for (Repository repo : repositories) { - allFeatures.addAll(Arrays.asList(repo.getFeatures())); - } root.preResolve(allFeatures, manager, overrides, featureResolutionRange); // Add system resources @@ -154,6 +151,26 @@ public class SubsystemResolver { return wiring; } + public Map<String, Map<String, BundleInfo>> getBundleInfos() { + Map<String, Map<String, BundleInfo>> infos = new HashMap<String, Map<String, BundleInfo>>(); + Map<String, String> flats = getFlatSubsystemsMap(); + addBundleInfos(infos, root, flats); + return infos; + } + + private void addBundleInfos(Map<String, Map<String, BundleInfo>> infos, Subsystem subsystem, Map<String, String> flats) { + String region = flats.get(subsystem.getName()); + Map<String, BundleInfo> bis = infos.get(region); + if (bis == null) { + bis = new HashMap<String, BundleInfo>(); + infos.put(region, bis); + } + bis.putAll(subsystem.getBundleInfos()); + for (Subsystem child : subsystem.getChildren()) { + addBundleInfos(infos, child, flats); + } + } + public Map<String, StreamProvider> getProviders() { return manager.getProviders(); } http://git-wip-us.apache.org/repos/asf/karaf/blob/d8e6ae44/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 7e42df9..cbfb15a 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 @@ -20,13 +20,13 @@ import java.io.IOException; import java.io.InputStream; import java.net.URI; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; @@ -54,6 +54,7 @@ import org.apache.karaf.features.internal.region.ResourceComparator; import org.apache.karaf.features.internal.region.SubsystemResolver; import org.apache.karaf.features.internal.util.ChecksumUtils; import org.apache.karaf.features.internal.util.Macro; +import org.apache.karaf.features.internal.util.MapUtils; import org.apache.karaf.features.internal.util.MultiException; import org.apache.karaf.util.collections.CopyOnWriteArrayIdentityList; import org.eclipse.equinox.region.Region; @@ -82,7 +83,11 @@ import static org.apache.felix.resolver.Util.getVersion; import static org.apache.karaf.features.internal.resolver.ResourceUtils.getFeatureId; import static org.apache.karaf.features.internal.resolver.ResourceUtils.getUri; import static org.apache.karaf.features.internal.util.MapUtils.addToMapSet; -import static org.apache.karaf.features.internal.util.MapUtils.copyMapSet; +import static org.apache.karaf.features.internal.util.MapUtils.apply; +import static org.apache.karaf.features.internal.util.MapUtils.copy; +import static org.apache.karaf.features.internal.util.MapUtils.diff; +import static org.apache.karaf.features.internal.util.MapUtils.flatten; +import static org.apache.karaf.features.internal.util.MapUtils.map; import static org.apache.karaf.features.internal.util.MapUtils.removeFromMapSet; import static org.osgi.framework.Bundle.ACTIVE; import static org.osgi.framework.Bundle.RESOLVED; @@ -582,7 +587,7 @@ public class FeaturesServiceImpl implements FeaturesService { public boolean isRequired(Feature f) { String id = f.getName() + "/" + new VersionRange(f.getVersion(), true); synchronized (lock) { - Set<String> features = state.features.get(ROOT_REGION); + Set<String> features = state.requestedFeatures.get(ROOT_REGION); return features != null && features.contains(id); } } @@ -659,7 +664,7 @@ public class FeaturesServiceImpl implements FeaturesService { @Override public void installFeatures(Set<String> features, String region, EnumSet<Option> options) throws Exception { State state = copyState(); - Map<String, Set<String>> required = copyMapSet(state.features); + Map<String, Set<String>> required = copy(state.requestedFeatures); if (region == null || region.isEmpty()) { region = ROOT_REGION; } @@ -700,7 +705,7 @@ public class FeaturesServiceImpl implements FeaturesService { public void uninstallFeatures(Set<String> features, String region, EnumSet<Option> options) throws Exception { State state = copyState(); - Map<String, Set<String>> required = copyMapSet(state.features); + Map<String, Set<String>> required = copy(state.requestedFeatures); if (region == null || region.isEmpty()) { region = ROOT_REGION; } @@ -811,9 +816,57 @@ public class FeaturesServiceImpl implements FeaturesService { } } - public void doInstallFeatures(Map<String, Set<String>> features, // all request features - State state, // current state - EnumSet<Option> options // installation options + static class DeploymentState { + Map<Long, Bundle> bundles; + Map<String, Feature> features; + Map<String, Set<Long>> bundlesPerRegion; + Map<String, Map<String, Map<String, Set<String>>>> filtersPerRegion; + } + + protected DeploymentState getDeploymentState() throws Exception { + DeploymentState state = new DeploymentState(); + // Bundles + state.bundles = new HashMap<Long, Bundle>(); + for (Bundle bundle : systemBundleContext.getBundles()) { + state.bundles.put(bundle.getBundleId(), bundle); + } + // Features + state.features = new HashMap<String, Feature>(); + for (Map<String, Feature> m : getFeatures().values()) { + for (Feature feature : m.values()) { + String id = feature.getName() + "/" + VersionTable.getVersion(feature.getVersion()); + state.features.put(id, feature); + } + } + // Region -> bundles mapping + // Region -> policy mapping + state.bundlesPerRegion = new HashMap<String, Set<Long>>(); + state.filtersPerRegion = new HashMap<String, Map<String, Map<String, Set<String>>>>(); + RegionDigraph clone = digraph.copy(); + for (Region region : clone.getRegions()) { + // Get bundles + state.bundlesPerRegion.put(region.getName(), new HashSet<Long>(region.getBundleIds())); + // Get policies + Map<String, Map<String, Set<String>>> edges = new HashMap<String, Map<String, Set<String>>>(); + for (RegionDigraph.FilteredRegion fr : clone.getEdges(region)) { + Map<String, Set<String>> policy = new HashMap<String, Set<String>>(); + Map<String, Collection<String>> current = fr.getFilter().getSharingPolicy(); + for (String ns : current.keySet()) { + for (String f : current.get(ns)) { + addToMapSet(policy, ns, f); + } + } + edges.put(fr.getRegion().getName(), policy); + } + state.filtersPerRegion.put(region.getName(), edges); + } + // Return + return state; + } + + public void doInstallFeatures(Map<String, Set<String>> requestedFeatures, // all request features + State state, // current state + EnumSet<Option> options // installation options ) throws Exception { boolean noRefreshUnmanaged = options.contains(Option.NoAutoRefreshUnmanagedBundles); @@ -823,94 +876,47 @@ public class FeaturesServiceImpl implements FeaturesService { boolean verbose = options.contains(Option.Verbose); boolean simulate = options.contains(Option.Simulate); - Map<String, Set<String>> installed = state.installedFeatures; - Map<String, Set<Long>> managed = copyMapSet(state.managedBundles); + DeploymentState dstate = getDeploymentState(); + + Map<String, Set<Long>> managedBundles = copy(state.managedBundles); - Bundle[] bundles = systemBundleContext.getBundles(); - - // Get a map of unmanaged bundles to use as capabilities during resolution - Map<String, Set<BundleRevision>> unmanagedBundles = new HashMap<String, Set<BundleRevision>>(); - Map<String, Map<String, Map<String, Set<String>>>> policies = new HashMap<String, Map<String, Map<String, Set<String>>>>(); - { - RegionDigraph clone = digraph.copy(); - for (Region region : clone.getRegions()) { - // Get bundles - Set<Long> ids = new HashSet<Long>(region.getBundleIds()); - if (managed.containsKey(region.getName())) { - ids.removeAll(managed.get(region.getName())); - } - if (!ids.isEmpty()) { - Set<BundleRevision> revs = new HashSet<BundleRevision>(); - for (Bundle bundle : bundles) { - if (ids.contains(bundle.getBundleId())) { - revs.add(bundle.adapt(BundleRevision.class)); - } - } - unmanagedBundles.put(region.getName(), revs); - } - // Get policies - Map<String, Map<String, Set<String>>> edges = new HashMap<String, Map<String, Set<String>>>(); - for (RegionDigraph.FilteredRegion fr : clone.getEdges(region)) { - Map<String, Set<String>> policy = new HashMap<String, Set<String>>(); - Map<String, Collection<String>> current = fr.getFilter().getSharingPolicy(); - for (String ns : current.keySet()) { - for (String f : current.get(ns)) { - addToMapSet(policy, ns, f); - } - } - edges.put(fr.getRegion().getName(), policy); - } - policies.put(region.getName(), edges); - } - } + Map<String, Set<Bundle>> unmanagedBundles = apply(diff(dstate.bundlesPerRegion, state.managedBundles), + map(dstate.bundles)); // Resolve // TODO: requirements // TODO: bundles - Repository[] repositories = listRepositories(); - - if (!installed.containsKey(ROOT_REGION)) { - installed.put(ROOT_REGION, new HashSet<String>()); - } SubsystemResolver resolver = new SubsystemResolver(); - Map<Resource, List<Wire>> resolution = resolver.resolve( - Arrays.asList(repositories), - features, - unmanagedBundles, + resolver.resolve( + dstate.features.values(), + requestedFeatures, + apply(unmanagedBundles, adapt(BundleRevision.class)), Overrides.loadOverrides(this.overrides), featureResolutionRange, globalRepository); - Collection<Resource> allResources = resolution.keySet(); - Map<String, StreamProvider> providers = resolver.getProviders(); - - Map<String, Set<String>> installedFeatures = getInstalledFeatures(resolver); - List<String> installedFeatureIds = getFeatureIds(allResources); - List<String> newFeatures = new ArrayList<String>(installedFeatureIds); - newFeatures.removeAll(installed.get(ROOT_REGION)); - List<String> delFeatures = new ArrayList<String>(installed.get(ROOT_REGION)); - delFeatures.removeAll(installedFeatureIds); + Map<String, StreamProvider> providers = resolver.getProviders(); + Map<String, Set<Resource>> featuresPerRegion = resolver.getFeaturesPerRegions(); + Map<String, Set<String>> installedFeatures = apply(featuresPerRegion, featureId()); + Map<String, Set<String>> newFeatures = diff(installedFeatures, state.installedFeatures); + Map<String, Set<String>> delFeatures = diff(state.installedFeatures, installedFeatures); // Compute information for each bundle - Map<String, BundleInfo> bundleInfos = new HashMap<String, BundleInfo>(); - for (Feature feature : getFeatures(repositories, getFeatureIds(allResources))) { - for (BundleInfo bi : feature.getBundles()) { - BundleInfo oldBi = bundleInfos.get(bi.getLocation()); - if (oldBi != null) { - bi = mergeBundleInfo(bi, oldBi); - } - bundleInfos.put(bi.getLocation(), bi); - } - } + Map<String, Map<String, BundleInfo>> bundleInfos = resolver.getBundleInfos(); // Get all resources that will be used to satisfy the old features set + // If noStart is true, we don't want to start the newly installed features + // but we still want old features to be started. Set<Resource> resourceLinkedToOldFeatures = new HashSet<Resource>(); if (noStart) { - for (Resource resource : resolver.getFeatures().keySet()) { - String id = getFeatureId(resource); - if (installed.get(ROOT_REGION).contains(id)) { - addTransitive(resource, resourceLinkedToOldFeatures, resolution); + for (Map.Entry<String, Set<Resource>> entry : featuresPerRegion.entrySet()) { + for (Resource resource : entry.getValue()) { + String id = getFeatureId(resource); + if (state.installedFeatures.containsKey(entry.getKey()) + && state.installedFeatures.get(entry.getKey()).contains(id)) { + addTransitive(resource, resourceLinkedToOldFeatures, resolver.getWiring()); + } } } } @@ -918,7 +924,7 @@ public class FeaturesServiceImpl implements FeaturesService { // // Compute deployment // - Deployment deployment = computeDeployment(resolver, state); + Deployment deployment = computeDeployment(dstate, resolver, state); if (deployment.regions.isEmpty()) { print("No deployment change.", verbose); @@ -937,63 +943,13 @@ public class FeaturesServiceImpl implements FeaturesService { toRefresh.addAll(regionDeployment.toDelete); toRefresh.addAll(regionDeployment.toUpdate.keySet()); } - if (!noRefreshManaged) { - int size; - do { - size = toRefresh.size(); - for (Bundle bundle : bundles) { - // Continue if we already know about this bundle - if (toRefresh.contains(bundle)) { - continue; - } - // Ignore non resolved bundle - BundleWiring wiring = bundle.adapt(BundleWiring.class); - if (wiring == null) { - continue; - } - // Get through the old resolution and flag this bundle - // if it was wired to a bundle to be refreshed - for (BundleWire wire : wiring.getRequiredWires(null)) { - if (toRefresh.contains(wire.getProvider().getBundle())) { - toRefresh.add(bundle); - break; - } - } - // 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) { - Bundle b; - if (wire.getProvider() instanceof BundleRevision) { - b = ((BundleRevision) wire.getProvider()).getBundle(); - } else { - b = deployment.resToBnd.get(wire.getProvider()); - } - if (b == null || toRefresh.contains(b)) { - toRefresh.add(bundle); - break; - } - } - } - } - } while (toRefresh.size() > size); + computeBundlesToRefresh(toRefresh, dstate.bundles.values(), deployment.resToBnd, resolver.getWiring()); } if (noRefreshUnmanaged) { - Set<Bundle> newSet = new HashSet<Bundle>(); - for (Bundle bundle : toRefresh) { - for (Set<Long> m : managed.values()) { - if (m.contains(bundle.getBundleId())) { - newSet.add(bundle); - break; - } - } - } - toRefresh = newSet; + toRefresh.removeAll(flatten(unmanagedBundles)); } - if (simulate) { if (!toRefresh.isEmpty()) { print(" Bundles to refresh:", verbose); @@ -1009,6 +965,17 @@ public class FeaturesServiceImpl implements FeaturesService { // // Execute deployment // + // #1: stop bundles that needs to be updated or uninstalled in order + // #2: uninstall needed bundles + // #3: update regions + // #4: update bundles + // #5: install bundles + // #6: save state + // #7: install configuration + // #8: refresh bundles + // #9: start bundles in order + // #10: send events + // // TODO: handle update on the features service itself RegionDeployment rootRegionDeployment = deployment.regions.get(ROOT_REGION); @@ -1064,7 +1031,7 @@ public class FeaturesServiceImpl implements FeaturesService { for (Bundle bundle : regionDeployment.toDelete) { print(" " + bundle.getSymbolicName() + " / " + bundle.getVersion(), verbose); bundle.uninstall(); - removeFromMapSet(managed, name, bundle.getBundleId()); + removeFromMapSet(managedBundles, name, bundle.getBundleId()); } } } @@ -1075,11 +1042,12 @@ public class FeaturesServiceImpl implements FeaturesService { { RegionDigraph clone = digraph.copy(); RegionDigraph computedDigraph = resolver.getFlatDigraph(); + Map<String, Map<String, Map<String, Set<String>>>> policies = copy(dstate.filtersPerRegion); // Iterate through previously managed regions and // delete those that do not contain any bundles anymore for (String name : state.managedBundles.keySet()) { - if (!managed.containsKey(name) && !unmanagedBundles.containsKey(name)) { - policies.remove(name); + if (!managedBundles.containsKey(name) && !unmanagedBundles.containsKey(name)) { + dstate.filtersPerRegion.remove(name); } } // Fix broken filters @@ -1128,12 +1096,12 @@ public class FeaturesServiceImpl implements FeaturesService { } // Dispatch bundles if (unmanagedBundles.containsKey(r1Name)) { - for (BundleRevision rev : unmanagedBundles.get(r1Name)) { - r1.addBundle(rev.getBundle()); + for (Bundle bundle : unmanagedBundles.get(r1Name)) { + r1.addBundle(bundle); } } - if (managed.containsKey(r1Name)) { - for (long id : managed.get(r1Name)) { + if (managedBundles.containsKey(r1Name)) { + for (long id : managedBundles.get(r1Name)) { r1.addBundle(id); } } @@ -1153,8 +1121,8 @@ public class FeaturesServiceImpl implements FeaturesService { } if (hasToUpdate) { print("Updating bundles:", verbose); - for (RegionDeployment regionDeployment : deployment.regions.values()) { - for (Map.Entry<Bundle, Resource> entry : regionDeployment.toUpdate.entrySet()) { + for (Map.Entry<String, RegionDeployment> rde : deployment.regions.entrySet()) { + for (Map.Entry<Bundle, Resource> entry : rde.getValue().toUpdate.entrySet()) { Bundle bundle = entry.getKey(); Resource resource = entry.getValue(); String uri = getUri(resource); @@ -1162,7 +1130,7 @@ public class FeaturesServiceImpl implements FeaturesService { InputStream is = getBundleInputStream(resource, providers); bundle.update(is); toStart.add(bundle); - BundleInfo bi = bundleInfos.get(uri); + BundleInfo bi = bundleInfos.get(rde.getKey()).get(uri); if (bi != null && bi.getStartLevel() > 0) { bundle.adapt(BundleStartLevel.class).setStartLevel(bi.getStartLevel()); } @@ -1194,14 +1162,14 @@ public class FeaturesServiceImpl implements FeaturesService { } else { bundle = region.installBundle(uri, is); } - addToMapSet(managed, name, bundle.getBundleId()); + addToMapSet(managedBundles, name, bundle.getBundleId()); deployment.resToBnd.put(resource, bundle); // save a checksum of installed snapshot bundle if (UPDATE_SNAPSHOTS_CRC.equals(updateSnaphots) - && isUpdateable(resource) && !deployment.newCheckums.containsKey(bundle.getBundleId())) { - deployment.newCheckums.put(bundle.getBundleId(), ChecksumUtils.checksum(getBundleInputStream(resource, providers))); + && isUpdateable(resource) && !deployment.bundleChecksums.containsKey(bundle.getBundleId())) { + deployment.bundleChecksums.put(bundle.getBundleId(), ChecksumUtils.checksum(getBundleInputStream(resource, providers))); } - BundleInfo bi = bundleInfos.get(uri); + BundleInfo bi = bundleInfos.get(entry.getKey()).get(uri); if (bi != null && bi.getStartLevel() > 0) { bundle.adapt(BundleStartLevel.class).setStartLevel(bi.getStartLevel()); } @@ -1220,13 +1188,14 @@ public class FeaturesServiceImpl implements FeaturesService { // Update and save state // synchronized (lock) { - this.state.bundleChecksums.putAll(deployment.newCheckums); - this.state.features.clear(); - this.state.features.putAll(features); + this.state.bundleChecksums.clear(); + this.state.bundleChecksums.putAll(deployment.bundleChecksums); + this.state.requestedFeatures.clear(); + this.state.requestedFeatures.putAll(requestedFeatures); this.state.installedFeatures.clear(); this.state.installedFeatures.putAll(installedFeatures); this.state.managedBundles.clear(); - this.state.managedBundles.putAll(managed); + this.state.managedBundles.putAll(managedBundles); saveState(); } @@ -1234,12 +1203,9 @@ public class FeaturesServiceImpl implements FeaturesService { // Install configurations // if (configInstaller != null && !newFeatures.isEmpty()) { - for (Repository repository : repositories) { - for (Feature feature : repository.getFeatures()) { - if (newFeatures.contains(feature.getId())) { - configInstaller.installFeatureConfigs(feature); - } - } + Set<Feature> set = apply(flatten(newFeatures), map(dstate.features)); + for (Feature feature : set) { + configInstaller.installFeatureConfigs(feature); } } @@ -1299,16 +1265,60 @@ public class FeaturesServiceImpl implements FeaturesService { } // Call listeners - for (Feature feature : getFeatures(repositories, delFeatures)) { + // TODO: add region information and avoid flattening + for (Feature feature : apply(flatten(delFeatures), map(dstate.features))) { callListeners(new FeatureEvent(feature, FeatureEvent.EventType.FeatureUninstalled, false)); } - for (Feature feature : getFeatures(repositories, newFeatures)) { + for (Feature feature : apply(flatten(newFeatures), map(dstate.features))) { callListeners(new FeatureEvent(feature, FeatureEvent.EventType.FeatureInstalled, false)); } print("Done.", verbose); } + private void computeBundlesToRefresh(Set<Bundle> toRefresh, Collection<Bundle> bundles, Map<Resource, Bundle> resources, Map<Resource, List<Wire>> resolution) { + int size; + do { + size = toRefresh.size(); + for (Bundle bundle : bundles) { + // Continue if we already know about this bundle + if (toRefresh.contains(bundle)) { + continue; + } + // Ignore non resolved bundle + BundleWiring wiring = bundle.adapt(BundleWiring.class); + if (wiring == null) { + continue; + } + // Get through the old resolution and flag this bundle + // if it was wired to a bundle to be refreshed + for (BundleWire wire : wiring.getRequiredWires(null)) { + if (toRefresh.contains(wire.getProvider().getBundle())) { + toRefresh.add(bundle); + break; + } + } + // 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) { + Bundle b; + if (wire.getProvider() instanceof BundleRevision) { + b = ((BundleRevision) wire.getProvider()).getBundle(); + } else { + b = resources.get(wire.getProvider()); + } + if (b == null || toRefresh.contains(b)) { + toRefresh.add(bundle); + break; + } + } + } + } + } while (toRefresh.size() > size); + } + private void addTransitive(Resource resource, Set<Resource> resources, Map<Resource, List<Wire>> resolution) { if (resources.add(resource)) { for (Wire wire : resolution.get(resource)) { @@ -1317,15 +1327,6 @@ public class FeaturesServiceImpl implements FeaturesService { } } - protected BundleInfo mergeBundleInfo(BundleInfo bi1, BundleInfo bi2) { - org.apache.karaf.features.internal.model.Bundle bi = new org.apache.karaf.features.internal.model.Bundle(); - bi.setLocation(bi1.getLocation()); - bi.setDependency(bi1.isDependency() && bi2.isDependency()); - bi.setStart(bi1.isStart() || bi2.isStart()); - bi.setStartLevel(Math.min(bi1.getStartLevel(), bi2.getStartLevel())); - return bi; - } - private void print(String message, boolean verbose) { LOGGER.info(message); if (verbose) { @@ -1334,10 +1335,11 @@ public class FeaturesServiceImpl implements FeaturesService { } private void removeFragmentsAndBundlesInState(Collection<Bundle> bundles, int state) { - for (Bundle bundle : new ArrayList<Bundle>(bundles)) { + for (Iterator<Bundle> iterator = bundles.iterator(); iterator.hasNext();) { + Bundle bundle = iterator.next(); if ((bundle.getState() & state) != 0 - || bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null) { - bundles.remove(bundle); + || bundle.getHeaders().get(Constants.FRAGMENT_HOST) != null) { + iterator.remove(); } } } @@ -1369,6 +1371,7 @@ public class FeaturesServiceImpl implements FeaturesService { } protected Deployment computeDeployment( + DeploymentState dstate, SubsystemResolver resolver, State state) throws IOException { @@ -1400,7 +1403,7 @@ public class FeaturesServiceImpl implements FeaturesService { // as either to ignore or delete for (long bundleId : managed) { // Look for the installed bundle - Bundle bundle = systemBundleContext.getBundle(bundleId); + Bundle bundle = dstate.bundles.get(bundleId); // Bundle has been manually uninstalled ? if (bundle != null) { // Look for a matching resource @@ -1432,8 +1435,8 @@ public class FeaturesServiceImpl implements FeaturesService { if (newCrc != oldCrc) { LOGGER.debug("New snapshot available for " + bundle.getLocation()); deployment.toUpdate.put(bundle, resource); - result.newCheckums.put(bundle.getBundleId(), newCrc); } + result.bundleChecksums.put(bundle.getBundleId(), newCrc); } finally { if (is != null) { is.close(); @@ -1483,40 +1486,24 @@ public class FeaturesServiceImpl implements FeaturesService { return result; } - protected List<Feature> getFeatures(Repository[] repositories, List<String> featureIds) throws Exception { - List<Feature> installedFeatures = new ArrayList<Feature>(); - for (Repository repository : repositories) { - for (Feature feature : repository.getFeatures()) { - String id = feature.getName() + "/" + VersionTable.getVersion(feature.getVersion()); - if (featureIds.contains(id)) { - installedFeatures.add(feature); - } + protected <T> MapUtils.Function<Bundle, T> adapt(final Class<T> clazz) { + return new MapUtils.Function<Bundle, T>() { + @Override + public T apply(Bundle bundle) { + return bundle.adapt(clazz); } - } - return installedFeatures; + }; } - protected Map<String, Set<String>> getInstalledFeatures(SubsystemResolver resolver) { - Map<String, Set<String>> installed = new HashMap<String, Set<String>>(); - Map<String, Set<Resource>> mapping = resolver.getFeaturesPerRegions(); - for (Map.Entry<String, Set<Resource>> entry : mapping.entrySet()) { - for (Resource resource : entry.getValue()) { - addToMapSet(installed, entry.getKey(), getFeatureId(resource)); + protected MapUtils.Function<Resource, String> featureId() { + return new MapUtils.Function<Resource, String>() { + @Override + public String apply(Resource resource) { + return getFeatureId(resource); } - } - return installed; + }; } - protected List<String> getFeatureIds(Collection<Resource> allResources) { - List<String> installedFeatureIds = new ArrayList<String>(); - for (Resource resource : allResources) { - String id = getFeatureId(resource); - if (id != null) { - installedFeatureIds.add(id); - } - } - return installedFeatureIds; - } protected boolean isUpdateable(Resource resource) { String uri = getUri(resource); @@ -1649,7 +1636,7 @@ public class FeaturesServiceImpl implements FeaturesService { static class Deployment { - Map<Long, Long> newCheckums = new HashMap<Long, Long>(); + Map<Long, Long> bundleChecksums = new HashMap<Long, Long>(); Map<Resource, Bundle> resToBnd = new HashMap<Resource, Bundle>(); Map<String, RegionDeployment> regions = new HashMap<String, RegionDeployment>(); } http://git-wip-us.apache.org/repos/asf/karaf/blob/d8e6ae44/features/core/src/main/java/org/apache/karaf/features/internal/service/State.java ---------------------------------------------------------------------- diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/State.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/State.java index df4d901..387e040 100644 --- a/features/core/src/main/java/org/apache/karaf/features/internal/service/State.java +++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/State.java @@ -22,13 +22,13 @@ import java.util.Set; import java.util.TreeSet; import java.util.concurrent.atomic.AtomicBoolean; -import static org.apache.karaf.features.internal.util.MapUtils.copyMapSet; +import org.apache.karaf.features.internal.util.MapUtils; public class State { public final AtomicBoolean bootDone = new AtomicBoolean(); public final Set<String> repositories = new TreeSet<String>(); - public final Map<String, Set<String>> features = new HashMap<String, Set<String>>(); + public final Map<String, Set<String>> requestedFeatures = new HashMap<String, Set<String>>(); public final Map<String, Set<String>> installedFeatures = new HashMap<String, Set<String>>(); public final Map<String, Set<Long>> managedBundles = new HashMap<String, Set<Long>>(); public final Map<Long, Long> bundleChecksums = new HashMap<Long, Long>(); @@ -36,20 +36,12 @@ public class State { public State copy() { State state = new State(); state.bootDone.set(bootDone.get()); - copySet(repositories, state.repositories); - copyMapSet(features, state.features); - copyMapSet(installedFeatures, state.installedFeatures); - copyMapSet(managedBundles, state.managedBundles); - copyMap(bundleChecksums, state.bundleChecksums); + MapUtils.copy(repositories, state.repositories); + MapUtils.copy(requestedFeatures, state.requestedFeatures); + MapUtils.copy(installedFeatures, state.installedFeatures); + MapUtils.copy(managedBundles, state.managedBundles); + MapUtils.copy(bundleChecksums, state.bundleChecksums); return state; } - static <T> void copySet(Set<T> from, Set<T> to) { - to.addAll(from); - } - - static <S, T> void copyMap(Map<S, T> from, Map<S, T> to) { - to.putAll(from); - } - } http://git-wip-us.apache.org/repos/asf/karaf/blob/d8e6ae44/features/core/src/main/java/org/apache/karaf/features/internal/service/StateStorage.java ---------------------------------------------------------------------- diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/StateStorage.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/StateStorage.java index 3b3424f..1b0a728 100644 --- a/features/core/src/main/java/org/apache/karaf/features/internal/service/StateStorage.java +++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/StateStorage.java @@ -33,7 +33,7 @@ public abstract class StateStorage { public void load(State state) throws IOException { state.repositories.clear(); - state.features.clear(); + state.requestedFeatures.clear(); state.installedFeatures.clear(); state.managedBundles.clear(); InputStream is = getInputStream(); @@ -42,7 +42,7 @@ public abstract class StateStorage { Map json = (Map) JsonReader.read(is); state.bootDone.set((Boolean) json.get("bootDone")); state.repositories.addAll(toStringSet((Collection) json.get("repositories"))); - state.features.putAll(toStringStringSetMap((Map) json.get("features"))); + state.requestedFeatures.putAll(toStringStringSetMap((Map) json.get("features"))); state.installedFeatures.putAll(toStringStringSetMap((Map) json.get("installed"))); state.managedBundles.putAll(toStringLongSetMap((Map) json.get("managed"))); state.bundleChecksums.putAll(toLongLongMap((Map) json.get("checksums"))); @@ -59,7 +59,7 @@ public abstract class StateStorage { Map<String, Object> json = new HashMap<String, Object>(); json.put("bootDone", state.bootDone.get()); json.put("repositories", state.repositories); - json.put("features", state.features); + json.put("features", state.requestedFeatures); json.put("installed", state.installedFeatures); json.put("managed", state.managedBundles); json.put("checksums", toStringLongMap(state.bundleChecksums)); http://git-wip-us.apache.org/repos/asf/karaf/blob/d8e6ae44/features/core/src/main/java/org/apache/karaf/features/internal/util/MapUtils.java ---------------------------------------------------------------------- diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/util/MapUtils.java b/features/core/src/main/java/org/apache/karaf/features/internal/util/MapUtils.java index 455ceda..0a21e29 100644 --- a/features/core/src/main/java/org/apache/karaf/features/internal/util/MapUtils.java +++ b/features/core/src/main/java/org/apache/karaf/features/internal/util/MapUtils.java @@ -16,21 +16,158 @@ */ package org.apache.karaf.features.internal.util; +import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; public class MapUtils { + public interface Function<T, U> { + U apply(T t); + } + public static <S, T> Map<S, Set<T>> invert(Map<T, S> map) { - Map<S, Set<T>> inverted = new HashMap<S, Set<T>>(); + Map<S, Set<T>> inverted = new HashMap<S, Set<T>>(map.size()); for (Map.Entry<T, S> entry : map.entrySet()) { addToMapSet(inverted, entry.getValue(), entry.getKey()); } return inverted; } + public static <S, T, U> Map<S, Set<U>> apply(Map<S, Set<T>> mapset, Function<T, U> function) { + Map<S, Set<U>> result = new HashMap<S, Set<U>>(mapset.size()); + for (Map.Entry<S, Set<T>> entry : mapset.entrySet()) { + result.put(entry.getKey(), apply(entry.getValue(), function)); + } + return result; + } + + public static <U, T> Set<U> apply(Set<T> set, Function<T, U> function) { + Set<U> result = new HashSet<U>(set.size()); + for (T t : set) { + result.add(function.apply(t)); + } + return result; + } + + public static <S, T, U> Map<T, U> build(Collection<S> col, Function<S, T> key, Function<S, U> value) { + Map<T, U> result = new HashMap<T, U>(col.size()); + for (S s : col) { + result.put(key.apply(s), value.apply(s)); + } + return result; + } + + public static <S, T, U> Function<S, U> compose(final Function<S, T> f1, final Function<T, U> f2) { + return new Function<S, U>() { + @Override + public U apply(S s) { + return f2.apply(f1.apply(s)); + } + }; + } + + public static <T, U> MapUtils.Function<T, U> map(final Map<T, U> map) { + return new MapUtils.Function<T, U>() { + @Override + public U apply(T t) { + return map.get(t); + } + }; + } + + public static <S, T> Set<T> flatten(Map<S, Set<T>> mapset) { + Set<T> set = new HashSet<T>(); + for (Set<T> s : mapset.values()) { + set.addAll(s); + } + return set; + } + + public static <S, T> Map<S, Set<T>> diff(Map<S, Set<T>> from, Map<S, Set<T>> to) { + Map<S, Set<T>> diff = copyMapSet(from); + remove(diff, to); + return diff; + } + + public static <S, T> void add(Map<S, Set<T>> from, Map<S, Set<T>> toAdd) { + for (Map.Entry<S, Set<T>> entry : toAdd.entrySet()) { + Set<T> s = from.get(entry.getKey()); + if (s == null) { + s = new HashSet<T>(); + from.put(entry.getKey(), s); + } + s.addAll(entry.getValue()); + } + } + + public static <S, T> void retain(Map<S, Set<T>> from, Map<S, Set<T>> toRetain) { + for (Iterator<Map.Entry<S, Set<T>>> iterator = from.entrySet().iterator(); iterator.hasNext();) { + Map.Entry<S, Set<T>> entry = iterator.next(); + Set<T> s = toRetain.get(entry.getKey()); + if (s != null) { + entry.getValue().retainAll(s); + } else { + iterator.remove(); + } + } + } + + public static <S, T> void remove(Map<S, Set<T>> from, Map<S, Set<T>> toRemove) { + for (Map.Entry<S, Set<T>> entry : toRemove.entrySet()) { + Set<T> s = from.get(entry.getKey()); + if (s != null) { + s.removeAll(entry.getValue()); + if (s.isEmpty()) { + from.remove(entry.getKey()); + } + } + } + } + + @SuppressWarnings("unchecked") + public static <S> S copy(S obj) { + if (obj instanceof List) { + List r = new ArrayList(); + for (Object o : (List) obj) { + r.add(copy(o)); + } + return (S) r; + } else if (obj instanceof Set) { + Set r = new HashSet(); + for (Object o : (Set) obj) { + r.add(copy(o)); + } + return (S) r; + } else if (obj instanceof Map) { + Map r = new HashMap(); + for (Map.Entry<?,?> e : ((Map<?,?>) obj).entrySet()) { + r.put(copy(e.getKey()), copy(e.getValue())); + } + return (S) r; + } + return obj; + } + + public static <S> void copy(S s1, S s2) { + if (s1 instanceof Collection) { + for (Object o : (Collection) s1) { + ((Collection) s2).add(copy(o)); + } + } else if (s1 instanceof Map) { + for (Map.Entry<?,?> e : ((Map<?,?>) s1).entrySet()) { + ((Map) s2).put(copy(e.getKey()), copy(e.getValue())); + } + } else { + throw new IllegalArgumentException("Source is not a Collection or a Map"); + } + } + public static <S, T> Map<S, Set<T>> copyMapSet(Map<S, Set<T>> from) { Map<S, Set<T>> to = new HashMap<S, Set<T>>(); copyMapSet(from, to); http://git-wip-us.apache.org/repos/asf/karaf/blob/d8e6ae44/features/core/src/test/java/org/apache/karaf/features/internal/region/SubsystemTest.java ---------------------------------------------------------------------- diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/region/SubsystemTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/region/SubsystemTest.java index a027290..5e8edd5 100644 --- a/features/core/src/test/java/org/apache/karaf/features/internal/region/SubsystemTest.java +++ b/features/core/src/test/java/org/apache/karaf/features/internal/region/SubsystemTest.java @@ -22,6 +22,7 @@ import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; @@ -62,7 +63,7 @@ public class SubsystemTest { addToMapSet(expected, "root/apps1", "b/1.0.0"); SubsystemResolver resolver = new SubsystemResolver(new TestDownloadManager("data1")); - resolver.resolve(Collections.<Repository>singletonList(repo), + resolver.resolve(Arrays.asList(repo.getFeatures()), features, Collections.<String, Set<BundleRevision>>emptyMap(), Collections.<String>emptySet(), @@ -93,7 +94,7 @@ public class SubsystemTest { addToMapSet(expected, "root/apps2#f1", "a/1.0.0"); SubsystemResolver resolver = new SubsystemResolver(new TestDownloadManager("data2")); - resolver.resolve(Collections.<Repository>singletonList(repo), + resolver.resolve(Arrays.asList(repo.getFeatures()), features, Collections.<String, Set<BundleRevision>>emptyMap(), Collections.<String>emptySet(), @@ -114,12 +115,12 @@ public class SubsystemTest { addToMapSet(expected, "root/apps1", "a/1.0.1"); SubsystemResolver resolver = new SubsystemResolver(new TestDownloadManager("data3")); - resolver.resolve(Collections.<Repository>singletonList(repo), - features, - Collections.<String, Set<BundleRevision>>emptyMap(), - Collections.singleton("b"), - FeaturesServiceImpl.DEFAULT_FEATURE_RESOLUTION_RANGE, - null); + resolver.resolve(Arrays.asList(repo.getFeatures()), + features, + Collections.<String, Set<BundleRevision>>emptyMap(), + Collections.singleton("b"), + FeaturesServiceImpl.DEFAULT_FEATURE_RESOLUTION_RANGE, + null); verify(resolver, expected); } http://git-wip-us.apache.org/repos/asf/karaf/blob/d8e6ae44/features/core/src/test/java/org/apache/karaf/features/internal/service/StateStorageTest.java ---------------------------------------------------------------------- diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/service/StateStorageTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/service/StateStorageTest.java index 5bee904..857936c 100644 --- a/features/core/src/test/java/org/apache/karaf/features/internal/service/StateStorageTest.java +++ b/features/core/src/test/java/org/apache/karaf/features/internal/service/StateStorageTest.java @@ -37,7 +37,7 @@ public class StateStorageTest { State oldState = new State(); oldState.bootDone.set(true); oldState.bundleChecksums.put(4l, 32794l); - oldState.features.put("bar", Collections.singleton("f1")); + oldState.requestedFeatures.put("bar", Collections.singleton("f1")); oldState.managedBundles.put("reg", Collections.singleton(32l)); oldState.managedBundles.put("reg2", new HashSet<Long>(Arrays.asList(24l, 43l))); oldState.repositories.add("repo"); @@ -53,7 +53,7 @@ public class StateStorageTest { assertEquals(oldState.bootDone.get(), newState.bootDone.get()); assertEquals(oldState.bundleChecksums, newState.bundleChecksums); - assertEquals(oldState.features, newState.features); + assertEquals(oldState.requestedFeatures, newState.requestedFeatures); assertEquals(oldState.managedBundles, newState.managedBundles); assertEquals(oldState.repositories, newState.repositories); }