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 2014/11/24 08:18:19 UTC

[2/2] karaf-cellar git commit: [KARAF-2168] Cleanup and refactoring after the change on the sync property

[KARAF-2168] Cleanup and refactoring after the change on the sync property


Project: http://git-wip-us.apache.org/repos/asf/karaf-cellar/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf-cellar/commit/d16c37c7
Tree: http://git-wip-us.apache.org/repos/asf/karaf-cellar/tree/d16c37c7
Diff: http://git-wip-us.apache.org/repos/asf/karaf-cellar/diff/d16c37c7

Branch: refs/heads/master
Commit: d16c37c73e5b1ff64d416fbcc5655e5285b566d8
Parents: 732fc6e
Author: Jean-Baptiste Onofré <jb...@apache.org>
Authored: Mon Nov 24 08:17:48 2014 +0100
Committer: Jean-Baptiste Onofré <jb...@apache.org>
Committed: Mon Nov 24 08:17:48 2014 +0100

----------------------------------------------------------------------
 .../karaf/cellar/bundle/BundleEventHandler.java |   6 +-
 .../karaf/cellar/bundle/BundleSynchronizer.java |   4 +-
 .../cellar/bundle/LocalBundleListener.java      |   4 +-
 .../config/ConfigurationEventHandler.java       |   4 +-
 .../config/ConfigurationSynchronizer.java       |   4 +-
 .../config/LocalConfigurationListener.java      |   2 +-
 .../apache/karaf/cellar/core/CellarSupport.java |   2 +-
 .../control/ConsumerSwitchCommandHandler.java   |   2 +-
 .../control/ManageHandlersCommandHandler.java   |   2 +-
 .../control/ProducerSwitchCommandHandler.java   |   2 +-
 .../cellar/core/discovery/DiscoveryTask.java    |   2 +-
 .../karaf/cellar/core/CellarSupportTest.java    |   2 +-
 .../cellar/dosgi/RemoteServiceCallHandler.java  |   2 +-
 .../karaf/cellar/event/ClusterEventHandler.java |   4 +-
 .../karaf/cellar/event/LocalEventListener.java  |   2 +-
 .../karaf/cellar/features/FeatureInfo.java      |  67 --------
 .../karaf/cellar/features/FeatureState.java     |  79 ++++++++++
 .../cellar/features/FeaturesEventHandler.java   |   4 +-
 .../karaf/cellar/features/FeaturesSupport.java  |  25 ++-
 .../cellar/features/FeaturesSynchronizer.java   |  16 +-
 .../cellar/features/LocalFeaturesListener.java  |  23 +--
 .../internal/CellarFeaturesMBeanImpl.java       | 109 +++++++------
 .../features/shell/FeatureCommandSupport.java   | 153 -------------------
 .../features/shell/InstallFeatureCommand.java   | 105 +++++++++----
 .../features/shell/ListFeaturesCommand.java     |  97 ++++++++++++
 .../features/shell/ListGroupFeatures.java       |  96 ------------
 .../cellar/features/shell/RepoAddCommand.java   |  34 +++--
 .../cellar/features/shell/RepoListCommand.java  |  11 +-
 .../features/shell/RepoRemoveCommand.java       |  25 ++-
 .../features/shell/UninstallFeatureCommand.java |  90 +++++++----
 .../OSGI-INF/blueprint/shell-features.xml       |   4 +-
 .../cellar/hazelcast/HazelcastGroupManager.java |  16 +-
 .../karaf/cellar/hazelcast/QueueConsumer.java   |   2 +-
 .../karaf/cellar/hazelcast/QueueProducer.java   |   2 +-
 .../karaf/cellar/hazelcast/TopicConsumer.java   |   2 +-
 .../karaf/cellar/hazelcast/TopicProducer.java   |   2 +-
 .../karaf/cellar/obr/ObrBundleEventHandler.java |   4 +-
 .../karaf/cellar/obr/ObrUrlEventHandler.java    |   2 +-
 .../karaf/cellar/obr/ObrUrlSynchronizer.java    |   2 +-
 39 files changed, 483 insertions(+), 531 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleEventHandler.java
----------------------------------------------------------------------
diff --git a/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleEventHandler.java b/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleEventHandler.java
index 757ca3e..43a8c51 100644
--- a/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleEventHandler.java
+++ b/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleEventHandler.java
@@ -72,7 +72,7 @@ public class BundleEventHandler extends BundleSupport implements EventHandler<Cl
             	List<Feature> matchingFeatures = retrieveFeature(event.getLocation());
             	for (Feature feature : matchingFeatures) {
 					if (!isAllowed(event.getSourceGroup(), "features", feature.getName(), EventType.INBOUND)) {
-						LOGGER.debug("CELLAR BUNDLE: bundle {} is contained in feature {} marked BLOCKED INBOUND for cluster group {}", event.getLocation(), feature.getName(), event.getSourceGroup().getName());
+						LOGGER.trace("CELLAR BUNDLE: bundle {} is contained in feature {} marked BLOCKED INBOUND for cluster group {}", event.getLocation(), feature.getName(), event.getSourceGroup().getName());
 						return;
 					}
 				}
@@ -92,7 +92,7 @@ public class BundleEventHandler extends BundleSupport implements EventHandler<Cl
                     updateBundle(event.getSymbolicName(), event.getVersion());
                     LOGGER.debug("CELLAR BUNDLE: updating {}/{}", event.getSymbolicName(), event.getVersion());
                 }
-            } else LOGGER.debug("CELLAR BUNDLE: bundle {} is marked BLOCKED INBOUND for cluster group {}", event.getSymbolicName(), event.getSourceGroup().getName());
+            } else LOGGER.trace("CELLAR BUNDLE: bundle {} is marked BLOCKED INBOUND for cluster group {}", event.getSymbolicName(), event.getSourceGroup().getName());
         } catch (BundleException e) {
             LOGGER.error("CELLAR BUNDLE: failed to install bundle {}/{}.", new Object[]{event.getSymbolicName(), event.getVersion()}, e);
         } catch (Exception e) {
@@ -117,7 +117,7 @@ public class BundleEventHandler extends BundleSupport implements EventHandler<Cl
     public Switch getSwitch() {
         // load the switch status from the config
         try {
-            Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE);
+            Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE, null);
             if (configuration != null) {
                 Boolean status = new Boolean((String) configuration.getProperties().get(Configurations.HANDLER + "." + this.getClass().getName()));
                 if (status) {

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleSynchronizer.java
----------------------------------------------------------------------
diff --git a/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleSynchronizer.java b/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleSynchronizer.java
index 64533c3..68b586d 100644
--- a/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleSynchronizer.java
+++ b/bundle/src/main/java/org/apache/karaf/cellar/bundle/BundleSynchronizer.java
@@ -117,7 +117,7 @@ public class BundleSynchronizer extends BundleSupport implements Synchronizer {
                                 } catch (BundleException e) {
                                     LOGGER.error("CELLAR BUNDLE: failed to pull bundle {}", id, e);
                                 }
-                            } else LOGGER.debug("CELLAR BUNDLE: bundle {} is marked BLOCKED INBOUND for cluster group {}", bundleLocation, groupName);
+                            } else LOGGER.trace("CELLAR BUNDLE: bundle {} is marked BLOCKED INBOUND for cluster group {}", bundleLocation, groupName);
                         }
                     }
                 }
@@ -202,7 +202,7 @@ public class BundleSynchronizer extends BundleSupport implements Synchronizer {
                             eventProducer.produce(event);
                         }
 
-                    } else LOGGER.debug("CELLAR BUNDLE: bundle {} is marked BLOCKED OUTBOUND for cluster group {}", bundleLocation, groupName);
+                    } else LOGGER.trace("CELLAR BUNDLE: bundle {} is marked BLOCKED OUTBOUND for cluster group {}", bundleLocation, groupName);
                 }
             } finally {
                 Thread.currentThread().setContextClassLoader(originalClassLoader);

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/bundle/src/main/java/org/apache/karaf/cellar/bundle/LocalBundleListener.java
----------------------------------------------------------------------
diff --git a/bundle/src/main/java/org/apache/karaf/cellar/bundle/LocalBundleListener.java b/bundle/src/main/java/org/apache/karaf/cellar/bundle/LocalBundleListener.java
index 2713614..c099e3c 100644
--- a/bundle/src/main/java/org/apache/karaf/cellar/bundle/LocalBundleListener.java
+++ b/bundle/src/main/java/org/apache/karaf/cellar/bundle/LocalBundleListener.java
@@ -116,7 +116,7 @@ public class LocalBundleListener extends BundleSupport implements SynchronousBun
                         	List<Feature> matchingFeatures = retrieveFeature(bundleLocation);
                         	for (Feature feature : matchingFeatures) {
             					if (!isAllowed(group, "features", feature.getName(), EventType.OUTBOUND)) {
-            						LOGGER.debug("CELLAR BUNDLE: bundle {} is contained in feature {} marked BLOCKED OUTBOUND for cluster group {}", bundleLocation, feature.getName(), group.getName());
+            						LOGGER.trace("CELLAR BUNDLE: bundle {} is contained in feature {} marked BLOCKED OUTBOUND for cluster group {}", bundleLocation, feature.getName(), group.getName());
             						return;
             					}
             				}
@@ -131,7 +131,7 @@ public class LocalBundleListener extends BundleSupport implements SynchronousBun
                             Thread.currentThread().setContextClassLoader(originalClassLoader);
                         }
 
-                    } else LOGGER.debug("CELLAR BUNDLE: bundle {} is marked BLOCKED OUTBOUND for cluster group {}", bundleLocation, group.getName());
+                    } else LOGGER.trace("CELLAR BUNDLE: bundle {} is marked BLOCKED OUTBOUND for cluster group {}", bundleLocation, group.getName());
                 }
             }
         }

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/config/src/main/java/org/apache/karaf/cellar/config/ConfigurationEventHandler.java
----------------------------------------------------------------------
diff --git a/config/src/main/java/org/apache/karaf/cellar/config/ConfigurationEventHandler.java b/config/src/main/java/org/apache/karaf/cellar/config/ConfigurationEventHandler.java
index 6392504..9382414 100644
--- a/config/src/main/java/org/apache/karaf/cellar/config/ConfigurationEventHandler.java
+++ b/config/src/main/java/org/apache/karaf/cellar/config/ConfigurationEventHandler.java
@@ -97,7 +97,7 @@ public class ConfigurationEventHandler extends ConfigurationSupport implements E
             } catch (Exception ex) {
                 LOGGER.error("CELLAR CONFIG: failed to read cluster configuration", ex);
             }
-        } else LOGGER.debug("CELLAR CONFIG: configuration PID {} is marked BLOCKED INBOUND for cluster group {}", pid, groupName);
+        } else LOGGER.trace("CELLAR CONFIG: configuration PID {} is marked BLOCKED INBOUND for cluster group {}", pid, groupName);
     }
 
     public void init() {
@@ -117,7 +117,7 @@ public class ConfigurationEventHandler extends ConfigurationSupport implements E
     public Switch getSwitch() {
         // load the switch status from the config
         try {
-            Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE);
+            Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE, null);
             if (configuration != null) {
                 Boolean status = new Boolean((String) configuration.getProperties().get(Configurations.HANDLER + "." + this.getClass().getName()));
                 if (status) {

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/config/src/main/java/org/apache/karaf/cellar/config/ConfigurationSynchronizer.java
----------------------------------------------------------------------
diff --git a/config/src/main/java/org/apache/karaf/cellar/config/ConfigurationSynchronizer.java b/config/src/main/java/org/apache/karaf/cellar/config/ConfigurationSynchronizer.java
index e52abd7..6093d51 100644
--- a/config/src/main/java/org/apache/karaf/cellar/config/ConfigurationSynchronizer.java
+++ b/config/src/main/java/org/apache/karaf/cellar/config/ConfigurationSynchronizer.java
@@ -115,7 +115,7 @@ public class ConfigurationSynchronizer extends ConfigurationSupport implements S
                         } catch (IOException ex) {
                             LOGGER.error("CELLAR CONFIG: failed to read local configuration", ex);
                         }
-                    } else  LOGGER.debug("CELLAR CONFIG: configuration with PID {} is marked BLOCKED INBOUND for cluster group {}", clusterConfiguration, groupName);
+                    } else  LOGGER.trace("CELLAR CONFIG: configuration with PID {} is marked BLOCKED INBOUND for cluster group {}", clusterConfiguration, groupName);
                 }
             } finally {
                 Thread.currentThread().setContextClassLoader(originalClassLoader);
@@ -160,7 +160,7 @@ public class ConfigurationSynchronizer extends ConfigurationSupport implements S
                             event.setSourceGroup(group);
                             eventProducer.produce(event);
                         } else
-                            LOGGER.debug("CELLAR CONFIG: configuration with PID {} is marked BLOCKED OUTBOUND for cluster group {}", pid, groupName);
+                            LOGGER.trace("CELLAR CONFIG: configuration with PID {} is marked BLOCKED OUTBOUND for cluster group {}", pid, groupName);
                     }
                 } catch (IOException ex) {
                     LOGGER.error("CELLAR CONFIG: failed to read configuration (IO error)", ex);

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/config/src/main/java/org/apache/karaf/cellar/config/LocalConfigurationListener.java
----------------------------------------------------------------------
diff --git a/config/src/main/java/org/apache/karaf/cellar/config/LocalConfigurationListener.java b/config/src/main/java/org/apache/karaf/cellar/config/LocalConfigurationListener.java
index 047678e..8846e70 100644
--- a/config/src/main/java/org/apache/karaf/cellar/config/LocalConfigurationListener.java
+++ b/config/src/main/java/org/apache/karaf/cellar/config/LocalConfigurationListener.java
@@ -101,7 +101,7 @@ public class LocalConfigurationListener extends ConfigurationSupport implements
                     } catch (Exception e) {
                         LOGGER.error("CELLAR CONFIG: failed to update configuration with PID {} in the cluster group {}", pid, group.getName(), e);
                     }
-                } else LOGGER.debug("CELLAR CONFIG: configuration with PID {} is marked BLOCKED OUTBOUND for cluster group {}", pid, group.getName());
+                } else LOGGER.trace("CELLAR CONFIG: configuration with PID {} is marked BLOCKED OUTBOUND for cluster group {}", pid, group.getName());
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/core/src/main/java/org/apache/karaf/cellar/core/CellarSupport.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/karaf/cellar/core/CellarSupport.java b/core/src/main/java/org/apache/karaf/cellar/core/CellarSupport.java
index cbde2f4..a7cfa85 100644
--- a/core/src/main/java/org/apache/karaf/cellar/core/CellarSupport.java
+++ b/core/src/main/java/org/apache/karaf/cellar/core/CellarSupport.java
@@ -51,7 +51,7 @@ public class CellarSupport {
         Set<String> result = null;
         if (group != null) {
             try {
-                Configuration configuration = configurationAdmin.getConfiguration(Configurations.GROUP);
+                Configuration configuration = configurationAdmin.getConfiguration(Configurations.GROUP, null);
                 Dictionary<String, Object> dictionary = configuration.getProperties();
                 if (dictionary != null) {
                     String parent = (String) dictionary.get(group + Configurations.SEPARATOR + Configurations.PARENT);

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/core/src/main/java/org/apache/karaf/cellar/core/control/ConsumerSwitchCommandHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/karaf/cellar/core/control/ConsumerSwitchCommandHandler.java b/core/src/main/java/org/apache/karaf/cellar/core/control/ConsumerSwitchCommandHandler.java
index 828db15..920c331 100644
--- a/core/src/main/java/org/apache/karaf/cellar/core/control/ConsumerSwitchCommandHandler.java
+++ b/core/src/main/java/org/apache/karaf/cellar/core/control/ConsumerSwitchCommandHandler.java
@@ -63,7 +63,7 @@ public class ConsumerSwitchCommandHandler extends CommandHandler<ConsumerSwitchC
      */
     private void persist(SwitchStatus switchStatus) {
         try {
-            Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE);
+            Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE, null);
             if (configuration != null) {
                 Dictionary<String, Object> properties = configuration.getProperties();
                 if (properties != null) {

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/core/src/main/java/org/apache/karaf/cellar/core/control/ManageHandlersCommandHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/karaf/cellar/core/control/ManageHandlersCommandHandler.java b/core/src/main/java/org/apache/karaf/cellar/core/control/ManageHandlersCommandHandler.java
index 02b4c3b..a908057 100644
--- a/core/src/main/java/org/apache/karaf/cellar/core/control/ManageHandlersCommandHandler.java
+++ b/core/src/main/java/org/apache/karaf/cellar/core/control/ManageHandlersCommandHandler.java
@@ -104,7 +104,7 @@ public class ManageHandlersCommandHandler extends CommandHandler<ManageHandlersC
      */
     private void persist(String handler, SwitchStatus switchStatus) {
         try {
-            Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE);
+            Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE, null);
             if (configuration != null) {
                 Dictionary<String, Object> properties = configuration.getProperties();
                 if (properties != null) {

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/core/src/main/java/org/apache/karaf/cellar/core/control/ProducerSwitchCommandHandler.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/karaf/cellar/core/control/ProducerSwitchCommandHandler.java b/core/src/main/java/org/apache/karaf/cellar/core/control/ProducerSwitchCommandHandler.java
index 39e8b08..6902b11 100644
--- a/core/src/main/java/org/apache/karaf/cellar/core/control/ProducerSwitchCommandHandler.java
+++ b/core/src/main/java/org/apache/karaf/cellar/core/control/ProducerSwitchCommandHandler.java
@@ -63,7 +63,7 @@ public class ProducerSwitchCommandHandler extends CommandHandler<ProducerSwitchC
      */
     private void persist(SwitchStatus switchStatus) {
         try {
-            Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE);
+            Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE, null);
             if (configuration != null) {
                 Dictionary<String, Object> properties = configuration.getProperties();
                 if (properties != null) {

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/core/src/main/java/org/apache/karaf/cellar/core/discovery/DiscoveryTask.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/org/apache/karaf/cellar/core/discovery/DiscoveryTask.java b/core/src/main/java/org/apache/karaf/cellar/core/discovery/DiscoveryTask.java
index 7e185cc..c3f0b41 100644
--- a/core/src/main/java/org/apache/karaf/cellar/core/discovery/DiscoveryTask.java
+++ b/core/src/main/java/org/apache/karaf/cellar/core/discovery/DiscoveryTask.java
@@ -62,7 +62,7 @@ public class DiscoveryTask implements Runnable {
                 }
                 try {
                 	LOGGER.trace("CELLAR DISCOVERY: retrieving configuration for PID={}", Discovery.PID);
-                    Configuration configuration = configurationAdmin.getConfiguration(Discovery.PID);
+                    Configuration configuration = configurationAdmin.getConfiguration(Discovery.PID, null);
                     Dictionary properties = configuration.getProperties();
                     if (properties == null) {
                     	// this is a new configuration ...

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/core/src/test/java/org/apache/karaf/cellar/core/CellarSupportTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/org/apache/karaf/cellar/core/CellarSupportTest.java b/core/src/test/java/org/apache/karaf/cellar/core/CellarSupportTest.java
index f8e7681..80d85b4 100644
--- a/core/src/test/java/org/apache/karaf/cellar/core/CellarSupportTest.java
+++ b/core/src/test/java/org/apache/karaf/cellar/core/CellarSupportTest.java
@@ -44,7 +44,7 @@ public class CellarSupportTest {
         InputStream is = getClass().getResourceAsStream("groups.properties");
         props.load(is);
         is.close();
-        expect(configurationAdmin.getConfiguration(EasyMock.<String>anyObject())).andReturn(configuration).anyTimes();
+        expect(configurationAdmin.getConfiguration(EasyMock.<String>anyObject(), EasyMock.anyString())).andReturn(configuration).anyTimes();
         Dictionary propsDictionary = (Dictionary) props;
         expect(configuration.getProperties()).andReturn(propsDictionary).anyTimes();
         replay(configuration);

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/dosgi/src/main/java/org/apache/karaf/cellar/dosgi/RemoteServiceCallHandler.java
----------------------------------------------------------------------
diff --git a/dosgi/src/main/java/org/apache/karaf/cellar/dosgi/RemoteServiceCallHandler.java b/dosgi/src/main/java/org/apache/karaf/cellar/dosgi/RemoteServiceCallHandler.java
index 67f8d04..dc0f37e 100644
--- a/dosgi/src/main/java/org/apache/karaf/cellar/dosgi/RemoteServiceCallHandler.java
+++ b/dosgi/src/main/java/org/apache/karaf/cellar/dosgi/RemoteServiceCallHandler.java
@@ -129,7 +129,7 @@ public class RemoteServiceCallHandler extends CellarSupport implements EventHand
     public Switch getSwitch() {
         // load the switch status from the config
         try {
-            Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE);
+            Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE, null);
             if (configuration != null) {
                 Boolean status = new Boolean((String) configuration.getProperties().get(Configurations.HANDLER + "." + this.getClass().getName()));
                 if (status) {

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/event/src/main/java/org/apache/karaf/cellar/event/ClusterEventHandler.java
----------------------------------------------------------------------
diff --git a/event/src/main/java/org/apache/karaf/cellar/event/ClusterEventHandler.java b/event/src/main/java/org/apache/karaf/cellar/event/ClusterEventHandler.java
index e0b9f12..0013115 100644
--- a/event/src/main/java/org/apache/karaf/cellar/event/ClusterEventHandler.java
+++ b/event/src/main/java/org/apache/karaf/cellar/event/ClusterEventHandler.java
@@ -64,7 +64,7 @@ public class ClusterEventHandler extends EventSupport implements EventHandler<Cl
                 properties.put(Constants.EVENT_SOURCE_GROUP_KEY, event.getSourceGroup());
                 properties.put(Constants.EVENT_SOURCE_NODE_KEY, event.getSourceNode());
                 postEvent(event.getTopicName(), properties);
-            } else LOGGER.debug("CELLAR EVENT: event {} is marked BLOCKED INBOUND for cluster group {}", event.getTopicName(), event.getSourceGroup().getName());
+            } else LOGGER.trace("CELLAR EVENT: event {} is marked BLOCKED INBOUND for cluster group {}", event.getTopicName(), event.getSourceGroup().getName());
         } catch (Exception e) {
             LOGGER.error("CELLAR EVENT: failed to handle event", e);
         }
@@ -87,7 +87,7 @@ public class ClusterEventHandler extends EventSupport implements EventHandler<Cl
     public Switch getSwitch() {
         // load the switch status from the config
         try {
-            Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE);
+            Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE, null);
             if (configuration != null) {
                 Boolean status = new Boolean((String) configuration.getProperties().get(Configurations.HANDLER + "." + this.getClass().getName()));
                 if (status) {

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/event/src/main/java/org/apache/karaf/cellar/event/LocalEventListener.java
----------------------------------------------------------------------
diff --git a/event/src/main/java/org/apache/karaf/cellar/event/LocalEventListener.java b/event/src/main/java/org/apache/karaf/cellar/event/LocalEventListener.java
index 8550b9d..41fec10 100644
--- a/event/src/main/java/org/apache/karaf/cellar/event/LocalEventListener.java
+++ b/event/src/main/java/org/apache/karaf/cellar/event/LocalEventListener.java
@@ -80,7 +80,7 @@ public class LocalEventListener extends EventSupport implements EventHandler {
                             ClusterEvent clusterEvent = new ClusterEvent(topicName, properties);
                             clusterEvent.setSourceGroup(group);
                             eventProducer.produce(clusterEvent);
-                        } else LOGGER.debug("CELLAR EVENT: event {} is marked as BLOCKED OUTBOUND for cluster group {}", topicName, group.getName());
+                        } else LOGGER.trace("CELLAR EVENT: event {} is marked as BLOCKED OUTBOUND for cluster group {}", topicName, group.getName());
                     }
                 }
             }

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/features/src/main/java/org/apache/karaf/cellar/features/FeatureInfo.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/cellar/features/FeatureInfo.java b/features/src/main/java/org/apache/karaf/cellar/features/FeatureInfo.java
deleted file mode 100644
index 1f9277f..0000000
--- a/features/src/main/java/org/apache/karaf/cellar/features/FeatureInfo.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Licensed 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.cellar.features;
-
-import java.io.Serializable;
-
-/**
- * Feature info to store in the cluster.
- */
-public class FeatureInfo implements Serializable {
-
-    private String name;
-    private String version;
-
-    public FeatureInfo(String name, String version) {
-        this.name = name;
-        this.version = version;
-    }
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public String getVersion() {
-        return version;
-    }
-
-    public void setVersion(String version) {
-        this.version = version;
-    }
-
-    @Override
-    public boolean equals(Object o) {
-        if (this == o) return true;
-        if (o == null || getClass() != o.getClass()) return false;
-
-        FeatureInfo info = (FeatureInfo) o;
-
-        if (name != null ? !name.equals(info.name) : info.name != null) return false;
-        if (version != null ? !version.equals(info.version) : info.version != null) return false;
-
-        return true;
-    }
-
-    @Override
-    public int hashCode() {
-        int result = name != null ? name.hashCode() : 0;
-        result = 31 * result + (version != null ? version.hashCode() : 0);
-        return result;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/features/src/main/java/org/apache/karaf/cellar/features/FeatureState.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/cellar/features/FeatureState.java b/features/src/main/java/org/apache/karaf/cellar/features/FeatureState.java
new file mode 100644
index 0000000..c993191
--- /dev/null
+++ b/features/src/main/java/org/apache/karaf/cellar/features/FeatureState.java
@@ -0,0 +1,79 @@
+/*
+ * Licensed 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.cellar.features;
+
+import java.io.Serializable;
+
+/**
+ * Feature info to store in the cluster.
+ */
+public class FeatureState implements Serializable {
+
+    private String name;
+    private String version;
+    private boolean installed;
+
+    public FeatureState() { }
+
+    public FeatureState(String name, String version, boolean installed) {
+        this.name = name;
+        this.version = version;
+        this.installed = installed;
+    }
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getVersion() {
+        return version;
+    }
+
+    public void setVersion(String version) {
+        this.version = version;
+    }
+
+    public boolean isInstalled() {
+        return installed;
+    }
+
+    public void setInstalled(boolean installed) {
+        this.installed = installed;
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (o == null || getClass() != o.getClass()) return false;
+
+        FeatureState info = (FeatureState) o;
+
+        if (name != null ? !name.equals(info.name) : info.name != null) return false;
+        if (version != null ? !version.equals(info.version) : info.version != null) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = name != null ? name.hashCode() : 0;
+        result = 31 * result + (version != null ? version.hashCode() : 0);
+        return result;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/features/src/main/java/org/apache/karaf/cellar/features/FeaturesEventHandler.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/cellar/features/FeaturesEventHandler.java b/features/src/main/java/org/apache/karaf/cellar/features/FeaturesEventHandler.java
index b49b8dd..0cd1644 100644
--- a/features/src/main/java/org/apache/karaf/cellar/features/FeaturesEventHandler.java
+++ b/features/src/main/java/org/apache/karaf/cellar/features/FeaturesEventHandler.java
@@ -116,7 +116,7 @@ public class FeaturesEventHandler extends FeaturesSupport implements EventHandle
             } catch (Exception e) {
                 LOGGER.error("CELLAR FEATURES: failed to handle cluster feature event", e);
             }
-        } else LOGGER.debug("CELLAR FEATURES: feature {} is marked BLOCKED INBOUND for cluster group {}", name, event.getSourceGroup().getName());
+        } else LOGGER.trace("CELLAR FEATURES: feature {} is marked BLOCKED INBOUND for cluster group {}", name, event.getSourceGroup().getName());
     }
 
     /**
@@ -138,7 +138,7 @@ public class FeaturesEventHandler extends FeaturesSupport implements EventHandle
     public Switch getSwitch() {
         // load the switch status from the config
         try {
-            Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE);
+            Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE, null);
             if (configuration != null) {
                 Boolean status = new Boolean((String) configuration.getProperties().get(Configurations.HANDLER + "." + this.getClass().getName()));
                 if (status) {

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/features/src/main/java/org/apache/karaf/cellar/features/FeaturesSupport.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/cellar/features/FeaturesSupport.java b/features/src/main/java/org/apache/karaf/cellar/features/FeaturesSupport.java
index fd4edc7..042c662 100644
--- a/features/src/main/java/org/apache/karaf/cellar/features/FeaturesSupport.java
+++ b/features/src/main/java/org/apache/karaf/cellar/features/FeaturesSupport.java
@@ -90,15 +90,14 @@ public class FeaturesSupport extends CellarSupport {
     public void pushFeature(Feature feature, Group group) {
         if (feature != null) {
             String groupName = group.getName();
-            Map<FeatureInfo, Boolean> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
+            Map<String, FeatureState> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
 
             if (isAllowed(group, Constants.CATEGORY, feature.getName(), EventType.OUTBOUND)) {
                 if (featuresService != null && clusterFeatures != null) {
-                    FeatureInfo info = new FeatureInfo(feature.getName(), feature.getVersion());
-                    Boolean installed = featuresService.isInstalled(feature);
-                    clusterFeatures.put(info, installed);
+                    FeatureState state = new FeatureState(feature.getName(), feature.getVersion(), featuresService.isInstalled(feature));
+                    clusterFeatures.put(feature.getName() + "/" + feature.getVersion() , state);
                 }
-            } else LOGGER.debug("CELLAR FEATURES: feature {} is marked BLOCKED OUTBOUND for cluster group {}", feature.getName(), groupName);
+            } else LOGGER.trace("CELLAR FEATURES: feature {} is marked BLOCKED OUTBOUND for cluster group {}", feature.getName(), groupName);
         } else LOGGER.warn("CELLAR FEATURES: feature is null");
     }
 
@@ -113,14 +112,14 @@ public class FeaturesSupport extends CellarSupport {
     public void pushFeature(Feature feature, Group group, Boolean force) {
         if (feature != null) {
             String groupName = group.getName();
-            Map<FeatureInfo, Boolean> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
+            Map<String, FeatureState> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
 
             if (isAllowed(group, Constants.CATEGORY, feature.getName(), EventType.OUTBOUND)) {
                 if (featuresService != null && clusterFeatures != null) {
-                    FeatureInfo info = new FeatureInfo(feature.getName(), feature.getVersion());
-                    clusterFeatures.put(info, force);
+                    FeatureState state = new FeatureState(feature.getName(), feature.getVersion(), force);
+                    clusterFeatures.put(feature.getName() + "/" + feature.getVersion(), state);
                 }
-            } else LOGGER.debug("CELLAR FEATURES: feature {} is marked BLOCKED OUTBOUND for cluster group {}", feature.getName(), groupName);
+            } else LOGGER.trace("CELLAR FEATURES: feature {} is marked BLOCKED OUTBOUND for cluster group {}", feature.getName(), groupName);
         } else LOGGER.warn("CELLAR FEATURES: feature is null");
     }
 
@@ -132,10 +131,10 @@ public class FeaturesSupport extends CellarSupport {
      */
     public void pushRepository(Repository repository, Group group) {
         String groupName = group.getName();
-        List<String> clusterRepositories = clusterManager.getList(Constants.REPOSITORIES_MAP + Configurations.SEPARATOR + groupName);
+        Map<String, String> clusterRepositories = clusterManager.getMap(Constants.REPOSITORIES_MAP + Configurations.SEPARATOR + groupName);
 
         boolean found = false;
-        for (String clusterRepository : clusterRepositories) {
+        for (String clusterRepository : clusterRepositories.keySet()) {
             if (clusterRepository.equals(repository.getURI().toString())) {
                 found = true;
                 break;
@@ -143,7 +142,7 @@ public class FeaturesSupport extends CellarSupport {
         }
 
         if (!found) {
-            clusterRepositories.add(repository.getURI().toString());
+            clusterRepositories.put(repository.getURI().toString(), repository.getName());
         }
     }
 
@@ -155,7 +154,7 @@ public class FeaturesSupport extends CellarSupport {
      */
     public void removeRepository(Repository repository, Group group) {
         String groupName = group.getName();
-        List<String> clusterRepositories = clusterManager.getList(Constants.REPOSITORIES_MAP + Configurations.SEPARATOR + groupName);
+        Map<String, String> clusterRepositories = clusterManager.getMap(Constants.REPOSITORIES_MAP + Configurations.SEPARATOR + groupName);
 
         if (featuresService != null && clusterRepositories != null) {
             URI uri = repository.getURI();

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/features/src/main/java/org/apache/karaf/cellar/features/FeaturesSynchronizer.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/cellar/features/FeaturesSynchronizer.java b/features/src/main/java/org/apache/karaf/cellar/features/FeaturesSynchronizer.java
index e7a1bcc..a96a3e2 100644
--- a/features/src/main/java/org/apache/karaf/cellar/features/FeaturesSynchronizer.java
+++ b/features/src/main/java/org/apache/karaf/cellar/features/FeaturesSynchronizer.java
@@ -87,15 +87,15 @@ public class FeaturesSynchronizer extends FeaturesSupport implements Synchronize
         if (group != null) {
             String groupName = group.getName();
             LOGGER.debug("CELLAR FEATURES: pulling features repositories and features from cluster group {}", groupName);
-            List<String> clusterRepositories = clusterManager.getList(Constants.REPOSITORIES_MAP + Configurations.SEPARATOR + groupName);
-            Map<FeatureInfo, Boolean> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
+            Map<String, String> clusterRepositories = clusterManager.getMap(Constants.REPOSITORIES_MAP + Configurations.SEPARATOR + groupName);
+            Map<String, FeatureState> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
             clusterManager.getList(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
             ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
             try {
                 Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
                 // get the features repositories URLs from the cluster group
                 if (clusterRepositories != null && !clusterRepositories.isEmpty()) {
-                    for (String url : clusterRepositories) {
+                    for (String url : clusterRepositories.keySet()) {
                         try {
                             if (!isRepositoryRegisteredLocally(url)) {
                                 LOGGER.debug("CELLAR FEATURES: adding new features repository {}", url);
@@ -111,11 +111,11 @@ public class FeaturesSynchronizer extends FeaturesSupport implements Synchronize
 
                 // get the features from the cluster group
                 if (clusterFeatures != null && !clusterFeatures.isEmpty()) {
-                    for (FeatureInfo info : clusterFeatures.keySet()) {
+                    for (FeatureState info : clusterFeatures.values()) {
                         String name = info.getName();
                         // check if feature is blocked
                         if (isAllowed(group, Constants.CATEGORY, name, EventType.INBOUND)) {
-                            Boolean remotelyInstalled = clusterFeatures.get(info);
+                            Boolean remotelyInstalled = info.isInstalled();
                             Boolean locallyInstalled = isFeatureInstalledLocally(info.getName(), info.getVersion());
 
                             // prevent NPE
@@ -143,7 +143,7 @@ public class FeaturesSynchronizer extends FeaturesSupport implements Synchronize
                                     LOGGER.error("CELLAR FEATURES: failed to uninstall feature {}/{} ", new Object[]{info.getName(), info.getVersion()}, e);
                                 }
                             }
-                        } else LOGGER.debug("CELLAR FEATURES: feature {} is marked BLOCKED INBOUND for cluster group {}", name, groupName);
+                        } else LOGGER.trace("CELLAR FEATURES: feature {} is marked BLOCKED INBOUND for cluster group {}", name, groupName);
                     }
                 }
             } finally {
@@ -162,12 +162,10 @@ public class FeaturesSynchronizer extends FeaturesSupport implements Synchronize
         if (group != null) {
             String groupName = group.getName();
             LOGGER.debug("CELLAR FEATURES: pushing features repositories and features in cluster group {}", groupName);
-            clusterManager.getList(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
 
             ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
+            Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
             try {
-                Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
-
                 Repository[] repositoryList = new Repository[0];
                 Feature[] featuresList = new Feature[0];
 

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/features/src/main/java/org/apache/karaf/cellar/features/LocalFeaturesListener.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/cellar/features/LocalFeaturesListener.java b/features/src/main/java/org/apache/karaf/cellar/features/LocalFeaturesListener.java
index f0811a5..70c938b 100644
--- a/features/src/main/java/org/apache/karaf/cellar/features/LocalFeaturesListener.java
+++ b/features/src/main/java/org/apache/karaf/cellar/features/LocalFeaturesListener.java
@@ -91,7 +91,7 @@ public class LocalFeaturesListener extends FeaturesSupport implements org.apache
                         ClusterFeaturesEvent featureEvent = new ClusterFeaturesEvent(name, version, type);
                         featureEvent.setSourceGroup(group);
                         eventProducer.produce(featureEvent);
-                    } else LOGGER.debug("CELLAR FEATURES: feature {} is marked BLOCKED OUTBOUND for cluster group {}", name, group.getName());
+                    } else LOGGER.trace("CELLAR FEATURES: feature {} is marked BLOCKED OUTBOUND for cluster group {}", name, group.getName());
                 }
             }
         }
@@ -132,20 +132,21 @@ public class LocalFeaturesListener extends FeaturesSupport implements org.apache
                         if (RepositoryEvent.EventType.RepositoryAdded.equals(type)) {
                             pushRepository(event.getRepository(), group);
                             // update the features in the cluster group
-                            Map<FeatureInfo, Boolean> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + group.getName());
+                            Map<String, FeatureState> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + group.getName());
                             try {
                                 for (Feature feature : event.getRepository().getFeatures()) {
                                     // check the feature in the distributed map
-                                    FeatureInfo featureInfo = null;
-                                    for (FeatureInfo clusterFeature : clusterFeatures.keySet()) {
+                                    FeatureState featureState = null;
+                                    for (String k : clusterFeatures.keySet()) {
+                                        FeatureState clusterFeature = clusterFeatures.get(k);
                                         if (clusterFeature.getName().equals(feature.getName()) && clusterFeature.getVersion().equals(feature.getVersion())) {
-                                            featureInfo = clusterFeature;
+                                            featureState = clusterFeature;
                                             break;
                                         }
                                     }
-                                    if (featureInfo == null) {
-                                        featureInfo = new FeatureInfo(feature.getName(), feature.getVersion());
-                                        clusterFeatures.put(featureInfo, false);
+                                    if (featureState == null) {
+                                        featureState = new FeatureState(feature.getName(), feature.getVersion(), featuresService.isInstalled(feature));
+                                        clusterFeatures.put(feature.getName() + "/" + feature.getVersion(), featureState);
                                     }
                                 }
                             } catch (Exception e) {
@@ -154,11 +155,11 @@ public class LocalFeaturesListener extends FeaturesSupport implements org.apache
                         } else {
                             removeRepository(event.getRepository(), group);
                             // update the features in the cluster group
-                            Map<FeatureInfo, Boolean> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + group.getName());
+                            Map<String, FeatureState> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + group.getName());
                             try {
                                 for (Feature feature : event.getRepository().getFeatures()) {
-                                    FeatureInfo info = new FeatureInfo(feature.getName(), feature.getVersion());
-                                    clusterFeatures.remove(info);
+                                    FeatureState info = new FeatureState(feature.getName(), feature.getVersion(), featuresService.isInstalled(feature));
+                                    clusterFeatures.remove(feature.getName() + "/" + feature.getVersion());
                                 }
                             } catch (Exception e) {
                                 LOGGER.warn("CELLAR FEATURES: failed to update the cluster group", e);

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/features/src/main/java/org/apache/karaf/cellar/features/management/internal/CellarFeaturesMBeanImpl.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/cellar/features/management/internal/CellarFeaturesMBeanImpl.java b/features/src/main/java/org/apache/karaf/cellar/features/management/internal/CellarFeaturesMBeanImpl.java
index 434d5bf..7aebf5b 100644
--- a/features/src/main/java/org/apache/karaf/cellar/features/management/internal/CellarFeaturesMBeanImpl.java
+++ b/features/src/main/java/org/apache/karaf/cellar/features/management/internal/CellarFeaturesMBeanImpl.java
@@ -20,7 +20,7 @@ import org.apache.karaf.cellar.core.event.EventProducer;
 import org.apache.karaf.cellar.core.event.EventType;
 import org.apache.karaf.cellar.features.ClusterFeaturesEvent;
 import org.apache.karaf.cellar.features.Constants;
-import org.apache.karaf.cellar.features.FeatureInfo;
+import org.apache.karaf.cellar.features.FeatureState;
 import org.apache.karaf.cellar.features.ClusterRepositoryEvent;
 import org.apache.karaf.cellar.features.management.CellarFeaturesMBean;
 import org.apache.karaf.features.*;
@@ -118,23 +118,26 @@ public class CellarFeaturesMBeanImpl extends StandardMBean implements CellarFeat
         }
 
         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
+        Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
         try {
-            Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
 
             // get the features in the cluster group
-            Map<FeatureInfo, Boolean> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
+            Map<String, FeatureState> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
 
             // check if the feature exist
-            FeatureInfo feature = null;
-            for (FeatureInfo info : clusterFeatures.keySet()) {
+            FeatureState feature = null;
+            String key = null;
+            for (String k: clusterFeatures.keySet()) {
+                FeatureState state = clusterFeatures.get(k);
+                key = k;
                 if (version == null) {
-                    if (info.getName().equals(name)) {
-                        feature = info;
+                    if (state.getName().equals(name)) {
+                        feature = state;
                         break;
                     }
                 } else {
-                    if (info.getName().equals(name) && info.getVersion().equals(version)) {
-                        feature = info;
+                    if (state.getName().equals(name) && state.getVersion().equals(version)) {
+                        feature = state;
                         break;
                     }
                 }
@@ -148,22 +151,8 @@ public class CellarFeaturesMBeanImpl extends StandardMBean implements CellarFeat
             }
 
             // update the cluster group
-            clusterFeatures.put(feature, true);
-            try {
-                // TODO does it make sense ?
-                /*
-                List<BundleInfo> bundles = featuresService.getFeature(feature.getName(), version).getBundles();
-                Map<String, BundleState> clusterBundles = clusterManager.getMap(org.apache.karaf.cellar.bundle.Constants.BUNDLE_MAP + Configurations.SEPARATOR + groupName);
-                for (BundleInfo bundle : bundles) {
-                    BundleState state = new BundleState();
-                    state.setLocation(bundle.getLocation());
-                    state.setStatus(BundleEvent.STARTED);
-                    clusterBundles.put(bundle.toString(), state);
-                }
-                */
-            } catch (Exception e) {
-                // ignore
-            }
+            feature.setInstalled(true);
+            clusterFeatures.put(key, feature);
         } finally {
             Thread.currentThread().setContextClassLoader(originalClassLoader);
         }
@@ -222,23 +211,26 @@ public class CellarFeaturesMBeanImpl extends StandardMBean implements CellarFeat
         }
 
         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
+        Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
         try {
-            Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
 
             // get the features in the cluster group
-            Map<FeatureInfo, Boolean> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
+            Map<String, FeatureState> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
 
             // check if the feature exist
-            FeatureInfo feature = null;
-            for (FeatureInfo info : clusterFeatures.keySet()) {
+            FeatureState feature = null;
+            String key = null;
+            for (String k : clusterFeatures.keySet()) {
+                FeatureState state = clusterFeatures.get(k);
+                key = k;
                 if (version == null) {
-                    if (info.getName().equals(name)) {
-                        feature = info;
+                    if (state.getName().equals(name)) {
+                        feature = state;
                         break;
                     }
                 } else {
-                    if (info.getName().equals(name) && info.getVersion().equals(version)) {
-                        feature = info;
+                    if (state.getName().equals(name) && state.getVersion().equals(version)) {
+                        feature = state;
                         break;
                     }
                 }
@@ -252,7 +244,8 @@ public class CellarFeaturesMBeanImpl extends StandardMBean implements CellarFeat
             }
 
             // update the cluster group
-            clusterFeatures.put(feature, false);
+            feature.setInstalled(false);
+            clusterFeatures.put(key, feature);
         } finally {
             Thread.currentThread().setContextClassLoader(originalClassLoader);
         }
@@ -287,13 +280,12 @@ public class CellarFeaturesMBeanImpl extends StandardMBean implements CellarFeat
         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
         Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
         try {
-            Map<FeatureInfo, Boolean> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + group);
+            Map<String, FeatureState> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + group);
             if (clusterFeatures != null && !clusterFeatures.isEmpty()) {
-                for (FeatureInfo feature : clusterFeatures.keySet()) {
-                    boolean installed = clusterFeatures.get(feature);
+                for (FeatureState feature : clusterFeatures.values()) {
                     CompositeData data = new CompositeDataSupport(featuresType,
                             new String[]{"name", "version", "installed"},
-                            new Object[]{feature.getName(), feature.getVersion(), installed});
+                            new Object[]{feature.getName(), feature.getVersion(), feature.isInstalled()});
                     table.put(data);
                 }
             }
@@ -313,10 +305,10 @@ public class CellarFeaturesMBeanImpl extends StandardMBean implements CellarFeat
         }
 
         // get the features repositories in the cluster group
-        List<String> clusterRepositories = clusterManager.getList(Constants.REPOSITORIES_MAP + Configurations.SEPARATOR + groupName);
+        Map<String, String> clusterRepositories = clusterManager.getMap(Constants.REPOSITORIES_MAP + Configurations.SEPARATOR + groupName);
 
         List<String> result = new ArrayList<String>();
-        for (String clusterRepository : clusterRepositories) {
+        for (String clusterRepository : clusterRepositories.keySet()) {
             result.add(clusterRepository);
         }
 
@@ -342,22 +334,22 @@ public class CellarFeaturesMBeanImpl extends StandardMBean implements CellarFeat
         }
 
         ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
+        Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
         try {
-            Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
             // get the features repositories in the cluster group
-            List<String> clusterRepositories = clusterManager.getList(Constants.REPOSITORIES_MAP + Configurations.SEPARATOR + groupName);
+            Map<String, String> clusterRepositories = clusterManager.getMap(Constants.REPOSITORIES_MAP + Configurations.SEPARATOR + groupName);
             // get the features in the cluster group
-            Map<FeatureInfo, Boolean> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
+            Map<String, FeatureState> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
 
             // check if the URL is already registered
-            boolean found = false;
-            for (String repository : clusterRepositories) {
+            String name = null;
+            for (String repository : clusterRepositories.keySet()) {
                 if (repository.equals(url)) {
-                    found = true;
+                    name = clusterRepositories.get(repository);
                     break;
                 }
             }
-            if (!found) {
+            if (name == null) {
                 // update the repository temporary locally
                 Repository repository = null;
                 boolean localRegistered = false;
@@ -382,16 +374,17 @@ public class CellarFeaturesMBeanImpl extends StandardMBean implements CellarFeat
                             break;
                         }
                     }
+                    name = repository.getName();
                 } else {
                     localRegistered = true;
                 }
 
                 // update the cluster group
-                clusterRepositories.add(url);
+                clusterRepositories.put(url, name);
 
                 for (Feature feature : repository.getFeatures()) {
-                    FeatureInfo info = new FeatureInfo(feature.getName(), feature.getVersion());
-                    clusterFeatures.put(info, false);
+                    FeatureState state = new FeatureState(feature.getName(), feature.getVersion(), featuresService.isInstalled(feature));
+                    clusterFeatures.put(feature.getName() + "/" + feature.getVersion(), state);
                 }
 
                 // un-register the repository if it's not local registered
@@ -430,19 +423,19 @@ public class CellarFeaturesMBeanImpl extends StandardMBean implements CellarFeat
         }
 
         // get the features repositories in the cluster group
-        List<String> clusterRepositories = clusterManager.getList(Constants.REPOSITORIES_MAP + Configurations.SEPARATOR + groupName);
+        Map<String, String> clusterRepositories = clusterManager.getMap(Constants.REPOSITORIES_MAP + Configurations.SEPARATOR + groupName);
         // get the features in the cluster group
-        Map<FeatureInfo, Boolean> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
+        Map<FeatureState, Boolean> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
 
         // looking for the URL in the list
-        boolean found = false;
-        for (String clusterRepository : clusterRepositories) {
+        String name = null;
+        for (String clusterRepository : clusterRepositories.keySet()) {
             if (clusterRepository.equals(url)) {
-                found = true;
+                name = clusterRepositories.get(clusterRepository);
                 break;
             }
         }
-        if (found) {
+        if (name == null) {
             // update the repository temporary locally
             Repository repository = null;
             boolean localRegistered = false;
@@ -475,8 +468,8 @@ public class CellarFeaturesMBeanImpl extends StandardMBean implements CellarFeat
             clusterRepositories.remove(url);
 
             for (Feature feature : repository.getFeatures()) {
-                FeatureInfo info = new FeatureInfo(feature.getName(), feature.getVersion());
-                clusterFeatures.remove(info);
+                FeatureState state = new FeatureState(feature.getName(), feature.getVersion(), featuresService.isInstalled(feature));
+                clusterFeatures.remove(state);
             }
 
             // un-register the repository if it's not local registered

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/features/src/main/java/org/apache/karaf/cellar/features/shell/FeatureCommandSupport.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/cellar/features/shell/FeatureCommandSupport.java b/features/src/main/java/org/apache/karaf/cellar/features/shell/FeatureCommandSupport.java
deleted file mode 100644
index a9400fb..0000000
--- a/features/src/main/java/org/apache/karaf/cellar/features/shell/FeatureCommandSupport.java
+++ /dev/null
@@ -1,153 +0,0 @@
-/*
- * Licensed 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.cellar.features.shell;
-
-import org.apache.karaf.cellar.core.CellarSupport;
-import org.apache.karaf.cellar.core.Configurations;
-import org.apache.karaf.cellar.core.Group;
-import org.apache.karaf.cellar.core.event.EventType;
-import org.apache.karaf.cellar.core.shell.CellarCommandSupport;
-import org.apache.karaf.cellar.features.Constants;
-import org.apache.karaf.cellar.features.FeatureInfo;
-import org.apache.karaf.features.Feature;
-import org.apache.karaf.features.FeaturesService;
-import org.osgi.framework.BundleContext;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.util.Map;
-
-/**
- * Abstract cluster feature shell command.
- */
-public abstract class FeatureCommandSupport extends CellarCommandSupport {
-
-    protected static final transient Logger LOGGER = LoggerFactory.getLogger(FeatureCommandSupport.class);
-
-    protected FeaturesService featuresService;
-    protected BundleContext bundleContext;
-
-    /**
-     * Force the features status for a specific group.
-     * Why? Its required if no group member currently in the cluster.
-     * If a member of the group joins later, it won't find the change, unless we force it.
-     *
-     * @param groupName the cluster group name.
-     * @param feature the feature name.
-     * @param version the feature version.
-     * @param status the feature status (installed, uninstalled).
-     */
-    public Boolean updateFeatureStatus(String groupName, String feature, String version, Boolean status) {
-
-        Boolean result = Boolean.FALSE;
-        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
-        try {
-            Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
-            Group group = groupManager.findGroupByName(groupName);
-            if (group == null || group.getNodes().isEmpty()) {
-
-                FeatureInfo info = new FeatureInfo(feature, version);
-                Map<FeatureInfo, Boolean> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
-                // check the existing configuration
-                if (version == null || (version.trim().length() < 1)) {
-                    for (FeatureInfo f : clusterFeatures.keySet()) {
-                        if (f.getName().equals(feature)) {
-                            version = f.getVersion();
-                            info.setVersion(version);
-                        }
-                    }
-                }
-
-                // check the features service
-                try {
-                    for (Feature f : featuresService.listFeatures()) {
-                        if (f.getName().equals(feature)) {
-                            version = f.getVersion();
-                            info.setVersion(version);
-                        }
-                    }
-                } catch (Exception e) {
-                    LOGGER.error("Error while browsing features", e);
-                }
-
-                if (info.getVersion() != null && (info.getVersion().trim().length() > 0)) {
-                    clusterFeatures.put(info, status);
-                    result = Boolean.TRUE;
-                }
-            }
-        } finally {
-            Thread.currentThread().setContextClassLoader(originalClassLoader);
-        }
-        return result;
-    }
-
-    /**
-     * Check if a feature is present in the cluster group.
-     *
-     * @param groupName the cluster group.
-     * @param feature the feature name.
-     * @param version the feature version.
-     * @return true if the feature exists in the cluster group, false else.
-     */
-    public boolean featureExists(String groupName, String feature, String version) {
-        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
-        try {
-            Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
-            Map<FeatureInfo, Boolean> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
-
-            if (clusterFeatures == null)
-                return false;
-
-            for (FeatureInfo distributedFeature : clusterFeatures.keySet()) {
-                if (version == null) {
-                    if (distributedFeature.getName().equals(feature))
-                        return true;
-                } else {
-                    if (distributedFeature.getName().equals(feature) && distributedFeature.getVersion().equals(version))
-                        return true;
-                }
-            }
-
-            return false;
-        } finally {
-            Thread.currentThread().setContextClassLoader(originalClassLoader);
-        }
-    }
-
-    /**
-     * Check if a cluster features event is allowed.
-     *
-     * @param group the cluster group.
-     * @param category the features category name.
-     * @param name the feature name.
-     * @param type the event type (inbound, outbound).
-     * @return true if the cluster features event is allowed, false else.
-     */
-    public boolean isAllowed(Group group, String category, String name, EventType type) {
-        CellarSupport support = new CellarSupport();
-        support.setClusterManager(this.clusterManager);
-        support.setGroupManager(this.groupManager);
-        support.setConfigurationAdmin(this.configurationAdmin);
-        return support.isAllowed(group, Constants.CATEGORY, name, EventType.OUTBOUND);
-    }
-
-    public FeaturesService getFeaturesService() {
-        return featuresService;
-    }
-
-    public void setFeaturesService(FeaturesService featuresService) {
-        this.featuresService = featuresService;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/features/src/main/java/org/apache/karaf/cellar/features/shell/InstallFeatureCommand.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/cellar/features/shell/InstallFeatureCommand.java b/features/src/main/java/org/apache/karaf/cellar/features/shell/InstallFeatureCommand.java
index db6c446..e5c7ebf 100644
--- a/features/src/main/java/org/apache/karaf/cellar/features/shell/InstallFeatureCommand.java
+++ b/features/src/main/java/org/apache/karaf/cellar/features/shell/InstallFeatureCommand.java
@@ -13,29 +13,35 @@
  */
 package org.apache.karaf.cellar.features.shell;
 
+import org.apache.karaf.cellar.core.CellarSupport;
+import org.apache.karaf.cellar.core.Configurations;
 import org.apache.karaf.cellar.core.Group;
 import org.apache.karaf.cellar.core.control.SwitchStatus;
 import org.apache.karaf.cellar.core.event.EventProducer;
 import org.apache.karaf.cellar.core.event.EventType;
+import org.apache.karaf.cellar.core.shell.CellarCommandSupport;
 import org.apache.karaf.cellar.features.ClusterFeaturesEvent;
 import org.apache.karaf.cellar.features.Constants;
+import org.apache.karaf.cellar.features.FeatureState;
 import org.apache.karaf.features.FeatureEvent;
+import org.apache.karaf.features.FeaturesService;
 import org.apache.karaf.shell.commands.Argument;
 import org.apache.karaf.shell.commands.Command;
 import org.apache.karaf.shell.commands.Option;
 
 import java.util.List;
+import java.util.Map;
 
 @Command(scope = "cluster", name = "feature-install", description = "Install a feature in a cluster group")
-public class InstallFeatureCommand extends FeatureCommandSupport {
+public class InstallFeatureCommand extends CellarCommandSupport {
 
-    @Option(name = "-c", aliases = { "--no-clean" }, description = "Do not uninstall bundles on failure", required = false, multiValued = false)
+    @Option(name = "-c", aliases = {"--no-clean"}, description = "Do not uninstall bundles on failure", required = false, multiValued = false)
     boolean noClean;
 
-    @Option(name = "-r", aliases = { "--no-auto-refresh" }, description = "Do not automatically refresh bundles", required = false, multiValued = false)
+    @Option(name = "-r", aliases = {"--no-auto-refresh"}, description = "Do not automatically refresh bundles", required = false, multiValued = false)
     boolean noRefresh;
 
-    @Option(name = "-s", aliases = { "--no-auto-start" }, description = "Do not automatically start bundles", required = false, multiValued = false)
+    @Option(name = "-s", aliases = {"--no-auto-start"}, description = "Do not automatically start bundles", required = false, multiValued = false)
     boolean noStart;
 
     @Argument(index = 0, name = "group", description = "The cluster group name", required = true, multiValued = false)
@@ -45,6 +51,7 @@ public class InstallFeatureCommand extends FeatureCommandSupport {
     List<String> features;
 
     private EventProducer eventProducer;
+    private FeaturesService featuresService;
 
     @Override
     protected Object doExecute() throws Exception {
@@ -61,37 +68,68 @@ public class InstallFeatureCommand extends FeatureCommandSupport {
             return null;
         }
 
-        for (String feature : features) {
-            String[] split = feature.split("/");
-            String name = split[0];
-            String version = null;
-            if (split.length == 2) {
-                version = split[1];
-            }
+        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
+        Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
 
-            // check if the feature exists in the map
-            if (!featureExists(groupName, name, version)) {
-                if (version != null) {
-                    System.err.println("Feature " + name + "/" + version + " doesn't exist in the cluster group " + groupName);
-                } else {
-                    System.err.println("Feature " + feature + " doesn't exist in the cluster group " + groupName);
+        try {
+
+            // get cluster feature
+            Map<String, FeatureState> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
+
+            CellarSupport support = new CellarSupport();
+            support.setClusterManager(clusterManager);
+            support.setConfigurationAdmin(configurationAdmin);
+            support.setGroupManager(groupManager);
+
+            for (String feature : features) {
+                String[] split = feature.split("/");
+                String name = split[0];
+                String version = null;
+                if (split.length == 2) {
+                    // use the version provided by the user
+                    version = split[1];
+                }
+                FeatureState found = null;
+                String foundKey = null;
+                for (String k : clusterFeatures.keySet()) {
+                    FeatureState f = clusterFeatures.get(k);
+                    foundKey = k;
+                    if (version == null) {
+                        if (f.getName().equals(name)) {
+                            found = f;
+                            break;
+                        }
+                    } else {
+                        if (f.getName().equals(name) && f.getVersion().equals(version)) {
+                            found = f;
+                            break;
+                        }
+                    }
+                }
+                if (found == null) {
+                    if (version == null)
+                        throw new IllegalArgumentException("Feature " + name + " doesn't exist in cluster group " + groupName);
+                    else
+                        throw new IllegalArgumentException("Feature " + name + "/" + version + " doesn't exist in cluster group " + groupName);
                 }
-                continue;
-            }
 
-            // check if the feature is allowed (outbound)
-            if (!isAllowed(group, Constants.CATEGORY, name, EventType.OUTBOUND)) {
-                System.err.println("Feature " + feature + " is blocked outbound for cluster group " + groupName);
-                continue;
-            }
+                // check if the feature is allowed
+                if (support.isAllowed(group, Constants.CATEGORY, found.getName(), EventType.OUTBOUND)) {
 
-            // update the cluster state
-            updateFeatureStatus(groupName, name, version, true);
+                    // update the cluster group
+                    found.setInstalled(true);
+                    clusterFeatures.put(foundKey, found);
 
-            // broadcast the cluster event
-            ClusterFeaturesEvent event = new ClusterFeaturesEvent(name, version, noClean, noRefresh, noStart, FeatureEvent.EventType.FeatureInstalled);
-            event.setSourceGroup(group);
-            eventProducer.produce(event);
+                    // broadcast the cluster event
+                    ClusterFeaturesEvent event = new ClusterFeaturesEvent(found.getName(), found.getVersion(), noClean, noRefresh, noStart, FeatureEvent.EventType.FeatureInstalled);
+                    event.setSourceGroup(group);
+                    eventProducer.produce(event);
+                } else {
+                    System.err.println("Feature name " + found.getName() + " is blocked outbound for cluster group " + groupName);
+                }
+            }
+        } finally {
+            Thread.currentThread().setContextClassLoader(originalClassLoader);
         }
 
         return null;
@@ -105,4 +143,11 @@ public class InstallFeatureCommand extends FeatureCommandSupport {
         this.eventProducer = eventProducer;
     }
 
+    public FeaturesService getFeaturesService() {
+        return featuresService;
+    }
+
+    public void setFeaturesService(FeaturesService featuresService) {
+        this.featuresService = featuresService;
+    }
 }

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/features/src/main/java/org/apache/karaf/cellar/features/shell/ListFeaturesCommand.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/cellar/features/shell/ListFeaturesCommand.java b/features/src/main/java/org/apache/karaf/cellar/features/shell/ListFeaturesCommand.java
new file mode 100644
index 0000000..20bff51
--- /dev/null
+++ b/features/src/main/java/org/apache/karaf/cellar/features/shell/ListFeaturesCommand.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed 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.cellar.features.shell;
+
+import org.apache.karaf.cellar.core.Configurations;
+import org.apache.karaf.cellar.core.Group;
+import org.apache.karaf.cellar.core.shell.CellarCommandSupport;
+import org.apache.karaf.cellar.features.Constants;
+import org.apache.karaf.cellar.features.FeatureState;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.apache.karaf.shell.table.ShellTable;
+
+import java.util.*;
+
+@Command(scope = "cluster", name = "feature-list", description = "List the features in a cluster group")
+public class ListFeaturesCommand extends CellarCommandSupport {
+
+    @Argument(index = 0, name = "group", description = "The cluster group name", required = true, multiValued = false)
+    String groupName;
+
+    @Option(name = "-i", aliases = { "--installed" }, description = "Display only installed features", required = false, multiValued = false)
+    boolean installed;
+
+    @Option(name = "-o", aliases = { "--ordered" }, description = "Display a list using alphabetical order", required = false, multiValued = false)
+    boolean ordered;
+
+    @Option(name = "--no-format", description = "Disable table rendered output", required = false, multiValued = false)
+    boolean noFormat;
+
+    @Override
+    protected Object doExecute() throws Exception {
+        Group group = groupManager.findGroupByName(groupName);
+        if (group == null) {
+            System.err.println("Cluster group " + groupName + " doesn't exist");
+            return null;
+        }
+
+        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
+        try {
+            Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
+
+            Map<String, FeatureState> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
+;
+            if (clusterFeatures != null && !clusterFeatures.isEmpty()) {
+
+                ShellTable table = new ShellTable();
+                table.column("Name");
+                table.column("Version");
+                table.column("Installed");
+
+                List<FeatureState> featureStates = new ArrayList<FeatureState>(clusterFeatures.values());
+                if (ordered) {
+                    Collections.sort(featureStates, new FeatureComparator());
+                }
+                for (FeatureState info : featureStates) {
+
+                    String name = info.getName();
+                    String version = info.getVersion();
+                    boolean isInstalled = info.isInstalled();
+                    if (version == null)
+                        version = "";
+                    if (!installed || (installed && isInstalled)) {
+                        table.addRow().addContent(
+                                name,
+                                version,
+                                isInstalled ? "x" : "");
+                    }
+                }
+
+                table.print(System.out, !noFormat);
+            } else System.err.println("No features in cluster group " + groupName);
+        } finally {
+            Thread.currentThread().setContextClassLoader(originalClassLoader);
+        }
+        return null;
+    }
+
+    class FeatureComparator implements Comparator<FeatureState> {
+        public int compare(FeatureState f1, FeatureState f2) {
+            return f1.getName().toLowerCase().compareTo(f2.getName().toLowerCase());
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/d16c37c7/features/src/main/java/org/apache/karaf/cellar/features/shell/ListGroupFeatures.java
----------------------------------------------------------------------
diff --git a/features/src/main/java/org/apache/karaf/cellar/features/shell/ListGroupFeatures.java b/features/src/main/java/org/apache/karaf/cellar/features/shell/ListGroupFeatures.java
deleted file mode 100644
index 377b550..0000000
--- a/features/src/main/java/org/apache/karaf/cellar/features/shell/ListGroupFeatures.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Licensed 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.cellar.features.shell;
-
-import org.apache.karaf.cellar.core.Configurations;
-import org.apache.karaf.cellar.core.Group;
-import org.apache.karaf.cellar.features.Constants;
-import org.apache.karaf.cellar.features.FeatureInfo;
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Command;
-import org.apache.karaf.shell.commands.Option;
-import org.apache.karaf.shell.table.ShellTable;
-
-import java.util.*;
-
-@Command(scope = "cluster", name = "feature-list", description = "List the features in a cluster group")
-public class ListGroupFeatures extends FeatureCommandSupport {
-
-    @Argument(index = 0, name = "group", description = "The cluster group name", required = true, multiValued = false)
-    String groupName;
-
-    @Option(name = "-i", aliases = { "--installed" }, description = "Display only installed features", required = false, multiValued = false)
-    boolean installed;
-
-    @Option(name = "-o", aliases = { "--ordered" }, description = "Display a list using alphabetical order", required = false, multiValued = false)
-    boolean ordered;
-
-    @Option(name = "--no-format", description = "Disable table rendered output", required = false, multiValued = false)
-    boolean noFormat;
-
-    @Override
-    protected Object doExecute() throws Exception {
-        Group group = groupManager.findGroupByName(groupName);
-        if (group == null) {
-            System.err.println("Cluster group " + groupName + " doesn't exist");
-            return null;
-        }
-
-        ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
-        try {
-            Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
-
-            Map<FeatureInfo, Boolean> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName);
-
-            if (clusterFeatures != null && !clusterFeatures.isEmpty()) {
-
-                ShellTable table = new ShellTable();
-                table.column("Name");
-                table.column("Version");
-                table.column("Installed");
-
-                List<FeatureInfo> featureInfos = new ArrayList<FeatureInfo>(clusterFeatures.keySet());
-                if (ordered) {
-                    Collections.sort(featureInfos, new FeatureComparator());
-                }
-                for (FeatureInfo info : featureInfos) {
-
-                    String name = info.getName();
-                    String version = info.getVersion();
-                    boolean status = clusterFeatures.get(info);
-                    if (version == null)
-                        version = "";
-                    if (!installed || (installed && status)) {
-                        table.addRow().addContent(
-                                name,
-                                version,
-                                status ? "x" : "");
-                    }
-                }
-
-                table.print(System.out, !noFormat);
-            } else System.err.println("No features in cluster group " + groupName);
-        } finally {
-            Thread.currentThread().setContextClassLoader(originalClassLoader);
-        }
-        return null;
-    }
-
-    class FeatureComparator implements Comparator<FeatureInfo> {
-        public int compare(FeatureInfo f1, FeatureInfo f2) {
-            return f1.getName().toLowerCase().compareTo(f2.getName().toLowerCase());
-        }
-    }
-
-}