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 2014/04/29 16:00:06 UTC
git commit: [KARAF-2952] Add a lifecycle for features
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 <gn...@gmail.com>
Authored: Tue Apr 29 15:44:40 2014 +0200
Committer: Guillaume Nodet <gn...@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>