FELIX-1682: the newly installed bundles for a feature shuld be uninstalled when feature install failed
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@824313 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/karaf/features/command/src/main/java/org/apache/felix/karaf/features/command/InstallFeatureCommand.java b/karaf/features/command/src/main/java/org/apache/felix/karaf/features/command/InstallFeatureCommand.java
index 67a0571..47dd4e6 100644
--- a/karaf/features/command/src/main/java/org/apache/felix/karaf/features/command/InstallFeatureCommand.java
+++ b/karaf/features/command/src/main/java/org/apache/felix/karaf/features/command/InstallFeatureCommand.java
@@ -16,6 +16,7 @@
*/
package org.apache.felix.karaf.features.command;
+import org.apache.felix.gogo.commands.Option;
import org.apache.felix.karaf.features.FeaturesService;
import org.apache.felix.gogo.commands.Argument;
import org.apache.felix.gogo.commands.Command;
@@ -23,16 +24,19 @@
@Command(scope = "features", name = "install", description = "Installs a feature with the specified name and version.")
public class InstallFeatureCommand extends FeaturesCommandSupport {
+ private static String DEFAULT_VERSION = "0.0.0";
+
@Argument(index = 0, name = "name", description = "The name of the feature", required = true, multiValued = false)
String name;
@Argument(index = 1, name = "version", description = "The version of the feature", required = false, multiValued = false)
String version;
+ @Option(name = "-n", aliases = "--no-clean", description = "Do not uninstall bundles on failure", required = false, multiValued = false)
+ boolean noClean;
protected void doExecute(FeaturesService admin) throws Exception {
- if (version != null && version.length() > 0) {
- admin.installFeature(name, version);
- } else {
- admin.installFeature(name);
+ if (version == null || version.length() == 0) {
+ version = DEFAULT_VERSION;
}
+ admin.installFeature(name, version, !noClean);
}
}
diff --git a/karaf/features/core/src/main/java/org/apache/felix/karaf/features/FeaturesService.java b/karaf/features/core/src/main/java/org/apache/felix/karaf/features/FeaturesService.java
index cd1e9e4..f0052d1 100644
--- a/karaf/features/core/src/main/java/org/apache/felix/karaf/features/FeaturesService.java
+++ b/karaf/features/core/src/main/java/org/apache/felix/karaf/features/FeaturesService.java
@@ -33,6 +33,8 @@
void installFeature(String name, String version) throws Exception;
+ void installFeature(String name, String version, boolean cleanIfFailure) throws Exception;
+
void uninstallFeature(String name) throws Exception;
void uninstallFeature(String name, String version) throws Exception;
diff --git a/karaf/features/core/src/main/java/org/apache/felix/karaf/features/internal/FeaturesServiceImpl.java b/karaf/features/core/src/main/java/org/apache/felix/karaf/features/internal/FeaturesServiceImpl.java
index e68fb04..d2a4e76 100644
--- a/karaf/features/core/src/main/java/org/apache/felix/karaf/features/internal/FeaturesServiceImpl.java
+++ b/karaf/features/core/src/main/java/org/apache/felix/karaf/features/internal/FeaturesServiceImpl.java
@@ -196,16 +196,61 @@
}
public void installFeature(String name, String version) throws Exception {
+ installFeature(name, version, true);
+ }
+
+ public void installFeature(String name, String version, boolean cleanIfFailure) throws Exception {
+ InstallationState state = new InstallationState();
Feature f = getFeature(name, version);
if (f == null) {
- throw new Exception("No feature named '" + name
+ throw new Exception("No feature named '" + name
+ "' with version '" + version + "' available");
}
- for (Feature dependency : f.getDependencies()) {
- installFeature(dependency.getName(), dependency.getVersion());
+ try {
+ // Install everything
+ doInstallFeature(state, f);
+ // Start all bundles
+ for (Bundle b : state.bundles) {
+ // do not start fragment bundles.
+ Dictionary d = b.getHeaders();
+ String fragmentHostHeader = (String) d.get(Constants.FRAGMENT_HOST);
+ if (fragmentHostHeader == null || fragmentHostHeader.trim().length() == 0) {
+ b.start();
+ }
+ }
+ } catch (Exception e) {
+ // uninstall everything
+ if (cleanIfFailure) {
+ for (Bundle b : state.bundles) {
+ b.uninstall();
+ }
+ }
+ // rethrow exception
+ throw e;
}
- for (String config : f.getConfigurations().keySet()) {
- Dictionary<String,String> props = new Hashtable<String, String>(f.getConfigurations().get(config));
+ callListeners(new FeatureEvent(f, FeatureEvent.EventType.FeatureInstalled, false));
+ for (Map.Entry<Feature, Set<Long>> e : state.features.entrySet()) {
+ installed.put(e.getKey(), e.getValue());
+ }
+ saveState();
+ }
+
+ protected static class InstallationState {
+ final Set<Bundle> bundles = new HashSet<Bundle>();
+ final Map<Feature, Set<Long>> features = new HashMap<Feature, Set<Long>>();
+ }
+
+ protected void doInstallFeature(InstallationState state, Feature feature) throws Exception {
+ for (Feature dependency : feature.getDependencies()) {
+ Feature f = getFeature(dependency.getName(), dependency.getVersion());
+ if (f == null) {
+ throw new Exception("No feature named '" + dependency.getName()
+ + "' with version '" + dependency.getVersion() + "' available");
+ }
+ doInstallFeature(state, f);
+ }
+ for (String config : feature.getConfigurations().keySet()) {
+ Dictionary<String,String> props = new Hashtable<String, String>(feature.getConfigurations().get(config));
String[] pid = parsePid(config);
String key = (pid[1] == null ? pid[0] : pid[0] + "-" + pid[1]);
props.put(CONFIG_KEY, key);
@@ -216,25 +261,19 @@
cfg.update(props);
}
Set<Long> bundles = new HashSet<Long>();
- for (String bundleLocation : f.getBundles()) {
- Bundle b = installBundleIfNeeded(bundleLocation);
- bundles.add(b.getBundleId());
- }
- for (long id : bundles) {
- Bundle b = bundleContext.getBundle(id);
- // do not start fragment bundles.
- Dictionary d = b.getHeaders();
- String fragmentHostHeader = (String) d.get(Constants.FRAGMENT_HOST);
- if (fragmentHostHeader == null || fragmentHostHeader.trim().length() == 0) {
- b.start();
+ for (String bundleLocation : feature.getBundles()) {
+ try {
+ Bundle b = installBundleIfNeeded(bundleLocation);
+ state.bundles.add(b);
+ bundles.add(b.getBundleId());
+ } catch (BundleAlreadyInstalledException e) {
+ bundles.add(e.getBundle().getBundleId());
}
}
- callListeners(new FeatureEvent(f, FeatureEvent.EventType.FeatureInstalled, false));
- installed.put(f, bundles);
- saveState();
+ state.features.put(feature, bundles);
}
- protected Bundle installBundleIfNeeded(String bundleLocation) throws IOException, BundleException {
+ protected Bundle installBundleIfNeeded(String bundleLocation) throws IOException, BundleException, BundleAlreadyInstalledException {
LOGGER.debug("Checking " + bundleLocation);
InputStream is;
try {
@@ -256,7 +295,7 @@
Version bv = vStr == null ? Version.emptyVersion : Version.parseVersion(vStr);
if (v.equals(bv)) {
LOGGER.debug(" found installed bundle: " + b);
- return b;
+ throw new BundleAlreadyInstalledException(b);
}
}
}
@@ -273,6 +312,18 @@
}
}
+ protected static class BundleAlreadyInstalledException extends Exception {
+ private final Bundle bundle;
+
+ public BundleAlreadyInstalledException(Bundle bundle) {
+ this.bundle = bundle;
+ }
+
+ public Bundle getBundle() {
+ return bundle;
+ }
+ }
+
public void uninstallFeature(String name) throws Exception {
List<String> versions = new ArrayList<String>();
for (Feature f : installed.keySet()) {