You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2019/05/23 09:29:20 UTC

[karaf] branch master updated: [KARAF-2894] Add option to delete configurations/configuration files while uninstalling feature

This is an automated email from the ASF dual-hosted git repository.

jbonofre pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/karaf.git


The following commit(s) were added to refs/heads/master by this push:
     new 2d3c15e  [KARAF-2894] Add option to delete configurations/configuration files while uninstalling feature
     new 54505d8  Merge pull request #849 from jbonofre/KARAF-2894
2d3c15e is described below

commit 2d3c15ea30222776d092edf138ac540e4115a55a
Author: Jean-Baptiste Onofré <jb...@apache.org>
AuthorDate: Tue May 21 14:53:12 2019 +0200

    [KARAF-2894] Add option to delete configurations/configuration files while uninstalling feature
---
 .../apache/karaf/config/command/ExistsCommand.java | 42 ++++++++++++++
 .../org/apache/karaf/config/core/ConfigMBean.java  |  9 +++
 .../apache/karaf/config/core/ConfigRepository.java |  2 +
 .../karaf/config/core/impl/ConfigMBeanImpl.java    |  9 +++
 .../config/core/impl/ConfigRepositoryImpl.java     |  9 +++
 .../features/command/UninstallFeatureCommand.java  |  4 ++
 .../org/apache/karaf/features/FeaturesService.java |  3 +-
 .../management/FeaturesServiceMBeanImpl.java       | 41 +++++++++++--
 .../internal/service/BundleInstallSupport.java     |  2 +
 .../internal/service/BundleInstallSupportImpl.java |  7 +++
 .../karaf/features/internal/service/Deployer.java  | 42 ++++++++++++++
 .../internal/service/FeatureConfigInstaller.java   | 30 ++++++++++
 .../internal/service/FeaturesServiceImpl.java      |  5 ++
 .../features/management/FeaturesServiceMBean.java  |  4 ++
 .../features/internal/service/DeployerTest.java    |  5 ++
 .../java/org/apache/karaf/itests/FeatureTest.java  | 67 ++++++++++++++++++++++
 .../profile/assembly/AssemblyDeployCallback.java   |  6 ++
 .../java/org/apache/karaf/tooling/VerifyMojo.java  |  7 ++-
 18 files changed, 286 insertions(+), 8 deletions(-)

diff --git a/config/src/main/java/org/apache/karaf/config/command/ExistsCommand.java b/config/src/main/java/org/apache/karaf/config/command/ExistsCommand.java
new file mode 100644
index 0000000..a1032da
--- /dev/null
+++ b/config/src/main/java/org/apache/karaf/config/command/ExistsCommand.java
@@ -0,0 +1,42 @@
+/*
+ * 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.config.command;
+
+import org.apache.karaf.config.command.completers.ConfigurationCompleter;
+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.lifecycle.Service;
+
+@Command(scope = "config", name = "exists", description = "Check if a configuration exists.")
+@Service
+public class ExistsCommand extends ConfigCommandSupport {
+
+    @Argument(index = 0, name = "pid", description = "PID of the configuration", required = true, multiValued = false)
+    @Completion(ConfigurationCompleter.class)
+    String pid;
+
+    @Override
+    protected Object doExecute() throws Exception {
+        if (configRepository.exists(pid)) {
+            return Boolean.TRUE;
+        } else {
+            return Boolean.FALSE;
+        }
+    }
+
+}
diff --git a/config/src/main/java/org/apache/karaf/config/core/ConfigMBean.java b/config/src/main/java/org/apache/karaf/config/core/ConfigMBean.java
index 283f677..6ae174b 100644
--- a/config/src/main/java/org/apache/karaf/config/core/ConfigMBean.java
+++ b/config/src/main/java/org/apache/karaf/config/core/ConfigMBean.java
@@ -57,6 +57,15 @@ public interface ConfigMBean {
     void delete(String pid) throws MBeanException;
 
     /**
+     * Check if a configuration identified by the given PID exists.
+     *
+     * @param pid The configuration PID to check.
+     * @return true if the configuration exists, false else.
+     * @throws MBeanException in case of MBean failure.
+     */
+    boolean exists(String pid) throws MBeanException;
+
+    /**
      * Get the list of properties for a configuration PID.
      *
      * @param pid the configuration PID.
diff --git a/config/src/main/java/org/apache/karaf/config/core/ConfigRepository.java b/config/src/main/java/org/apache/karaf/config/core/ConfigRepository.java
index 556cc17..36dd59a 100644
--- a/config/src/main/java/org/apache/karaf/config/core/ConfigRepository.java
+++ b/config/src/main/java/org/apache/karaf/config/core/ConfigRepository.java
@@ -31,6 +31,8 @@ public interface ConfigRepository {
 
     void delete(String pid) throws Exception;
 
+    boolean exists(String pid) throws Exception;
+
     void update(String pid, Map<String, Object> properties) throws IOException;
 
     String createFactoryConfiguration(String factoryPid, Map<String, Object> properties) throws IOException;
diff --git a/config/src/main/java/org/apache/karaf/config/core/impl/ConfigMBeanImpl.java b/config/src/main/java/org/apache/karaf/config/core/impl/ConfigMBeanImpl.java
index cdd2f3c..ec37598 100644
--- a/config/src/main/java/org/apache/karaf/config/core/impl/ConfigMBeanImpl.java
+++ b/config/src/main/java/org/apache/karaf/config/core/impl/ConfigMBeanImpl.java
@@ -125,6 +125,15 @@ public class ConfigMBeanImpl extends StandardMBean implements ConfigMBean {
     }
 
     @Override
+    public boolean exists(String pid) throws MBeanException {
+        try {
+            return this.configRepo.exists(pid);
+        } catch (Exception e) {
+            throw new MBeanException(null, e.toString());
+        }
+    }
+
+    @Override
     @SuppressWarnings("rawtypes")
     public Map<String, String> listProperties(String pid) throws MBeanException {
         try {
diff --git a/config/src/main/java/org/apache/karaf/config/core/impl/ConfigRepositoryImpl.java b/config/src/main/java/org/apache/karaf/config/core/impl/ConfigRepositoryImpl.java
index 04757bb..1643daf 100644
--- a/config/src/main/java/org/apache/karaf/config/core/impl/ConfigRepositoryImpl.java
+++ b/config/src/main/java/org/apache/karaf/config/core/impl/ConfigRepositoryImpl.java
@@ -92,6 +92,15 @@ public class ConfigRepositoryImpl implements ConfigRepository {
         configuration.delete();
     }
 
+    @Override
+    public boolean exists(String pid) throws Exception {
+        Configuration[] configurations = configAdmin.listConfigurations("(service.pid=" + pid + ")");
+        if (configurations == null || configurations.length == 0) {
+            return false;
+        }
+        return true;
+    }
+
     private File getCfgFileFromProperties(Dictionary<String, Object> properties) throws URISyntaxException, MalformedURLException {
         if (properties != null) {
             Object val = properties.get(FILEINSTALL_FILE_NAME);
diff --git a/features/command/src/main/java/org/apache/karaf/features/command/UninstallFeatureCommand.java b/features/command/src/main/java/org/apache/karaf/features/command/UninstallFeatureCommand.java
index 5ce3a03..2cbcd48 100644
--- a/features/command/src/main/java/org/apache/karaf/features/command/UninstallFeatureCommand.java
+++ b/features/command/src/main/java/org/apache/karaf/features/command/UninstallFeatureCommand.java
@@ -47,10 +47,14 @@ public class UninstallFeatureCommand extends FeaturesCommandSupport {
     @Option(name = "-g", aliases = "--region", description = "Region to install to")
     String region;
 
+    @Option(name = "-c", aliases = "--delete-configurations", description = "Delete configurations", required = false, multiValued = false)
+    boolean deleteConfigurations;
+
     protected void doExecute(FeaturesService admin) throws Exception {
         addOption(FeaturesService.Option.Simulate, simulate);
         addOption(FeaturesService.Option.Verbose, verbose);
         addOption(FeaturesService.Option.NoAutoRefreshBundles, noRefresh);
+        addOption(FeaturesService.Option.DeleteConfigurations, deleteConfigurations);
         admin.uninstallFeatures(new HashSet<>(features), region, options);
     }
 }
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 2594dab..6c2b4e1 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
@@ -57,7 +57,8 @@ public interface FeaturesService {
         Verbose,
         Upgrade,
         DisplayFeaturesWiring,
-        DisplayAllWiring
+        DisplayAllWiring,
+        DeleteConfigurations
     }
 
     /**
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/management/FeaturesServiceMBeanImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/management/FeaturesServiceMBeanImpl.java
index 47b369b..6882b7f 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/management/FeaturesServiceMBeanImpl.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/management/FeaturesServiceMBeanImpl.java
@@ -83,9 +83,7 @@ public class FeaturesServiceMBeanImpl extends StandardEmitterMBean implements
     public void postDeregister() {
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    @Override
     public TabularData getFeatures() throws Exception {
         try {
             List<Feature> allFeatures = Arrays.asList(featuresService.listFeatures());
@@ -105,9 +103,7 @@ public class FeaturesServiceMBeanImpl extends StandardEmitterMBean implements
         }
     }
 
-    /**
-     * {@inheritDoc}
-     */
+    @Override
     public TabularData getRepositories() throws Exception {
         try {
             List<Repository> allRepositories = Arrays.asList(featuresService.listRepositories());
@@ -131,6 +127,7 @@ public class FeaturesServiceMBeanImpl extends StandardEmitterMBean implements
         return infoFeature(featuresService.repositoryProvidedFeatures(new URI(uri)));
     }
 
+    @Override
     public void addRepository(String uri) throws Exception {
         URI repoUri = new URI(uri);
         if (featuresService.isRepositoryUriBlacklisted(repoUri)) {
@@ -139,6 +136,7 @@ public class FeaturesServiceMBeanImpl extends StandardEmitterMBean implements
         featuresService.addRepository(repoUri);
     }
 
+    @Override
     public void addRepository(String uri, boolean install) throws Exception {
         URI repoUri = new URI(uri);
         if (featuresService.isRepositoryUriBlacklisted(repoUri)) {
@@ -147,10 +145,12 @@ public class FeaturesServiceMBeanImpl extends StandardEmitterMBean implements
         featuresService.addRepository(repoUri, install);
     }
 
+    @Override
     public void removeRepository(String uri) throws Exception {
         removeRepository(uri, false);
     }
 
+    @Override
     public void removeRepository(String uri, boolean uninstall) throws Exception {
         List<URI> uris = new ArrayList<>();
         Pattern pattern = Pattern.compile(uri);
@@ -180,6 +180,7 @@ public class FeaturesServiceMBeanImpl extends StandardEmitterMBean implements
         }
     }
 
+    @Override
     public void refreshRepository(String uri) throws Exception {
         if (uri == null || uri.isEmpty()) {
             for (Repository repository : featuresService.listRepositories()) {
@@ -202,10 +203,12 @@ public class FeaturesServiceMBeanImpl extends StandardEmitterMBean implements
         }
     }
 
+    @Override
     public void installFeature(String name) throws Exception {
         featuresService.installFeature(name);
     }
 
+    @Override
     public void installFeature(String name, boolean noRefresh) throws Exception {
         EnumSet<org.apache.karaf.features.FeaturesService.Option> options = EnumSet.noneOf(org.apache.karaf.features.FeaturesService.Option.class);
         if (noRefresh) {
@@ -214,6 +217,7 @@ public class FeaturesServiceMBeanImpl extends StandardEmitterMBean implements
         featuresService.installFeature(name, options);
     }
 
+    @Override
     public void installFeature(String name, boolean noRefresh, boolean noStart) throws Exception {
         EnumSet<org.apache.karaf.features.FeaturesService.Option> options = EnumSet.noneOf(org.apache.karaf.features.FeaturesService.Option.class);
         if (noRefresh) {
@@ -225,10 +229,12 @@ public class FeaturesServiceMBeanImpl extends StandardEmitterMBean implements
         featuresService.installFeature(name, options);
     }
 
+    @Override
     public void installFeature(String name, String version) throws Exception {
         featuresService.installFeature(name, version);
     }
 
+    @Override
     public void installFeature(String name, String version, boolean noRefresh) throws Exception {
         EnumSet<org.apache.karaf.features.FeaturesService.Option> options = EnumSet.noneOf(org.apache.karaf.features.FeaturesService.Option.class);
         if (noRefresh) {
@@ -237,6 +243,7 @@ public class FeaturesServiceMBeanImpl extends StandardEmitterMBean implements
         featuresService.installFeature(name, version, options);
     }
 
+    @Override
     public void installFeature(String name, String version, boolean noRefresh, boolean noStart) throws Exception {
         EnumSet<org.apache.karaf.features.FeaturesService.Option> options = EnumSet.noneOf(org.apache.karaf.features.FeaturesService.Option.class);
         if (noRefresh) {
@@ -248,6 +255,7 @@ public class FeaturesServiceMBeanImpl extends StandardEmitterMBean implements
         featuresService.installFeature(name, version, options);
     }
 
+    @Override
     public TabularData infoFeature(String name) throws Exception {
         try {
             Feature[] features = featuresService.getFeatures(name);
@@ -258,6 +266,7 @@ public class FeaturesServiceMBeanImpl extends StandardEmitterMBean implements
         }
     }
 
+    @Override
     public TabularData infoFeature(String name, String version) throws Exception {
         try {
             Feature[] features = featuresService.getFeatures(name, version);
@@ -279,27 +288,47 @@ public class FeaturesServiceMBeanImpl extends StandardEmitterMBean implements
         return JmxFeature.tableFrom(features);
     }
 
+    @Override
     public void uninstallFeature(String name) throws Exception {
         featuresService.uninstallFeature(name);
     }
 
+    @Override
     public void uninstallFeature(String name, boolean noRefresh) throws Exception {
+        uninstallFeature(name, noRefresh, false);
+    }
+
+    @Override
+    public void uninstallFeature(String name, boolean noRefresh, boolean deleteConfigurations) throws Exception {
         EnumSet<org.apache.karaf.features.FeaturesService.Option> options = EnumSet.noneOf(org.apache.karaf.features.FeaturesService.Option.class);
         if (noRefresh) {
             options.add(org.apache.karaf.features.FeaturesService.Option.NoAutoRefreshBundles);
         }
+        if (deleteConfigurations) {
+            options.add(org.apache.karaf.features.FeaturesService.Option.DeleteConfigurations);
+        }
         featuresService.uninstallFeature(name, options);
     }
 
+    @Override
     public void uninstallFeature(String name, String version) throws Exception {
         featuresService.uninstallFeature(name, version);
     }
 
+    @Override
     public void uninstallFeature(String name, String version, boolean noRefresh) throws Exception {
+        uninstallFeature(name, version, noRefresh, false);
+    }
+
+    @Override
+    public void uninstallFeature(String name, String version, boolean noRefresh, boolean deleteConfigurations) throws Exception {
         EnumSet<org.apache.karaf.features.FeaturesService.Option> options = EnumSet.noneOf(org.apache.karaf.features.FeaturesService.Option.class);
         if (noRefresh) {
             options.add(FeaturesService.Option.NoAutoRefreshBundles);
         }
+        if (deleteConfigurations) {
+            options.add(FeaturesService.Option.DeleteConfigurations);
+        }
         featuresService.uninstallFeature(name, version, options);
     }
 
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/BundleInstallSupport.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/BundleInstallSupport.java
index 9dc06a4..b37f689 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/service/BundleInstallSupport.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/BundleInstallSupport.java
@@ -68,6 +68,8 @@ public interface BundleInstallSupport {
     RegionDigraph getDiGraphCopy() throws BundleException;
     
     void installConfigs(Feature feature) throws IOException, InvalidSyntaxException;
+
+    void deleteConfigs(Feature feature) throws IOException, InvalidSyntaxException;
     
     void installLibraries(Feature feature) throws IOException;
 
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/BundleInstallSupportImpl.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/BundleInstallSupportImpl.java
index b05425c..ca893df 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/service/BundleInstallSupportImpl.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/BundleInstallSupportImpl.java
@@ -303,6 +303,13 @@ public class BundleInstallSupportImpl implements BundleInstallSupport {
     }
 
     @Override
+    public void deleteConfigs(Feature feature) throws IOException, InvalidSyntaxException {
+        if (configInstaller != null) {
+            configInstaller.uninstallFeatureConfigs(feature);
+        }
+    }
+
+    @Override
     public void installLibraries(Feature feature) {
         // TODO: install libraries
     }
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
index c7c077c..51b1d45 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/Deployer.java
@@ -42,6 +42,8 @@ import org.apache.felix.utils.version.VersionRange;
 import org.apache.felix.utils.version.VersionTable;
 import org.apache.karaf.features.BundleInfo;
 import org.apache.karaf.features.Conditional;
+import org.apache.karaf.features.ConfigFileInfo;
+import org.apache.karaf.features.ConfigInfo;
 import org.apache.karaf.features.DeploymentEvent;
 import org.apache.karaf.features.Feature;
 import org.apache.karaf.features.FeatureEvent;
@@ -49,6 +51,7 @@ import org.apache.karaf.features.FeatureState;
 import org.apache.karaf.features.FeaturesService;
 import org.apache.karaf.features.internal.download.DownloadManager;
 import org.apache.karaf.features.internal.download.StreamProvider;
+import org.apache.karaf.features.internal.model.ConfigFile;
 import org.apache.karaf.features.internal.region.SubsystemResolver;
 import org.apache.karaf.features.internal.region.SubsystemResolverCallback;
 import org.apache.karaf.features.internal.resolver.FeatureResource;
@@ -60,6 +63,7 @@ import org.apache.karaf.features.internal.util.MapUtils;
 import org.apache.karaf.features.internal.util.MultiException;
 import org.eclipse.equinox.region.Region;
 import org.eclipse.equinox.region.RegionDigraph;
+import org.omg.CORBA.DynAnyPackage.Invalid;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
@@ -133,6 +137,7 @@ public class Deployer {
         void replaceDigraph(Map<String, Map<String, Map<String, Set<String>>>> policies,
                             Map<String, Set<Long>> bundles) throws BundleException, InvalidSyntaxException;
         void installConfigs(Feature feature) throws IOException, InvalidSyntaxException;
+        void deleteConfigs(Feature feature) throws IOException, InvalidSyntaxException;
         void installLibraries(Feature feature) throws IOException;
     }
 
@@ -359,6 +364,7 @@ public class Deployer {
                     || request.options.contains(FeaturesService.Option.DisplayAllWiring);
         boolean showFeaturesWiringOnly = request.options.contains(FeaturesService.Option.DisplayFeaturesWiring)
                     && !request.options.contains(FeaturesService.Option.DisplayAllWiring);
+        boolean deleteConfigurations = request.options.contains(FeaturesService.Option.DeleteConfigurations);
 
         // TODO: add an option to unmanage bundles instead of uninstalling those
 
@@ -647,6 +653,30 @@ public class Deployer {
                     print("    " + bundle.getSymbolicName() + "/" + bundle.getVersion(), verbose);
                 }
             }
+            if (deleteConfigurations) {
+                print(" Configurations to delete:", verbose);
+                for (Map.Entry<String, Set<String>> entry : delFeatures.entrySet()) {
+                    for (String name : entry.getValue()) {
+                        Feature feature = dstate.featuresById.get(name);
+                        if (feature != null) {
+                            for (ConfigInfo configInfo : feature.getConfigurations()) {
+                                print("    " + configInfo.getName(), verbose);
+                            }
+                        }
+                    }
+                }
+                print(" Configuration Files to delete:", verbose);
+                for (Map.Entry<String, Set<String>> entry : delFeatures.entrySet()) {
+                    for (String name : entry.getValue()) {
+                        Feature feature = dstate.featuresById.get(name);
+                        if (feature != null) {
+                            for (ConfigFileInfo configFileInfo : feature.getConfigurationFiles()) {
+                                print("    " + configFileInfo.getFinalname(), verbose);
+                            }
+                        }
+                    }
+                }
+            }
             return;
         }
 
@@ -936,6 +966,16 @@ public class Deployer {
             }
         }
 
+        // Delete configurations
+        if (deleteConfigurations) {
+            for (Map.Entry<String, Set<String>> entry : delFeatures.entrySet()) {
+                for (String name : entry.getValue()) {
+                    Feature feature = dstate.featuresById.get(name);
+                    callback.deleteConfigs(feature);
+                }
+            }
+        }
+
         if (!noRefresh) {
             if (toRefresh.containsKey(dstate.bundles.get(0l))) {
                 print("The system bundle needs to be refreshed, restarting Karaf...", verbose);
@@ -1005,6 +1045,8 @@ public class Deployer {
             }
         }
 
+        // If uninstall and delete configurations, actually delete configurations and configuration files
+
         // Call listeners
         for (Map.Entry<String, Set<String>> entry : delFeatures.entrySet()) {
             for (String name : entry.getValue()) {
diff --git a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java
index 66ec66d..e401a69 100644
--- a/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java
+++ b/features/core/src/main/java/org/apache/karaf/features/internal/service/FeatureConfigInstaller.java
@@ -157,6 +157,36 @@ public class FeatureConfigInstaller {
         }
     }
 
+    public void uninstallFeatureConfigs(Feature feature) throws IOException, InvalidSyntaxException {
+        if (feature != null) {
+            if (feature.getConfigurations() != null) {
+                for (ConfigInfo configInfo : feature.getConfigurations()) {
+                    ConfigId configId = parsePid(configInfo.getName());
+                    Configuration configuration = findExistingConfiguration(configAdmin, configId);
+                    if (configuration != null) {
+                        configuration.delete();
+                    }
+                    File cfgFile = null;
+                    if (storage != null) {
+                        cfgFile = new File(storage, configId.fullPid + ".cfg");
+                    }
+                    if (cfgFile.exists()) {
+                        cfgFile.delete();
+                    }
+                }
+            }
+            if (feature.getConfigurationFiles() != null) {
+                for (ConfigFileInfo configFileInfo : feature.getConfigurationFiles()) {
+                    String finalname = substFinalName(configFileInfo.getFinalname());
+                    File cfgFile = new File(finalname);
+                    if (cfgFile.exists()) {
+                        cfgFile.delete();
+                    }
+                }
+            }
+        }
+    }
+
     private Dictionary<String, Object> convertToDict(Map<String, Object> props) {
         Dictionary<String, Object> cfgProps = new Hashtable<>();
         for (Map.Entry<String, Object> e : props.entrySet()) {
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 d6e9e40..e6c54c4c 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
@@ -1179,6 +1179,11 @@ public class FeaturesServiceImpl implements FeaturesService, Deployer.DeployCall
     }
 
     @Override
+    public void deleteConfigs(Feature feature) throws IOException, InvalidSyntaxException {
+        installSupport.deleteConfigs(feature);
+    }
+
+    @Override
     public void installLibraries(Feature feature) throws IOException {
         installSupport.installLibraries(feature);
     }
diff --git a/features/core/src/main/java/org/apache/karaf/features/management/FeaturesServiceMBean.java b/features/core/src/main/java/org/apache/karaf/features/management/FeaturesServiceMBean.java
index 70fe8a6..c0a3a30 100644
--- a/features/core/src/main/java/org/apache/karaf/features/management/FeaturesServiceMBean.java
+++ b/features/core/src/main/java/org/apache/karaf/features/management/FeaturesServiceMBean.java
@@ -149,8 +149,12 @@ public interface FeaturesServiceMBean {
 
     void uninstallFeature(String name, boolean noRefresh) throws Exception;
 
+    void uninstallFeature(String name, boolean noRefresh, boolean deleteConfigurations) throws Exception;
+
     void uninstallFeature(String name, String version) throws Exception;
 
     void uninstallFeature(String name, String version, boolean noRefresh) throws Exception;
 
+    void uninstallFeature(String name, String version, boolean noRefresh, boolean deleteConfigurations) throws Exception;
+
 }
diff --git a/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java b/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java
index a124e8d..6b437eb 100644
--- a/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java
+++ b/features/core/src/test/java/org/apache/karaf/features/internal/service/DeployerTest.java
@@ -629,6 +629,11 @@ public class DeployerTest {
         }
 
         @Override
+        public void deleteConfigs(Feature feature) throws IOException, InvalidSyntaxException {
+
+        }
+
+        @Override
         public void installLibraries(Feature feature) throws IOException {
 
         }
diff --git a/itests/test/src/test/java/org/apache/karaf/itests/FeatureTest.java b/itests/test/src/test/java/org/apache/karaf/itests/FeatureTest.java
index 9f58b75..dff4a20 100644
--- a/itests/test/src/test/java/org/apache/karaf/itests/FeatureTest.java
+++ b/itests/test/src/test/java/org/apache/karaf/itests/FeatureTest.java
@@ -33,6 +33,7 @@ import org.ops4j.pax.exam.junit.PaxExam;
 import org.ops4j.pax.exam.spi.reactors.ExamReactorStrategy;
 import org.ops4j.pax.exam.spi.reactors.PerClass;
 
+import java.io.File;
 import java.lang.management.ManagementFactory;
 import java.util.Arrays;
 import java.util.LinkedList;
@@ -157,4 +158,70 @@ public class FeatureTest extends KarafTestSupport {
         mbeanServer.invoke(name, "refreshRepository", new Object[] { ".*pax-web.*" }, new String[]{ "java.lang.String" });
     }
 
+    @Test
+    public void configRegularLifecycle() throws Exception {
+        System.out.println(executeCommand("feature:install http", new RolePrincipal("admin")));
+        String output = executeCommand("config:exists org.ops4j.pax.web");
+        assertContains("true", output);
+        File jetty = new File(System.getProperty("karaf.etc"), "jetty.xml");
+        assertTrue("jetty.xml should exist", jetty.exists());
+
+        System.out.println(executeCommand("feature:uninstall http", new RolePrincipal("admin")));
+        output = executeCommand("config:exists org.ops4j.pax.web");
+        assertContains("true", output);
+        jetty = new File(System.getProperty("karaf.etc"), "jetty.xml");
+        assertTrue("jetty.xml should still exist", jetty.exists());
+    }
+
+    @Test
+    public void configDelete() throws Exception {
+        System.out.println(executeCommand("feature:install http", new RolePrincipal("admin")));
+        String output = executeCommand("config:exists org.ops4j.pax.web");
+        assertContains("true", output);
+        File jetty = new File(System.getProperty("karaf.etc"), "jetty.xml");
+        assertTrue("etc/jetty.xml should exist", jetty.exists());
+
+        System.out.println(executeCommand("feature:uninstall -c http", new RolePrincipal("admin")));
+        output = executeCommand("config:exists org.ops4j.pax.web");
+        assertContains("false", output);
+        jetty = new File(System.getProperty("karaf.etc"), "jetty.xml");
+        assertFalse("jetty.xml should not still exist", jetty.exists());
+    }
+
+    @Test
+    public void configRegularLifecycleViaMBean() throws Exception {
+        MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
+        ObjectName featureMBean = new ObjectName("org.apache.karaf:type=feature,name=root");
+        ObjectName configMBean = new ObjectName("org.apache.karaf:type=config,name=root");
+        mbeanServer.invoke(featureMBean, "installFeature", new Object[]{ "http" }, new String[]{ "java.lang.String" });
+
+        boolean exist = (boolean) mbeanServer.invoke(configMBean, "exists", new Object[]{ "org.ops4j.pax.web" }, new String[]{ "java.lang.String" });
+        assertTrue("true", exist);
+        File jetty = new File(System.getProperty("karaf.etc", "jetty.xml"));
+        assertTrue("jetty.xml should exist", jetty.exists());
+
+        mbeanServer.invoke(featureMBean, "uninstallFeature", new Object[]{ "http" }, new String[]{ "java.lang.String" });
+        exist = (boolean) mbeanServer.invoke(configMBean, "exists", new Object[]{ "org.ops4j.pax.web" }, new String[]{ "java.lang.String" });
+        assertTrue("true", exist);
+        jetty = new File(System.getProperty("karaf.etc", "jetty.xml"));
+        assertTrue("jetty.xml should exist", jetty.exists());
+    }
+
+    @Test
+    public void configDeleteViaMBean() throws Exception {
+        MBeanServer mbeanServer = ManagementFactory.getPlatformMBeanServer();
+        ObjectName featureMBean = new ObjectName("org.apache.karaf:type=feature,name=root");
+        ObjectName configMBean = new ObjectName("org.apache.karaf:type=config,name=root");
+        mbeanServer.invoke(featureMBean, "installFeature", new Object[]{ "http" }, new String[]{ "java.lang.String" });
+
+        boolean exist = (boolean) mbeanServer.invoke(configMBean, "exists", new Object[]{ "org.ops4j.pax.web" }, new String[]{ "java.lang.String" });
+        assertTrue("true", exist);
+        File jetty = new File(System.getProperty("karaf.etc", "jetty.xml"));
+        assertTrue("jetty.xml should exist", jetty.exists());
+
+        mbeanServer.invoke(featureMBean, "uninstallFeature", new Object[]{ "http", false, true }, new String[]{ "java.lang.String", "boolean", "boolean" });
+        exist = (boolean) mbeanServer.invoke(configMBean, "exists", new Object[]{ "org.ops4j.pax.web" }, new String[]{ "java.lang.String" });
+        assertFalse("false", exist);
+    }
+
 }
diff --git a/profile/src/main/java/org/apache/karaf/profile/assembly/AssemblyDeployCallback.java b/profile/src/main/java/org/apache/karaf/profile/assembly/AssemblyDeployCallback.java
index f478f33..6160c13 100644
--- a/profile/src/main/java/org/apache/karaf/profile/assembly/AssemblyDeployCallback.java
+++ b/profile/src/main/java/org/apache/karaf/profile/assembly/AssemblyDeployCallback.java
@@ -53,6 +53,7 @@ import org.apache.karaf.features.internal.util.MapUtils;
 import org.apache.karaf.util.maven.Parser;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
+import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.startlevel.BundleStartLevel;
 import org.osgi.framework.wiring.BundleRevision;
 import org.slf4j.Logger;
@@ -205,6 +206,11 @@ public class AssemblyDeployCallback extends StaticInstallSupport implements Depl
     }
 
     @Override
+    public void deleteConfigs(org.apache.karaf.features.Feature feature) throws IOException, InvalidSyntaxException {
+        // nothing to do
+    }
+
+    @Override
     public void installLibraries(org.apache.karaf.features.Feature feature) throws IOException {
         assertNotBlacklisted(feature);
         Downloader downloader = manager.createDownloader();
diff --git a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/VerifyMojo.java b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/VerifyMojo.java
index 32ac5ef..f13e0cb 100644
--- a/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/VerifyMojo.java
+++ b/tooling/karaf-maven-plugin/src/main/java/org/apache/karaf/tooling/VerifyMojo.java
@@ -102,6 +102,7 @@ import org.ops4j.pax.url.mvn.MavenResolvers;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
+import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.Version;
 import org.osgi.framework.namespace.IdentityNamespace;
 import org.osgi.framework.startlevel.BundleStartLevel;
@@ -822,7 +823,11 @@ public class VerifyMojo extends MojoSupport {
         @Override
         public void installConfigs(org.apache.karaf.features.Feature feature) {
         }
-        
+
+        @Override
+        public void deleteConfigs(org.apache.karaf.features.Feature feature) throws IOException, InvalidSyntaxException {
+        }
+
         @Override
         public void installLibraries(org.apache.karaf.features.Feature feature) {
         }