Repository: karaf Updated Branches: refs/heads/master ea95ef65d -> b982479dd
[KARAF-2952] Add a lifecycle for features The commands needs a bit of love... Project: http://git-wip-us.apache.org/repos/asf/karaf/repo Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/b982479d Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/b982479d Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/b982479d Branch: refs/heads/master Commit: b982479dd7b39e6e9800761dcf2bb6ea2b3a1444 Parents: ea95ef6 Author: Guillaume Nodet <gno...@gmail.com> Authored: Tue Apr 29 15:44:40 2014 +0200 Committer: Guillaume Nodet <gno...@gmail.com> Committed: Tue Apr 29 15:45:00 2014 +0200 ---------------------------------------------------------------------- .../features/command/StartFeaturesCommand.java | 69 +++++ .../features/command/StopFeaturesCommand.java | 68 +++++ .../apache/karaf/features/FeaturesService.java | 11 + .../features/internal/region/Subsystem.java | 1 + .../internal/resolver/ResourceUtils.java | 10 +- .../internal/service/FeaturesServiceImpl.java | 256 +++++++++++++------ .../karaf/features/internal/service/State.java | 12 +- .../features/internal/service/StateStorage.java | 18 ++ .../karaf/features/internal/util/MapUtils.java | 11 +- shell/commands/pom.xml | 3 + 10 files changed, 374 insertions(+), 85 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/karaf/blob/b982479d/features/command/src/main/java/org/apache/karaf/features/command/StartFeaturesCommand.java ---------------------------------------------------------------------- diff --git a/features/command/src/main/java/org/apache/karaf/features/command/StartFeaturesCommand.java b/features/command/src/main/java/org/apache/karaf/features/command/StartFeaturesCommand.java new file mode 100644 index 0000000..38b5a41 --- /dev/null +++ b/features/command/src/main/java/org/apache/karaf/features/command/StartFeaturesCommand.java @@ -0,0 +1,69 @@ +/* + * 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.karaf.features.command; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; + +import org.apache.karaf.features.FeaturesService; +import org.apache.karaf.features.command.completers.AvailableFeatureCompleter; +import org.apache.karaf.shell.api.action.Argument; +import org.apache.karaf.shell.api.action.Command; +import org.apache.karaf.shell.api.action.Completion; +import org.apache.karaf.shell.api.action.Option; +import org.apache.karaf.shell.api.action.lifecycle.Service; + +@Command(scope = "feature", name = "start", description = "Start features with the specified name and version.") +@Service +public class StartFeaturesCommand extends FeaturesCommandSupport { + + @Argument(index = 0, name = "feature", description = "The name and version of the features to install. A feature id looks like name/version. The version is optional.", required = true, multiValued = true) + @Completion(AvailableFeatureCompleter.class) + List<String> features; + + @Option(name = "-v", aliases = "--verbose", description = "Explain what is being done", required = false, multiValued = false) + boolean verbose; + + @Option(name = "-t", aliases = "--simulate", description = "Perform a simulation only", required = false, multiValued = false) + boolean simulate; + + @Option(name = "-g", aliases = "--region", description = "Region to install to") + String region; + + protected void doExecute(FeaturesService admin) throws Exception { + EnumSet<FeaturesService.Option> options = EnumSet.noneOf(FeaturesService.Option.class); + if (simulate) { + options.add(FeaturesService.Option.Simulate); + } + if (verbose) { + options.add(FeaturesService.Option.Verbose); + } + Map<String, Map<String, FeaturesService.RequestedState>> stateChanges = new HashMap<>(); + if (region == null) { + region = FeaturesService.ROOT_REGION; + } + Map<String, FeaturesService.RequestedState> regionChanges = new HashMap<>(); + for (String feature : features) { + regionChanges.put(feature, FeaturesService.RequestedState.Started); + } + stateChanges.put(region, regionChanges); + admin.updateFeaturesState(stateChanges, options); + } +} http://git-wip-us.apache.org/repos/asf/karaf/blob/b982479d/features/command/src/main/java/org/apache/karaf/features/command/StopFeaturesCommand.java ---------------------------------------------------------------------- diff --git a/features/command/src/main/java/org/apache/karaf/features/command/StopFeaturesCommand.java b/features/command/src/main/java/org/apache/karaf/features/command/StopFeaturesCommand.java new file mode 100644 index 0000000..0afdf07 --- /dev/null +++ b/features/command/src/main/java/org/apache/karaf/features/command/StopFeaturesCommand.java @@ -0,0 +1,68 @@ +/* + * 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.karaf.features.command; + +import java.util.EnumSet; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.karaf.features.FeaturesService; +import org.apache.karaf.features.command.completers.AvailableFeatureCompleter; +import org.apache.karaf.shell.api.action.Argument; +import org.apache.karaf.shell.api.action.Command; +import org.apache.karaf.shell.api.action.Completion; +import org.apache.karaf.shell.api.action.Option; +import org.apache.karaf.shell.api.action.lifecycle.Service; + +@Command(scope = "feature", name = "stop", description = "Start features with the specified name and version.") +@Service +public class StopFeaturesCommand extends FeaturesCommandSupport { + + @Argument(index = 0, name = "feature", description = "The name and version of the features to install. A feature id looks like name/version. The version is optional.", required = true, multiValued = true) + @Completion(AvailableFeatureCompleter.class) + List<String> features; + + @Option(name = "-v", aliases = "--verbose", description = "Explain what is being done", required = false, multiValued = false) + boolean verbose; + + @Option(name = "-t", aliases = "--simulate", description = "Perform a simulation only", required = false, multiValued = false) + boolean simulate; + + @Option(name = "-g", aliases = "--region", description = "Region to install to") + String region; + + protected void doExecute(FeaturesService admin) throws Exception { + EnumSet<FeaturesService.Option> options = EnumSet.noneOf(FeaturesService.Option.class); + if (simulate) { + options.add(FeaturesService.Option.Simulate); + } + if (verbose) { + options.add(FeaturesService.Option.Verbose); + } + Map<String, Map<String, FeaturesService.RequestedState>> stateChanges = new HashMap<>(); + if (region == null) { + region = FeaturesService.ROOT_REGION; + } + Map<String, FeaturesService.RequestedState> regionChanges = new HashMap<>(); + for (String feature : features) { + regionChanges.put(feature, FeaturesService.RequestedState.Resolved); + } + stateChanges.put(region, regionChanges); + admin.updateFeaturesState(stateChanges, options); + } +} http://git-wip-us.apache.org/repos/asf/karaf/blob/b982479d/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java ---------------------------------------------------------------------- diff --git a/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java b/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java index d8f3d50..4366036 100644 --- a/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java +++ b/features/core/src/main/java/org/apache/karaf/features/FeaturesService.java @@ -18,6 +18,7 @@ package org.apache.karaf.features; import java.net.URI; import java.util.EnumSet; +import java.util.Map; import java.util.Set; /** @@ -25,6 +26,8 @@ import java.util.Set; */ public interface FeaturesService { + public static final String ROOT_REGION = "root"; + enum Option { NoFailOnFeatureNotFound, NoAutoRefreshManagedBundles, @@ -36,6 +39,12 @@ public interface FeaturesService { Verbose } + enum RequestedState { + Installed, + Resolved, + Started + } + /** * Validate repository contents. * @@ -86,6 +95,8 @@ public interface FeaturesService { void uninstallFeatures(Set<String> features, String region, EnumSet<Option> options) throws Exception; + void updateFeaturesState(Map<String, Map<String, RequestedState>> stateChanges, EnumSet<Option> options) throws Exception; + Feature[] listFeatures() throws Exception; Feature[] listRequiredFeatures() throws Exception; http://git-wip-us.apache.org/repos/asf/karaf/blob/b982479d/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 d4e362f..bfe170e 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 @@ -298,6 +298,7 @@ public class Subsystem extends ResourceImpl { for (Conditional cond : feature.getConditional()) { FeatureResource resCond = FeatureResource.build(feature, cond, featureResolutionRange, bundles); addIdentityRequirement(this, resCond, false); + addIdentityRequirement(resCond, this, true); installable.add(resCond); resConds.put(cond, resCond); } http://git-wip-us.apache.org/repos/asf/karaf/blob/b982479d/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java ---------------------------------------------------------------------- diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java index b22f9b8..9657689 100644 --- a/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java +++ b/features/core/src/main/java/org/apache/karaf/features/internal/resolver/ResourceUtils.java @@ -21,7 +21,6 @@ import java.util.List; import java.util.Map; import org.apache.felix.utils.version.VersionRange; -import org.osgi.framework.Constants; import org.osgi.framework.Version; import org.osgi.resource.Capability; import org.osgi.resource.Resource; @@ -29,6 +28,9 @@ import org.osgi.resource.Resource; import static org.osgi.framework.namespace.IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE; import static org.osgi.framework.namespace.IdentityNamespace.CAPABILITY_VERSION_ATTRIBUTE; import static org.osgi.framework.namespace.IdentityNamespace.IDENTITY_NAMESPACE; +import static org.osgi.resource.Namespace.REQUIREMENT_RESOLUTION_DIRECTIVE; +import static org.osgi.resource.Namespace.RESOLUTION_MANDATORY; +import static org.osgi.resource.Namespace.RESOLUTION_OPTIONAL; import static org.osgi.service.repository.ContentNamespace.CAPABILITY_URL_ATTRIBUTE; import static org.osgi.service.repository.ContentNamespace.CONTENT_NAMESPACE; @@ -89,9 +91,9 @@ public final class ResourceUtils { for (Capability cap : required.getCapabilities(null)) { if (cap.getNamespace().equals(IDENTITY_NAMESPACE)) { Map<String, Object> attributes = cap.getAttributes(); - Map<String, String> dirs = new HashMap<String, String>(); - dirs.put(Constants.RESOLUTION_DIRECTIVE, mandatory ? Constants.RESOLUTION_MANDATORY : Constants.RESOLUTION_OPTIONAL); - Map<String, Object> attrs = new HashMap<String, Object>(); + Map<String, String> dirs = new HashMap<>(); + dirs.put(REQUIREMENT_RESOLUTION_DIRECTIVE, mandatory ? RESOLUTION_MANDATORY : RESOLUTION_OPTIONAL); + Map<String, Object> attrs = new HashMap<>(); attrs.put(IDENTITY_NAMESPACE, attributes.get(IDENTITY_NAMESPACE)); attrs.put(CAPABILITY_TYPE_ATTRIBUTE, attributes.get(CAPABILITY_TYPE_ATTRIBUTE)); Version version = (Version) attributes.get(CAPABILITY_VERSION_ATTRIBUTE); http://git-wip-us.apache.org/repos/asf/karaf/blob/b982479d/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 d406726..d902a0e 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 @@ -78,6 +78,7 @@ 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 org.osgi.resource.Capability; import org.osgi.resource.Resource; import org.osgi.resource.Wire; import org.slf4j.Logger; @@ -85,12 +86,12 @@ import org.slf4j.LoggerFactory; import static org.apache.felix.resolver.Util.getSymbolicName; import static org.apache.felix.resolver.Util.getVersion; +import static org.apache.karaf.features.internal.resolver.ResourceUtils.TYPE_SUBSYSTEM; 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.service.StateStorage.toStringStringSetMap; import static org.apache.karaf.features.internal.util.MapUtils.addToMapSet; import static org.apache.karaf.features.internal.util.MapUtils.apply; -import static org.apache.karaf.features.internal.util.MapUtils.contains; 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; @@ -102,14 +103,16 @@ import static org.osgi.framework.Bundle.STARTING; import static org.osgi.framework.Bundle.STOPPING; import static org.osgi.framework.Bundle.STOP_TRANSIENT; import static org.osgi.framework.Bundle.UNINSTALLED; +import static org.osgi.framework.namespace.IdentityNamespace.CAPABILITY_TYPE_ATTRIBUTE; +import static org.osgi.framework.namespace.IdentityNamespace.IDENTITY_NAMESPACE; +import static org.osgi.resource.Namespace.CAPABILITY_EFFECTIVE_DIRECTIVE; +import static org.osgi.resource.Namespace.EFFECTIVE_ACTIVE; /** * */ public class FeaturesServiceImpl implements FeaturesService { - public static final String ROOT_REGION = "root"; - public static final String UPDATE_SNAPSHOTS_NONE = "none"; public static final String UPDATE_SNAPSHOTS_CRC = "crc"; public static final String UPDATE_SNAPSHOTS_ALWAYS = "always"; @@ -232,7 +235,8 @@ public class FeaturesServiceImpl implements FeaturesService { } // Resolve try { - doInstallFeaturesInThread(requestedFeatures, copyState(), options); + Map<String, Map<String, RequestedState>> stateChanges = Collections.emptyMap(); + doInstallFeaturesInThread(requestedFeatures, stateChanges, copyState(), options); } catch (Exception e) { LOGGER.warn("Error updating state", e); } @@ -756,7 +760,8 @@ public class FeaturesServiceImpl implements FeaturesService { required.put(region, fl); } fl.addAll(featuresToAdd); - doInstallFeaturesInThread(required, state, options); + Map<String, Map<String, RequestedState>> stateChanges = Collections.emptyMap(); + doInstallFeaturesInThread(required, stateChanges, state, options); } public void uninstallFeatures(Set<String> features, String region, EnumSet<Option> options) throws Exception { @@ -819,7 +824,14 @@ public class FeaturesServiceImpl implements FeaturesService { if (fl.isEmpty()) { required.remove(region); } - doInstallFeaturesInThread(required, state, options); + Map<String, Map<String, RequestedState>> stateChanges = Collections.emptyMap(); + doInstallFeaturesInThread(required, stateChanges, state, options); + } + + @Override + public void updateFeaturesState(Map<String, Map<String, RequestedState>> stateChanges, EnumSet<Option> options) throws Exception { + State state = copyState(); + doInstallFeaturesInThread(copy(state.requestedFeatures), stateChanges, state, options); } private State copyState() { @@ -845,6 +857,7 @@ public class FeaturesServiceImpl implements FeaturesService { * to bundles not being started after the refresh. */ public void doInstallFeaturesInThread(final Map<String, Set<String>> features, + final Map<String, Map<String, RequestedState>> stateChanges, final State state, final EnumSet<Option> options) throws Exception { ExecutorService executor = Executors.newCachedThreadPool(); @@ -852,7 +865,7 @@ public class FeaturesServiceImpl implements FeaturesService { executor.submit(new Callable<Object>() { @Override public Object call() throws Exception { - doInstallFeatures(features, state, options); + doInstallFeatures(features, stateChanges, state, options); return null; } }).get(); @@ -920,9 +933,10 @@ public class FeaturesServiceImpl implements FeaturesService { return state; } - public void doInstallFeatures(Map<String, Set<String>> requestedFeatures, // all request features - State state, // current state - EnumSet<Option> options // installation options + public void doInstallFeatures(Map<String, Set<String>> requestedFeatures, // all request features + Map<String, Map<String, RequestedState>> stateChanges, // features state changes + State state, // current state + EnumSet<Option> options // installation options ) throws Exception { boolean noRefreshUnmanaged = options.contains(Option.NoAutoRefreshUnmanagedBundles); @@ -933,6 +947,8 @@ public class FeaturesServiceImpl implements FeaturesService { boolean simulate = options.contains(Option.Simulate); boolean noManageBundles = options.contains(Option.NoAutoManageBundles); + // TODO: add an option to unmanage bundles instead of uninstalling those + DeploymentState dstate = getDeploymentState(); Map<String, Set<Long>> managedBundles = copy(state.managedBundles); @@ -959,24 +975,44 @@ public class FeaturesServiceImpl implements FeaturesService { 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, 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<>(); - if (noStart) { - for (Map.Entry<String, Set<Resource>> entry : featuresPerRegion.entrySet()) { - String region = entry.getKey(); - for (Resource resource : entry.getValue()) { - String id = getFeatureId(resource); - if (contains(state.installedFeatures, region, id)) { - addTransitive(resource, resourceLinkedToOldFeatures, resolver.getWiring()); + // + // Compute requested features state + // + Map<String, Map<String, String>> stateFeatures = copy(state.stateFeatures); + for (Map.Entry<String, Set<String>> entry : delFeatures.entrySet()) { + Map<String, String> map = stateFeatures.get(entry.getKey()); + if (map != null) { + map.entrySet().removeAll(entry.getValue()); + if (map.isEmpty()) { + stateFeatures.remove(entry.getKey()); + } + } + } + for (Map.Entry<String, Map<String, RequestedState>> entry1 : stateChanges.entrySet()) { + String region = entry1.getKey(); + Map<String, String> regionStates = stateFeatures.get(region); + if (regionStates != null) { + for (Map.Entry<String, RequestedState> entry2 : entry1.getValue().entrySet()) { + String feature = entry2.getKey(); + if (regionStates.containsKey(feature)) { + regionStates.put(feature, entry2.getValue().name()); } } } } + for (Map.Entry<String, Set<String>> entry : newFeatures.entrySet()) { + for (String feature : entry.getValue()) { + Map<String, String> map = stateFeatures.get(entry.getKey()); + if (map == null) { + map = new HashMap<>(); + stateFeatures.put(entry.getKey(), map); + } + map.put(feature, noStart ? RequestedState.Installed.name() : RequestedState.Started.name()); + } + } + + // Compute information for each bundle + Map<String, Map<String, BundleInfo>> bundleInfos = resolver.getBundleInfos(); // // Compute deployment @@ -1081,6 +1117,67 @@ public class FeaturesServiceImpl implements FeaturesService { // // + // Compute bundle states + // + Map<Resource, RequestedState> states = new HashMap<>(); + for (Map.Entry<String, Set<Resource>> entry : resolver.getFeaturesPerRegions().entrySet()) { + String region = entry.getKey(); + Map<String, String> fss = stateFeatures.get(region); + for (Resource feature : entry.getValue()) { + String fs = fss.get(getFeatureId(feature)); + propagateState(states, feature, RequestedState.valueOf(fs), resolver); + } + } + states.keySet().retainAll(resolver.getBundles().keySet()); + // + // Compute bundles to start, stop and resolve + // + for (Map.Entry<Resource, RequestedState> entry : states.entrySet()) { + Bundle bundle = deployment.resToBnd.get(entry.getKey()); + if (bundle != null) { + switch (entry.getValue()) { + case Started: + toResolve.add(bundle); + toStart.add(bundle); + break; + case Resolved: + toResolve.add(bundle); + toStop.add(bundle); + break; + } + } + } + // + // Compute bundle all start levels and start levels to update + // + FrameworkStartLevel fsl = systemBundleContext.getBundle().adapt(FrameworkStartLevel.class); + int initialBundleStartLevel = fsl.getInitialBundleStartLevel(); + int currentStartLevel = fsl.getStartLevel(); + Map<Resource, Integer> startLevels = new HashMap<>(); + Map<Bundle, Integer> toUpdateStartLevel = new HashMap<>(); + for (Map.Entry<String, Set<Resource>> entry : resolver.getBundlesPerRegions().entrySet()) { + String region = entry.getKey(); + for (Resource resource : entry.getValue()) { + BundleInfo bi = bundleInfos.get(region).get(getUri(resource)); + if (bi != null) { + int sl = bi.getStartLevel() > 0 ? bi.getStartLevel() : initialBundleStartLevel; + startLevels.put(resource, sl); + Bundle bundle = deployment.resToBnd.get(resource); + if (bundle != null) { + int curSl = bundle.adapt(BundleStartLevel.class).getStartLevel(); + if (sl != curSl) { + toUpdateStartLevel.put(bundle, sl); + if (sl > currentStartLevel) { + toStop.add(bundle); + } + } + } + } + } + } + + + // // Handle updates on the FeaturesService bundle // RegionDeployment rootRegionDeployment = deployment.regions.get(ROOT_REGION); @@ -1133,40 +1230,6 @@ public class FeaturesServiceImpl implements FeaturesService { // // - // Find start levels to update - // - Map<Bundle, Integer> toUpdateStartLevel = new HashMap<>(); - { - FrameworkStartLevel fsl = systemBundleContext.getBundle().adapt(FrameworkStartLevel.class); - for (Map.Entry<Resource, String> entry : resolver.getBundles().entrySet()) { - Resource resource = entry.getKey(); - Bundle bundle = deployment.resToBnd.get(resource); - String region = entry.getValue(); - BundleInfo bi = bundleInfos.get(region).get(getUri(resource)); - if (bundle != null && bi != null) { - int sl; - if (bi.getStartLevel() > 0) { - sl = bi.getStartLevel(); - } else { - sl = fsl.getInitialBundleStartLevel(); - } - BundleStartLevel bundleStartLevel = bundle.adapt(BundleStartLevel.class); - if (bundleStartLevel.getStartLevel() != sl) { - toUpdateStartLevel.put(bundle, sl); - if (sl > fsl.getStartLevel()) { - toStop.add(bundle); - } - } - if (bi.isStart()) { - toStart.add(bundle); - } else { - toStop.add(bundle); - } - } - } - } - - // // Stop bundles by chunks // for (RegionDeployment regionDeployment : deployment.regions.values()) { @@ -1355,15 +1418,17 @@ public class FeaturesServiceImpl implements FeaturesService { && isUpdateable(resource) && !deployment.bundleChecksums.containsKey(bundle.getBundleId())) { deployment.bundleChecksums.put(bundle.getBundleId(), crc); } - BundleInfo bi = bundleInfos.get(entry.getKey()).get(uri); - if (bi != null && bi.getStartLevel() > 0) { - bundle.adapt(BundleStartLevel.class).setStartLevel(bi.getStartLevel()); - } - toResolve.add(bundle); - if (resourceLinkedToOldFeatures.contains(resource)) { - toStart.add(bundle); - } else if (!noStart && (bi == null || bi.isStart())) { + int startLevel = startLevels.get(resource); + bundle.adapt(BundleStartLevel.class).setStartLevel(startLevel); + RequestedState reqState = states.get(resource); + switch (reqState) { + case Started: + toResolve.add(bundle); toStart.add(bundle); + break; + case Resolved: + toResolve.add(bundle); + break; } } } @@ -1379,6 +1444,8 @@ public class FeaturesServiceImpl implements FeaturesService { this.state.requestedFeatures.putAll(requestedFeatures); this.state.installedFeatures.clear(); this.state.installedFeatures.putAll(installedFeatures); + this.state.stateFeatures.clear(); + this.state.stateFeatures.putAll(stateFeatures); this.state.managedBundles.clear(); this.state.managedBundles.putAll(managedBundles); saveState(); @@ -1467,6 +1534,55 @@ public class FeaturesServiceImpl implements FeaturesService { print("Done.", verbose); } + private void propagateState(Map<Resource, RequestedState> states, Resource resource, RequestedState state, SubsystemResolver resolver) { + if (!isSubsystem(resource)) { + RequestedState reqState = mergeStates(state, states.get(resource)); + if (reqState != states.get(resource)) { + states.put(resource, reqState); + for (Wire wire : resolver.getWiring().get(resource)) { + Resource provider = wire.getProvider(); + RequestedState stateToMerge; + String region = resolver.getBundles().get(provider); + BundleInfo bi = region != null ? resolver.getBundleInfos().get(region).get(getUri(provider)) : null; + if (reqState == RequestedState.Started) { + String effective = wire.getCapability().getDirectives().get(CAPABILITY_EFFECTIVE_DIRECTIVE); + // If there is an active effective capability or a requirement from the feature + // and if the bundle is flagged as to start, start it + if ((EFFECTIVE_ACTIVE.equals(effective) || IDENTITY_NAMESPACE.equals(wire.getCapability().getNamespace())) + && (bi == null || bi.isStart())) { + stateToMerge = RequestedState.Started; + } else { + stateToMerge = RequestedState.Resolved; + } + } else { + stateToMerge = reqState; + } + propagateState(states, provider, stateToMerge, resolver); + } + } + } + } + + private boolean isSubsystem(Resource resource) { + for (Capability cap : resource.getCapabilities(null)) { + if (cap.getNamespace().equals(IDENTITY_NAMESPACE)) { + String type = (String) cap.getAttributes().get(CAPABILITY_TYPE_ATTRIBUTE); + return (type != null) && type.equals(TYPE_SUBSYSTEM); + } + } + return false; + } + + private RequestedState mergeStates(RequestedState s1, RequestedState s2) { + if (s1 == RequestedState.Started || s2 == RequestedState.Started) { + return RequestedState.Started; + } + if (s1 == RequestedState.Resolved || s2 == RequestedState.Resolved) { + return RequestedState.Resolved; + } + return RequestedState.Installed; + } + private void computeBundlesToRefresh(Set<Bundle> toRefresh, Collection<Bundle> bundles, Map<Resource, Bundle> resources, Map<Resource, List<Wire>> resolution) { int size; do { @@ -1510,14 +1626,6 @@ public class FeaturesServiceImpl implements FeaturesService { } 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)) { - addTransitive(wire.getProvider(), resources, resolution); - } - } - } - private void print(String message, boolean verbose) { LOGGER.info(message); if (verbose) { http://git-wip-us.apache.org/repos/asf/karaf/blob/b982479d/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 387e040..fa1fd18 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 @@ -27,11 +27,12 @@ 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>> 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>(); + public final Set<String> repositories = new TreeSet<>(); + public final Map<String, Set<String>> requestedFeatures = new HashMap<>(); + public final Map<String, Set<String>> installedFeatures = new HashMap<>(); + public final Map<String, Map<String, String>> stateFeatures = new HashMap<>(); + public final Map<String, Set<Long>> managedBundles = new HashMap<>(); + public final Map<Long, Long> bundleChecksums = new HashMap<>(); public State copy() { State state = new State(); @@ -39,6 +40,7 @@ public class State { MapUtils.copy(repositories, state.repositories); MapUtils.copy(requestedFeatures, state.requestedFeatures); MapUtils.copy(installedFeatures, state.installedFeatures); + MapUtils.copy(stateFeatures, state.stateFeatures); MapUtils.copy(managedBundles, state.managedBundles); MapUtils.copy(bundleChecksums, state.bundleChecksums); return state; http://git-wip-us.apache.org/repos/asf/karaf/blob/b982479d/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 c94bb0b..e085b65 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 @@ -44,6 +44,7 @@ public abstract class StateStorage { state.repositories.addAll(toStringSet((Collection) json.get("repositories"))); state.requestedFeatures.putAll(toStringStringSetMap((Map) json.get("features"))); state.installedFeatures.putAll(toStringStringSetMap((Map) json.get("installed"))); + state.stateFeatures.putAll(toStringStringStringMapMap((Map) json.get("state"))); state.managedBundles.putAll(toStringLongSetMap((Map) json.get("managed"))); state.bundleChecksums.putAll(toLongLongMap((Map) json.get("checksums"))); } @@ -60,6 +61,7 @@ public abstract class StateStorage { json.put("repositories", state.repositories); json.put("features", state.requestedFeatures); json.put("installed", state.installedFeatures); + json.put("state", state.stateFeatures); json.put("managed", state.managedBundles); json.put("checksums", toStringLongMap(state.bundleChecksums)); JsonWriter.write(os, json); @@ -71,6 +73,22 @@ public abstract class StateStorage { protected abstract OutputStream getOutputStream() throws IOException; + static Map<String, Map<String, String>> toStringStringStringMapMap(Map<?, ?> map) { + Map<String, Map<String, String>> nm = new HashMap<>(); + for (Map.Entry entry : map.entrySet()) { + nm.put(entry.getKey().toString(), toStringStringMap((Map) entry.getValue())); + } + return nm; + } + + static Map<String, String> toStringStringMap(Map<?, ?> map) { + Map<String, String> nm = new HashMap<>(); + for (Map.Entry entry : map.entrySet()) { + nm.put(entry.getKey().toString(), entry.getValue().toString()); + } + return nm; + } + static Map<String, Set<String>> toStringStringSetMap(Map<?, ?> map) { Map<String, Set<String>> nm = new HashMap<>(); for (Map.Entry entry : map.entrySet()) { http://git-wip-us.apache.org/repos/asf/karaf/blob/b982479d/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 efde48d..3288e84 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 @@ -53,7 +53,10 @@ public final class MapUtils { public static <U, T> Set<U> apply(Set<T> set, Function<T, U> function) { Set<U> result = new HashSet<>(set.size()); for (T t : set) { - result.add(function.apply(t)); + U u = function.apply(t); + if (u != null) { + result.add(u); + } } return result; } @@ -61,7 +64,11 @@ public final class MapUtils { 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<>(col.size()); for (S s : col) { - result.put(key.apply(s), value.apply(s)); + T t = key.apply(s); + U u = value.apply(s); + if (t != null && u != null) { + result.put(t, u); + } } return result; } http://git-wip-us.apache.org/repos/asf/karaf/blob/b982479d/shell/commands/pom.xml ---------------------------------------------------------------------- diff --git a/shell/commands/pom.xml b/shell/commands/pom.xml index 96589d0..93505ef 100644 --- a/shell/commands/pom.xml +++ b/shell/commands/pom.xml @@ -106,6 +106,9 @@ <Bundle-Activator> org.apache.karaf.shell.commands.impl.info.Activator </Bundle-Activator> + <Require-Capability> + osgi.service;filter:="(objectClass=org.jledit.EditorFactory)";effective:=active + </Require-Capability> <Karaf-Commands> org.apache.karaf.shell.commands.impl </Karaf-Commands>