Repository: karaf-cellar
Updated Branches:
  refs/heads/master faa015365 -> 529c16083


[KARAF-3051] Refactore Cellar bundle module (commands and MBean) to use "look'n 
feel" from Karaf 3.x


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

Branch: refs/heads/master
Commit: 529c16083e8fbdccc019e451d8434b3120ebf346
Parents: faa0153
Author: Jean-Baptiste Onofré <[email protected]>
Authored: Tue Jun 17 16:33:47 2014 +0200
Committer: Jean-Baptiste Onofré <[email protected]>
Committed: Tue Jun 17 16:33:47 2014 +0200

----------------------------------------------------------------------
 bundle/pom.xml                                  |   6 +
 .../bundle/management/CellarBundleMBean.java    |  25 +-
 .../internal/CellarBundleMBeanImpl.java         | 334 ++++++++++---------
 .../bundle/shell/BundleCommandSupport.java      | 161 +++++----
 .../bundle/shell/InstallBundleCommand.java      |   5 +
 .../cellar/bundle/shell/ListBundleCommand.java  |  26 +-
 .../cellar/bundle/shell/StartBundleCommand.java |  62 ++--
 .../cellar/bundle/shell/StopBundleCommand.java  |  61 ++--
 .../bundle/shell/UninstallBundleCommand.java    |  59 ++--
 config/pom.xml                                  |   1 +
 pom.xml                                         |   5 +
 11 files changed, 409 insertions(+), 336 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/529c1608/bundle/pom.xml
----------------------------------------------------------------------
diff --git a/bundle/pom.xml b/bundle/pom.xml
index 0420dc8..4125073 100644
--- a/bundle/pom.xml
+++ b/bundle/pom.xml
@@ -75,6 +75,12 @@
             <artifactId>org.apache.karaf.features.core</artifactId>
         </dependency>
 
+        <!-- Shell table -->
+        <dependency>
+            <groupId>org.apache.karaf.shell</groupId>
+            <artifactId>org.apache.karaf.shell.table</artifactId>
+        </dependency>
+
         <!-- Logging Dependencies -->
         <dependency>
             <groupId>org.slf4j</groupId>

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/529c1608/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/CellarBundleMBean.java
----------------------------------------------------------------------
diff --git 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/CellarBundleMBean.java
 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/CellarBundleMBean.java
index 558df61..996aa72 100644
--- 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/CellarBundleMBean.java
+++ 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/CellarBundleMBean.java
@@ -30,34 +30,41 @@ public interface CellarBundleMBean {
     void install(String group, String location) throws Exception;
 
     /**
+     * Install and eventually start a bundle in a cluster group.
+     *
+     * @param group the cluster group name.
+     * @param location the bundle location.
+     * @param start true to start the bundle, false else.
+     * @throws Exception
+     */
+    void install(String group, String location, boolean start) throws 
Exception;
+
+    /**
      * Uninstall a bundle from a cluster group.
      *
      * @param group the cluster group name.
-     * @param symbolicName the bundle symbolic name.
-     * @param version the bundle version.
+     * @param id the bundle id.
      * @throws Exception in case of uninstall failure.
      */
-    void uninstall(String group, String symbolicName, String version) throws 
Exception;
+    void uninstall(String group, String id) throws Exception;
 
     /**
      * Start a bundle in a cluster group.
      *
      * @param group the cluster group name.
-     * @param symbolicName the bundle symbolic name.
-     * @param version the bundle version.
+     * @param id the bundle id.
      * @throws Exception in case of start failure.
      */
-    void start(String group, String symbolicName, String version) throws 
Exception;
+    void start(String group, String id) throws Exception;
 
     /**
      * Stop a bundle in a cluster group.
      *
      * @param group the cluster group name.
-     * @param symbolicName the bundle symbolic name.
-     * @param version the bundle version.
+     * @param id the bundle id.
      * @throws Exception in case of stop failure.
      */
-    void stop(String group, String symbolicName, String version) throws 
Exception;
+    void stop(String group, String id) throws Exception;
 
     /**
      * Get the bundles in a cluster group.

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/529c1608/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/internal/CellarBundleMBeanImpl.java
----------------------------------------------------------------------
diff --git 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/internal/CellarBundleMBeanImpl.java
 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/internal/CellarBundleMBeanImpl.java
index f6b5d8a..d01b045 100644
--- 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/internal/CellarBundleMBeanImpl.java
+++ 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/management/internal/CellarBundleMBeanImpl.java
@@ -28,6 +28,8 @@ import javax.management.NotCompliantMBeanException;
 import javax.management.StandardMBean;
 import javax.management.openmbean.*;
 import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 import java.util.jar.JarInputStream;
 import java.util.jar.Manifest;
@@ -82,6 +84,11 @@ public class CellarBundleMBeanImpl extends StandardMBean 
implements CellarBundle
 
     @Override
     public void install(String groupName, String location) throws Exception {
+        this.install(groupName, location, false);
+    }
+
+    @Override
+    public void install(String groupName, String location, boolean start) 
throws Exception {
         // check if cluster group exists
         Group group = groupManager.findGroupByName(groupName);
         if (group == null) {
@@ -123,7 +130,11 @@ public class CellarBundleMBeanImpl extends StandardMBean 
implements CellarBundle
             BundleState state = new BundleState();
             state.setName(name);
             state.setLocation(location);
-            state.setStatus(BundleEvent.INSTALLED);
+            if (start) {
+                state.setStatus(BundleEvent.STARTED);
+            } else {
+                state.setStatus(BundleEvent.INSTALLED);
+            }
             clusterBundles.put(name + "/" + version, state);
         } finally {
             Thread.currentThread().setContextClassLoader(originalClassLoader);
@@ -133,10 +144,15 @@ public class CellarBundleMBeanImpl extends StandardMBean 
implements CellarBundle
         ClusterBundleEvent event = new ClusterBundleEvent(name, version, 
location, BundleEvent.INSTALLED);
         event.setSourceGroup(group);
         eventProducer.produce(event);
+        if (start) {
+            event = new ClusterBundleEvent(name, version, location, 
BundleEvent.STARTED);
+            event.setSourceGroup(group);
+            eventProducer.produce(event);
+        }
     }
 
     @Override
-    public void uninstall(String groupName, String symbolicName, String 
version) throws Exception {
+    public void uninstall(String groupName, String id) throws Exception {
         // check if the cluster group exists
         Group group = groupManager.findGroupByName(groupName);
         if (group == null) {
@@ -152,46 +168,45 @@ public class CellarBundleMBeanImpl extends StandardMBean 
implements CellarBundle
         ClassLoader originalClassLoader = 
Thread.currentThread().getContextClassLoader();
         
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
 
-        String key = null;
-        String location = null;
         try {
             Map<String, BundleState> clusterBundles = 
clusterManager.getMap(Constants.BUNDLE_MAP + Configurations.SEPARATOR + 
groupName);
 
-            key = selector(symbolicName, version, clusterBundles);
+            List<String> bundles = selector(id, clusterBundles);
 
-            if (key == null) {
-                throw new IllegalArgumentException("Bundle " + key + " is not 
found in cluster group " + groupName);
-            }
+            for (String bundle : bundles) {
+                BundleState state = clusterBundles.get(bundle);
+                if (state == null) {
+                    continue;
+                }
+                String location = state.getLocation();
+
+                // check if the bundle location is allowed outbound
+                CellarSupport support = new CellarSupport();
+                support.setClusterManager(this.clusterManager);
+                support.setGroupManager(this.groupManager);
+                support.setConfigurationAdmin(this.configurationAdmin);
+                if (!support.isAllowed(group, Constants.CATEGORY, location, 
EventType.OUTBOUND)) {
+                    continue;
+                }
 
-            BundleState state = clusterBundles.get(key);
-            if (state == null) {
-                throw new IllegalArgumentException("Bundle " + key + " is not 
found in cluster group " + groupName);
-            }
-            location = state.getLocation();
-
-            // check if the bundle location is allowed outbound
-            CellarSupport support = new CellarSupport();
-            support.setClusterManager(this.clusterManager);
-            support.setGroupManager(this.groupManager);
-            support.setConfigurationAdmin(this.configurationAdmin);
-            if (!support.isAllowed(group, Constants.CATEGORY, location, 
EventType.OUTBOUND)) {
-                throw new IllegalArgumentException("Bundle location " + 
location + " is blocked outbound for cluster group " + groupName);
-            }
+                // update the cluster state
+                clusterBundles.remove(bundle);
 
-            clusterBundles.remove(key);
+                // broadcast the cluster event
+                String[] split = bundle.split("/");
+                ClusterBundleEvent event = new ClusterBundleEvent(split[0], 
split[1], location, BundleEvent.UNINSTALLED);
+                event.setSourceGroup(group);
+                eventProducer.produce(event);
+            }
         } finally {
             Thread.currentThread().setContextClassLoader(originalClassLoader);
         }
 
-        // broadcast the event
-        String[] split = key.split("/");
-        ClusterBundleEvent event = new ClusterBundleEvent(split[0], split[1], 
location, BundleEvent.UNINSTALLED);
-        event.setSourceGroup(group);
-        eventProducer.produce(event);
+
     }
 
     @Override
-    public void start(String groupName, String symbolicName, String version) 
throws Exception {
+    public void start(String groupName, String id) throws Exception {
         // check if the cluster group exists
         Group group = groupManager.findGroupByName(groupName);
         if (group == null) {
@@ -206,47 +221,45 @@ public class CellarBundleMBeanImpl extends StandardMBean 
implements CellarBundle
         // update the cluster group
         ClassLoader originalClassLoader = 
Thread.currentThread().getContextClassLoader();
         
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
-        String key = null;
-        String location = null;
         try {
+
             Map<String, BundleState> clusterBundles = 
clusterManager.getMap(Constants.BUNDLE_MAP + Configurations.SEPARATOR + 
groupName);
 
-            key = selector(symbolicName, version, clusterBundles);
+            List<String> bundles = selector(id, clusterBundles);
 
-            if (key == null) {
-                throw new IllegalStateException("Bundle " + key + " not found 
in cluster group " + groupName);
-            }
+            for (String bundle : bundles) {
+                BundleState state = clusterBundles.get(bundle);
+                if (state == null) {
+                    continue;
+                }
+                String location = state.getLocation();
+
+                // check if the bundle location is allowed
+                CellarSupport support = new CellarSupport();
+                support.setClusterManager(this.clusterManager);
+                support.setGroupManager(this.groupManager);
+                support.setConfigurationAdmin(this.configurationAdmin);
+                if (!support.isAllowed(group, Constants.CATEGORY, location, 
EventType.OUTBOUND)) {
+                    continue;
+                }
 
-            BundleState state = clusterBundles.get(key);
-            if (state == null) {
-                throw new IllegalStateException("Bundle " + key + " not found 
in cluster group " + groupName);
-            }
-            location = state.getLocation();
-
-            // check if the bundle location is allowed
-            CellarSupport support = new CellarSupport();
-            support.setClusterManager(this.clusterManager);
-            support.setGroupManager(this.groupManager);
-            support.setConfigurationAdmin(this.configurationAdmin);
-            if (!support.isAllowed(group, Constants.CATEGORY, location, 
EventType.OUTBOUND)) {
-                throw new IllegalArgumentException("Bundle location " + 
location + " is blocked outbound for cluster group " + groupName);
-            }
+                // update the cluster state
+                state.setStatus(BundleEvent.STARTED);
+                clusterBundles.put(bundle, state);
 
-            state.setStatus(BundleEvent.STARTED);
-            clusterBundles.put(key, state);
+                // broadcast the cluster event
+                String[] split = bundle.split("/");
+                ClusterBundleEvent event = new ClusterBundleEvent(split[0], 
split[1], location, BundleEvent.STARTED);
+                event.setSourceGroup(group);
+                eventProducer.produce(event);
+            }
         } finally {
             Thread.currentThread().setContextClassLoader(originalClassLoader);
         }
-
-        // broadcast the cluster event
-        String[] split = key.split("/");
-        ClusterBundleEvent event = new ClusterBundleEvent(split[0], split[1], 
location, BundleEvent.STARTED);
-        event.setSourceGroup(group);
-        eventProducer.produce(event);
     }
 
     @Override
-    public void stop(String groupName, String symbolicName, String version) 
throws Exception {
+    public void stop(String groupName, String id) throws Exception {
         // check if the cluster group exists
         Group group = groupManager.findGroupByName(groupName);
         if (group == null) {
@@ -261,43 +274,40 @@ public class CellarBundleMBeanImpl extends StandardMBean 
implements CellarBundle
         // update the cluster group
         ClassLoader originalClassLoader = 
Thread.currentThread().getContextClassLoader();
         
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
-        String key = null;
-        String location = null;
         try {
             Map<String, BundleState> clusterBundles = 
clusterManager.getMap(Constants.BUNDLE_MAP + Configurations.SEPARATOR + 
groupName);
 
-            key = selector(symbolicName, version, clusterBundles);
+            List<String> bundles = selector(id, clusterBundles);
 
-            if (key == null) {
-                throw new IllegalStateException("Bundle " + key + " not found 
in cluster group " + groupName);
-            }
+            for (String bundle : bundles) {
+                BundleState state = clusterBundles.get(bundle);
+                if (state == null) {
+                    continue;
+                }
+                String location = state.getLocation();
+
+                // check if the bundle location is allowed outbound
+                CellarSupport support = new CellarSupport();
+                support.setClusterManager(this.clusterManager);
+                support.setGroupManager(this.groupManager);
+                support.setConfigurationAdmin(this.configurationAdmin);
+                if (!support.isAllowed(group, Constants.CATEGORY, location, 
EventType.OUTBOUND)) {
+                    continue;
+                }
 
-            BundleState state = clusterBundles.get(key);
-            if (state == null) {
-                throw new IllegalStateException("Bundle " + key + " not found 
in cluster group " + groupName);
-            }
-            location = state.getLocation();
-
-            // check if the bundle location is allowed outbound
-            CellarSupport support = new CellarSupport();
-            support.setClusterManager(this.clusterManager);
-            support.setGroupManager(this.groupManager);
-            support.setConfigurationAdmin(this.configurationAdmin);
-            if (!support.isAllowed(group, Constants.CATEGORY, location, 
EventType.OUTBOUND)) {
-                throw new IllegalArgumentException("Bundle location " + 
location + " is blocked outbound for cluster group " + groupName);
-            }
+                // update the cluster state
+                state.setStatus(BundleEvent.STOPPED);
+                clusterBundles.put(bundle, state);
 
-            state.setStatus(BundleEvent.STOPPED);
-            clusterBundles.put(key, state);
+                // broadcast the cluster event
+                String[] split = bundle.split("/");
+                ClusterBundleEvent event = new ClusterBundleEvent(split[0], 
split[1], location, BundleEvent.STOPPED);
+                event.setSourceGroup(group);
+                eventProducer.produce(event);
+            }
         } finally {
             Thread.currentThread().setContextClassLoader(originalClassLoader);
         }
-
-        // broadcast the cluster event
-        String[] split = key.split("/");
-        ClusterBundleEvent event = new ClusterBundleEvent(split[0], split[1], 
location, BundleEvent.STOPPED);
-        event.setSourceGroup(group);
-        eventProducer.produce(event);
     }
 
     @Override
@@ -306,7 +316,7 @@ public class CellarBundleMBeanImpl extends StandardMBean 
implements CellarBundle
                 new String[]{"id", "name", "version", "status", "location"},
                 new String[]{"ID of the bundle", "Name of the bundle", 
"Version of the bundle", "Current status of the bundle", "Location of the 
bundle"},
                 new OpenType[]{SimpleType.INTEGER, SimpleType.STRING, 
SimpleType.STRING, SimpleType.STRING, SimpleType.STRING});
-        TabularType tableType = new TabularType("Bundles", "Table of all 
KarafCellar bundles", compositeType,
+        TabularType tableType = new TabularType("Bundles", "Table of all Karaf 
Cellar bundles", compositeType,
                 new String[]{"name", "version"});
         TabularData table = new TabularDataSupport(tableType);
 
@@ -366,101 +376,117 @@ public class CellarBundleMBeanImpl extends 
StandardMBean implements CellarBundle
     }
 
     /**
-     * Bundle selector.
+     * Bundle selector on the cluster.
      *
-     * @param name the bundle name, regex, or ID.
-     * @param version the bundle version.
-     * @param clusterBundles the cluster bundles map.
      * @return the bundle key is the distributed bundle map.
      */
-    private String selector(String name, String version, Map<String, 
BundleState> clusterBundles) {
-        String key = null;
-        if (version == null || version.trim().isEmpty()) {
-            // looking for bundle using ID
-            int id = -1;
-            try {
-                id = Integer.parseInt(name);
-                int index = 0;
-                for (String clusterBundle : clusterBundles.keySet()) {
-                    if (index == id) {
-                        key = clusterBundle;
-                        break;
-                    }
-                    index++;
+    protected List<String> selector(String id, Map<String, BundleState> 
clusterBundles) {
+        List<String> bundles = new ArrayList<String>();
+
+        addMatchingBundles(id, bundles, clusterBundles);
+
+        return bundles;
+    }
+
+    protected void addMatchingBundles(String id, List<String> bundles, 
Map<String, BundleState> clusterBundles) {
+
+        // id is a number
+        Pattern pattern = Pattern.compile("^\\d+$");
+        Matcher matcher = pattern.matcher(id);
+        if (matcher.find()) {
+            int idInt = Integer.parseInt(id);
+            int index = 0;
+            for (String bundle : clusterBundles.keySet()) {
+                if (index == idInt) {
+                    bundles.add(bundle);
+                    break;
                 }
-            } catch (NumberFormatException nfe) {
-                // ignore
+                index++;
             }
-            if (id == -1) {
-
-                // add regex support
-                Pattern namePattern = Pattern.compile(name);
+            return;
+        }
 
-                // looking for bundle using only the name
+        // id as a number range
+        pattern = Pattern.compile("^(\\d+)-(\\d+)$");
+        matcher = pattern.matcher(id);
+        if (matcher.find()) {
+            int index = id.indexOf('-');
+            long startId = Long.parseLong(id.substring(0, index));
+            long endId = Long.parseLong(id.substring(index + 1));
+            if (startId < endId) {
+                int bundleIndex = 0;
                 for (String bundle : clusterBundles.keySet()) {
-                    BundleState state = clusterBundles.get(bundle);
-                    if (state.getName() != null) {
-                        // bundle name is populated, check if it matches the 
regex
-                        Matcher matcher = namePattern.matcher(state.getName());
-                        if (matcher.find()) {
-                            key = bundle;
-                            break;
-                        } else {
-                            // no match on bundle name, fall back to symbolic 
name and check if it matches the regex
-                            String[] split = bundle.split("/");
-                            matcher = namePattern.matcher(split[0]);
-                            if (matcher.find()) {
-                                key = bundle;
-                                break;
-                            }
-                        }
-                    } else {
-                        // no bundle name, fall back to symbolic name and 
check if it matches the regex
-                        String[] split = bundle.split("/");
-                        Matcher matcher = namePattern.matcher(split[0]);
-                        if (matcher.find()) {
-                            key = bundle;
-                            break;
-                        }
+                    if (bundleIndex >= startId && bundleIndex <= endId) {
+                        bundles.add(bundle);
                     }
+                    bundleIndex++;
                 }
             }
-        } else {
-            // looking for the bundle using name and version
-
-            // add regex support of the name
-            Pattern namePattern = Pattern.compile(name);
+            return;
+        }
 
+        int index = id.indexOf('/');
+        if (index != -1) {
+            // id is name/version
+            String[] idSplit = id.split("/");
+            String name = idSplit[0];
+            String version = idSplit[1];
             for (String bundle : clusterBundles.keySet()) {
-                String[] split = bundle.split("/");
-                BundleState state = clusterBundles.get(bundle);
-                if (split[1].equals(version)) {
+                String[] bundleSplit = bundle.split("/");
+                if (bundleSplit[1].equals(version)) {
+                    // regex on the name
+                    Pattern namePattern = Pattern.compile(name);
+                    BundleState state = clusterBundles.get(bundle);
                     if (state.getName() != null) {
                         // bundle name is populated, check if it matches the 
regex
-                        Matcher matcher = namePattern.matcher(state.getName());
+                        matcher = namePattern.matcher(state.getName());
                         if (matcher.find()) {
-                            key = bundle;
-                            break;
+                            bundles.add(bundle);
                         } else {
                             // no match on bundle name, fall back to symbolic 
name and check if it matches the regex
-                            matcher = namePattern.matcher(split[0]);
+                            matcher = namePattern.matcher(name);
                             if (matcher.find()) {
-                                key = bundle;
-                                break;
+                                bundles.add(bundle);
                             }
                         }
                     } else {
                         // no bundle name, fall back to symbolic name and 
check if it matches the regex
-                        Matcher matcher = namePattern.matcher(split[0]);
+                        matcher = namePattern.matcher(name);
                         if (matcher.find()) {
-                            key = bundle;
-                            break;
+                            bundles.add(bundle);
                         }
                     }
                 }
             }
+            return;
+        }
+
+        // id is just name
+        // regex support on the name
+        Pattern namePattern = Pattern.compile(id);
+        // looking for bundle using only the name
+        for (String bundle : clusterBundles.keySet()) {
+            BundleState state = clusterBundles.get(bundle);
+            if (state.getName() != null) {
+                // bundle name is populated, check if it matches the regex
+                matcher = namePattern.matcher(state.getName());
+                if (matcher.find()) {
+                    bundles.add(bundle);
+                } else {
+                    // no match on bundle name, fall back to symbolic name and 
check if it matches the regex
+                    matcher = namePattern.matcher(bundle);
+                    if (matcher.find()) {
+                        bundles.add(bundle);
+                    }
+                }
+            } else {
+                // no bundle name, fall back to symbolic name and check if it 
matches the regex
+                matcher = namePattern.matcher(bundle);
+                if (matcher.find()) {
+                    bundles.add(bundle);
+                }
+            }
         }
-        return key;
     }
 
 }

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/529c1608/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/BundleCommandSupport.java
----------------------------------------------------------------------
diff --git 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/BundleCommandSupport.java
 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/BundleCommandSupport.java
index 97cc59b..6295743 100644
--- 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/BundleCommandSupport.java
+++ 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/BundleCommandSupport.java
@@ -17,6 +17,8 @@ import org.apache.karaf.cellar.bundle.BundleState;
 import org.apache.karaf.cellar.core.shell.CellarCommandSupport;
 import org.apache.karaf.shell.commands.Argument;
 
+import java.util.ArrayList;
+import java.util.List;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
@@ -26,11 +28,8 @@ public abstract class BundleCommandSupport extends 
CellarCommandSupport {
     @Argument(index = 0, name = "group", description = "The cluster group 
name", required = true, multiValued = false)
     String groupName;
 
-    @Argument(index = 1, name = "id", description = "The bundle ID or name", 
required = true, multiValued = false)
-    String name;
-
-    @Argument(index = 2, name = "version", description = "The bundle version", 
required = false, multiValued = false)
-    String version;
+    @Argument(index = 1, name = "ids", description = "The list of bundle 
(identified by IDs or name or name/version) separated by whitespaces", required 
= true, multiValued = true)
+    List<String> ids;
 
     protected abstract Object doExecute() throws Exception;
 
@@ -39,95 +38,121 @@ public abstract class BundleCommandSupport extends 
CellarCommandSupport {
      *
      * @return the bundle key is the distributed bundle map.
      */
-    protected String selector(Map<String, BundleState> clusterBundles) {
-        String key = null;
-        if (version == null) {
-            // looking for bundle using ID
-            int id = -1;
-            try {
-                id = Integer.parseInt(name);
-                int index = 0;
-                for (String bundle : clusterBundles.keySet()) {
-                    if (index == id) {
-                        key = bundle;
-                        break;
-                    }
-                    index++;
+    protected List<String> selector(Map<String, BundleState> clusterBundles) {
+        List<String> bundles = new ArrayList<String>();
+
+        if (ids != null && !ids.isEmpty()) {
+            for (String id : ids) {
+                if (id == null) {
+                    continue;
                 }
-            } catch (NumberFormatException nfe) {
-                // ignore
+
+                addMatchingBundles(id, bundles, clusterBundles);
+
             }
-            if (id == -1) {
+        }
+        return bundles;
+    }
 
-                // add regex support
-                Pattern namePattern = Pattern.compile(name);
+    protected void addMatchingBundles(String id, List<String> bundles, 
Map<String, BundleState> clusterBundles) {
 
-                // looking for bundle using only the name
+        // id is a number
+        Pattern pattern = Pattern.compile("^\\d+$");
+        Matcher matcher = pattern.matcher(id);
+        if (matcher.find()) {
+            int idInt = Integer.parseInt(id);
+            int index = 0;
+            for (String bundle : clusterBundles.keySet()) {
+                if (index == idInt) {
+                    bundles.add(bundle);
+                    break;
+                }
+                index++;
+            }
+            return;
+        }
+
+        // id as a number range
+        pattern = Pattern.compile("^(\\d+)-(\\d+)$");
+        matcher = pattern.matcher(id);
+        if (matcher.find()) {
+            int index = id.indexOf('-');
+            long startId = Long.parseLong(id.substring(0, index));
+            long endId = Long.parseLong(id.substring(index + 1));
+            if (startId < endId) {
+                int bundleIndex = 0;
                 for (String bundle : clusterBundles.keySet()) {
-                    BundleState state = clusterBundles.get(bundle);
-                    if (state.getName() != null) {
-                        // bundle name is populated, check if it matches the 
regex
-                        Matcher matcher = namePattern.matcher(state.getName());
-                        if (matcher.find()) {
-                            key = bundle;
-                            break;
-                        } else {
-                            // not matched on bundle name, fall back to 
symbolic name and check if it matches the regex
-                            String split[] = bundle.split("/");
-                            matcher = namePattern.matcher(split[0]);
-                            if (matcher.find()) {
-                                key = bundle;
-                                break;
-                            }
-                        }
-                    } else {
-                        // no bundle name, fall back to symbolic name and 
check if it matches the regex
-                        String split[] = bundle.split("/");
-                        System.out.println("Bundle-SymbolicName: " + split[0]);
-                        Matcher matcher = namePattern.matcher(split[0]);
-                        if (matcher.find()) {
-                            key = bundle;
-                            break;
-                        }
+                    if (bundleIndex >= startId && bundleIndex <= endId) {
+                        bundles.add(bundle);
                     }
+                    bundleIndex++;
                 }
             }
-        } else {
-            // looking for the bundle using name and version
-
-            // add regex support of the name
-            Pattern namePattern = Pattern.compile(name);
+            return;
+        }
 
+        int index = id.indexOf('/');
+        if (index != -1) {
+            // id is name/version
+            String[] idSplit = id.split("/");
+            String name = idSplit[0];
+            String version = idSplit[1];
             for (String bundle : clusterBundles.keySet()) {
-                String[] split = bundle.split("/");
-                BundleState state = clusterBundles.get(bundle);
-                if (split[1].equals(version)) {
+                String[] bundleSplit = bundle.split("/");
+                if (bundleSplit[1].equals(version)) {
+                    // regex on the name
+                    Pattern namePattern = Pattern.compile(name);
+                    BundleState state = clusterBundles.get(bundle);
                     if (state.getName() != null) {
                         // bundle name is populated, check if it matches the 
regex
-                        Matcher matcher = namePattern.matcher(state.getName());
+                        matcher = namePattern.matcher(state.getName());
                         if (matcher.find()) {
-                            key = bundle;
-                            break;
+                            bundles.add(bundle);
                         } else {
                             // no match on bundle name, fall back to symbolic 
name and check if it matches the regex
-                            matcher = namePattern.matcher(split[0]);
+                            matcher = namePattern.matcher(name);
                             if (matcher.find()) {
-                                key = bundle;
-                                break;
+                                bundles.add(bundle);
                             }
                         }
                     } else {
                         // no bundle name, fall back to symbolic name and 
check if it matches the regex
-                        Matcher matcher = namePattern.matcher(split[0]);
+                        matcher = namePattern.matcher(name);
                         if (matcher.find()) {
-                            key = bundle;
-                            break;
+                            bundles.add(bundle);
                         }
                     }
                 }
             }
+            return;
+        }
+
+        // id is just name
+        // regex support on the name
+        Pattern namePattern = Pattern.compile(id);
+        // looking for bundle using only the name
+        for (String bundle : clusterBundles.keySet()) {
+            BundleState state = clusterBundles.get(bundle);
+            if (state.getName() != null) {
+                // bundle name is populated, check if it matches the regex
+                matcher = namePattern.matcher(state.getName());
+                if (matcher.find()) {
+                    bundles.add(bundle);
+                } else {
+                    // no match on bundle name, fall back to symbolic name and 
check if it matches the regex
+                    matcher = namePattern.matcher(bundle);
+                    if (matcher.find()) {
+                        bundles.add(bundle);
+                    }
+                }
+            } else {
+                // no bundle name, fall back to symbolic name and check if it 
matches the regex
+                matcher = namePattern.matcher(bundle);
+                if (matcher.find()) {
+                    bundles.add(bundle);
+                }
+            }
         }
-        return key;
     }
 
 }

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/529c1608/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/InstallBundleCommand.java
----------------------------------------------------------------------
diff --git 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/InstallBundleCommand.java
 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/InstallBundleCommand.java
index e6712fc..390197d 100644
--- 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/InstallBundleCommand.java
+++ 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/InstallBundleCommand.java
@@ -109,6 +109,11 @@ public class InstallBundleCommand extends 
CellarCommandSupport {
                 ClusterBundleEvent event = new 
ClusterBundleEvent(symbolicName, version, url, BundleEvent.INSTALLED);
                 event.setSourceGroup(group);
                 eventProducer.produce(event);
+                if (start) {
+                    event = new ClusterBundleEvent(symbolicName, version, url, 
BundleEvent.STARTED);
+                    event.setSourceGroup(group);
+                    eventProducer.produce(event);
+                }
             } else {
                 System.err.println("Bundle location " + url + " is blocked 
outbound for cluster group " + groupName);
             }

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/529c1608/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/ListBundleCommand.java
----------------------------------------------------------------------
diff --git 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/ListBundleCommand.java
 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/ListBundleCommand.java
index 6af87ec..e4b5a76 100644
--- 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/ListBundleCommand.java
+++ 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/ListBundleCommand.java
@@ -21,6 +21,7 @@ import 
org.apache.karaf.cellar.core.shell.CellarCommandSupport;
 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 org.osgi.framework.BundleEvent;
 
 import java.util.Map;
@@ -28,9 +29,6 @@ import java.util.Map;
 @Command(scope = "cluster", name = "bundle-list", description = "List the 
bundles in a cluster group")
 public class ListBundleCommand extends CellarCommandSupport {
 
-    protected static final String HEADER_FORMAT = " %-4s   %-11s  %s";
-    protected static final String OUTPUT_FORMAT = "[%-4s] [%-11s] %s";
-
     @Argument(index = 0, name = "group", description = "The cluster group 
name", required = true, multiValued = false)
     String groupName;
 
@@ -56,7 +54,19 @@ public class ListBundleCommand extends CellarCommandSupport {
             Map<String, BundleState> clusterBundles = 
clusterManager.getMap(Constants.BUNDLE_MAP + Configurations.SEPARATOR + 
groupName);
             if (clusterBundles != null && !clusterBundles.isEmpty()) {
                 System.out.println(String.format("Bundles in cluster group " + 
groupName));
-                System.out.println(String.format(HEADER_FORMAT, "ID", "State", 
"Name"));
+
+                ShellTable table = new ShellTable();
+                table.column("ID").alignRight();
+                table.column("State");
+                table.column("Version");
+                if (showLocation) {
+                    table.column("Location");
+                } else if (showSymbolicName) {
+                    table.column("Symbolic Name");
+                } else {
+                    table.column("Name");
+                }
+
                 int id = 0;
                 for (String bundle : clusterBundles.keySet()) {
                     String[] tokens = bundle.split("/");
@@ -99,16 +109,18 @@ public class ListBundleCommand extends 
CellarCommandSupport {
                             break;
                     }
                     if (showLocation) {
-                        System.out.println(String.format(OUTPUT_FORMAT, id, 
status, state.getLocation()));
+                        table.addRow().addContent(id, status, version, 
state.getLocation());
                     } else {
                         if (showSymbolicName) {
-                            System.out.println(String.format(OUTPUT_FORMAT, 
id, status, symbolicName + " (" + version + ")"));
+                            table.addRow().addContent(id, status, version, 
symbolicName);
                         } else {
-                            System.out.println(String.format(OUTPUT_FORMAT, 
id, status, state.getName() + " (" + version + ")"));
+                            table.addRow().addContent(id, status, version, 
state.getName());
                         }
                     }
                     id++;
                 }
+
+                table.print(System.out);
             } else {
                 System.err.println("No bundle found in cluster group " + 
groupName);
             }

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/529c1608/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/StartBundleCommand.java
----------------------------------------------------------------------
diff --git 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/StartBundleCommand.java
 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/StartBundleCommand.java
index beee496..23cd418 100644
--- 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/StartBundleCommand.java
+++ 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/StartBundleCommand.java
@@ -25,9 +25,10 @@ import org.apache.karaf.cellar.core.event.EventType;
 import org.apache.karaf.shell.commands.Command;
 import org.osgi.framework.BundleEvent;
 
+import java.util.List;
 import java.util.Map;
 
-@Command(scope = "cluster", name = "bundle-start", description = "Start a 
bundle in a cluster group")
+@Command(scope = "cluster", name = "bundle-start", description = "Start 
bundles in a cluster group")
 public class StartBundleCommand extends BundleCommandSupport {
 
     private EventProducer eventProducer;
@@ -51,46 +52,41 @@ public class StartBundleCommand extends 
BundleCommandSupport {
         ClassLoader originalClassLoader = 
Thread.currentThread().getContextClassLoader();
         
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
 
-        String location;
-        String key = null;
         try {
             Map<String, BundleState> clusterBundles = 
clusterManager.getMap(Constants.BUNDLE_MAP + Configurations.SEPARATOR + 
groupName);
 
-            key = selector(clusterBundles);
-
-            if (key == null) {
-                System.err.println("Bundle " + key + " not found in cluster 
group " + groupName);
-            }
-
-            BundleState state = clusterBundles.get(key);
-            if (state == null) {
-                System.err.println("Bundle " + key + " not found in cluster 
group " + groupName);
-                return null;
+            List<String> bundles = selector(clusterBundles);
+
+            for (String bundle : bundles) {
+                BundleState state = clusterBundles.get(bundle);
+                if (state == null) {
+                    System.err.println("Bundle " + state + " not found in 
cluster group " + groupName);
+                }
+                String location = state.getLocation();
+
+                // check if the bundle is allowed
+                CellarSupport support = new CellarSupport();
+                support.setClusterManager(this.clusterManager);
+                support.setGroupManager(this.groupManager);
+                support.setConfigurationAdmin(this.configurationAdmin);
+                if (!support.isAllowed(group, Constants.CATEGORY, location, 
EventType.OUTBOUND)) {
+                    System.err.println("Bundle location " + location + " is 
blocked outbound for cluster group " + groupName);
+                }
+
+                // update the cluster state
+                state.setStatus(BundleEvent.STARTED);
+                clusterBundles.put(bundle, state);
+
+                // broadcast the cluster event
+                String[] split = bundle.split("/");
+                ClusterBundleEvent event = new ClusterBundleEvent(split[0], 
split[1], location, BundleEvent.STARTED);
+                event.setSourceGroup(group);
+                eventProducer.produce(event);
             }
-            location = state.getLocation();
-
-            // check if the bundle is allowed
-            CellarSupport support = new CellarSupport();
-            support.setClusterManager(this.clusterManager);
-            support.setGroupManager(this.groupManager);
-            support.setConfigurationAdmin(this.configurationAdmin);
-            if (!support.isAllowed(group, Constants.CATEGORY, location, 
EventType.OUTBOUND)) {
-                System.err.println("Bundle location " + location + " is 
blocked outbound for cluster group " + groupName);
-                return null;
-            }
-
-            state.setStatus(BundleEvent.STARTED);
-            clusterBundles.put(key, state);
         } finally {
             Thread.currentThread().setContextClassLoader(originalClassLoader);
         }
 
-        // broadcast the cluster event
-        String[] split = key.split("/");
-        ClusterBundleEvent event = new ClusterBundleEvent(split[0], split[1], 
location, BundleEvent.STARTED);
-        event.setSourceGroup(group);
-        eventProducer.produce(event);
-
         return null;
     }
 

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/529c1608/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/StopBundleCommand.java
----------------------------------------------------------------------
diff --git 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/StopBundleCommand.java
 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/StopBundleCommand.java
index 2f94b07..ed94092 100644
--- 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/StopBundleCommand.java
+++ 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/StopBundleCommand.java
@@ -25,6 +25,7 @@ import org.apache.karaf.cellar.core.event.EventType;
 import org.apache.karaf.shell.commands.Command;
 import org.osgi.framework.BundleEvent;
 
+import java.util.List;
 import java.util.Map;
 
 @Command(scope = "cluster", name = "bundle-stop", description = "Stop a bundle 
in a cluster group")
@@ -50,47 +51,41 @@ public class StopBundleCommand extends BundleCommandSupport 
{
         ClassLoader originalClassLoader = 
Thread.currentThread().getContextClassLoader();
         
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
 
-        String location;
-        String key = null;
         try {
             Map<String, BundleState> clusterBundles = 
clusterManager.getMap(Constants.BUNDLE_MAP + Configurations.SEPARATOR + 
groupName);
 
-            key = selector(clusterBundles);
-
-            if (key == null) {
-                System.err.println("Bundle " + key + " not found in cluster 
group " + groupName);
-                return null;
-            }
-
-            BundleState state = clusterBundles.get(key);
-            if (state == null) {
-                System.err.println("Bundle " + key + " not found in cluster 
group " + groupName);
-                return null;
+            List<String> bundles = selector(clusterBundles);
+
+            for (String bundle : bundles) {
+                BundleState state = clusterBundles.get(bundle);
+                if (state == null) {
+                    System.err.println("Bundle " + bundle + " not found in 
cluster group " + groupName);
+                }
+                String location = state.getLocation();
+
+                // check if the bundle is allowed
+                CellarSupport support = new CellarSupport();
+                support.setClusterManager(this.clusterManager);
+                support.setGroupManager(this.groupManager);
+                support.setConfigurationAdmin(this.configurationAdmin);
+                if (!support.isAllowed(group, Constants.CATEGORY, location, 
EventType.OUTBOUND)) {
+                    System.err.println("Bundle location " + location + " is 
blocked outbound for cluster group " + groupName);
+                }
+
+                // update the cluster state
+                state.setStatus(BundleEvent.STOPPED);
+                clusterBundles.put(bundle, state);
+
+                // broadcast the cluster event
+                String[] split = bundle.split("/");
+                ClusterBundleEvent event = new ClusterBundleEvent(split[0], 
split[1], location, BundleEvent.STOPPED);
+                event.setSourceGroup(group);
+                eventProducer.produce(event);
             }
-            state.setStatus(BundleEvent.STOPPED);
-            location = state.getLocation();
-
-            // check if the bundle is allowed
-            CellarSupport support = new CellarSupport();
-            support.setClusterManager(this.clusterManager);
-            support.setGroupManager(this.groupManager);
-            support.setConfigurationAdmin(this.configurationAdmin);
-            if (!support.isAllowed(group, Constants.CATEGORY, location, 
EventType.OUTBOUND)) {
-                System.err.println("Bundle location " + location + " is 
blocked outbound for cluster group " + groupName);
-                return null;
-            }
-
-            clusterBundles.put(key, state);
         } finally {
             Thread.currentThread().setContextClassLoader(originalClassLoader);
         }
 
-        // broadcast the cluster event
-        String[] split = key.split("/");
-        ClusterBundleEvent event = new ClusterBundleEvent(split[0], split[1], 
location, BundleEvent.STOPPED);
-        event.setSourceGroup(group);
-        eventProducer.produce(event);
-
         return null;
     }
 

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/529c1608/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/UninstallBundleCommand.java
----------------------------------------------------------------------
diff --git 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/UninstallBundleCommand.java
 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/UninstallBundleCommand.java
index d48a360..47cfe8c 100644
--- 
a/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/UninstallBundleCommand.java
+++ 
b/bundle/src/main/java/org/apache/karaf/cellar/bundle/shell/UninstallBundleCommand.java
@@ -25,6 +25,7 @@ import org.apache.karaf.cellar.core.event.EventType;
 import org.apache.karaf.shell.commands.Command;
 import org.osgi.framework.BundleEvent;
 
+import java.util.List;
 import java.util.Map;
 
 @Command(scope = "cluster", name = "bundle-uninstall", description = 
"Uninstall a bundle from a cluster group")
@@ -51,46 +52,40 @@ public class UninstallBundleCommand extends 
BundleCommandSupport {
         ClassLoader originalClassLoader = 
Thread.currentThread().getContextClassLoader();
         
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
 
-        String location;
-        String key = null;
         try {
             Map<String, BundleState> clusterBundles = 
clusterManager.getMap(Constants.BUNDLE_MAP + Configurations.SEPARATOR + 
groupName);
 
-            key = selector(clusterBundles);
-
-            if (key == null) {
-                System.err.println("Bundle " + key + " not found in cluster 
group " + groupName);
-                return null;
-            }
-
-            BundleState state = clusterBundles.get(key);
-            if (state == null) {
-                System.err.println("Bundle " + key + " not found in cluster 
group " + groupName);
-                return null;
+            List<String> bundles = selector(clusterBundles);
+
+            for (String bundle : bundles) {
+                BundleState state = clusterBundles.get(bundle);
+                if (state == null) {
+                    System.err.println("Bundle " + bundle + " not found in 
cluster group " + groupName);
+                }
+                String location = state.getLocation();
+
+                // check if the bundle is allowed
+                CellarSupport support = new CellarSupport();
+                support.setClusterManager(this.clusterManager);
+                support.setGroupManager(this.groupManager);
+                support.setConfigurationAdmin(this.configurationAdmin);
+                if (!support.isAllowed(group, Constants.CATEGORY, location, 
EventType.OUTBOUND)) {
+                    System.err.println("Bundle location " + location + " is 
blocked outbound for cluster group " + groupName);
+                    return null;
+                }
+
+                clusterBundles.remove(bundle);
+
+                // broadcast the cluster event
+                String[] split = bundle.split("/");
+                ClusterBundleEvent event = new ClusterBundleEvent(split[0], 
split[1], location, BundleEvent.UNINSTALLED);
+                event.setSourceGroup(group);
+                eventProducer.produce(event);
             }
-            location = state.getLocation();
-
-            // check if the bundle is allowed
-            CellarSupport support = new CellarSupport();
-            support.setClusterManager(this.clusterManager);
-            support.setGroupManager(this.groupManager);
-            support.setConfigurationAdmin(this.configurationAdmin);
-            if (!support.isAllowed(group, Constants.CATEGORY, location, 
EventType.OUTBOUND)) {
-                System.err.println("Bundle location " + location + " is 
blocked outbound for cluster group " + groupName);
-                return null;
-            }
-
-            clusterBundles.remove(key);
         } finally {
             Thread.currentThread().setContextClassLoader(originalClassLoader);
         }
 
-        // broadcast the cluster event
-        String[] split = key.split("/");
-        ClusterBundleEvent event = new ClusterBundleEvent(split[0], split[1], 
location, BundleEvent.UNINSTALLED);
-        event.setSourceGroup(group);
-        eventProducer.produce(event);
-
         return null;
     }
 

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/529c1608/config/pom.xml
----------------------------------------------------------------------
diff --git a/config/pom.xml b/config/pom.xml
index 0aa35c8..29a9ea8 100644
--- a/config/pom.xml
+++ b/config/pom.xml
@@ -74,6 +74,7 @@
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.configadmin</artifactId>
         </dependency>
+
         <!-- Logging Dependencies -->
         <dependency>
             <groupId>org.slf4j</groupId>

http://git-wip-us.apache.org/repos/asf/karaf-cellar/blob/529c1608/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index e4a7a90..dd239f9 100644
--- a/pom.xml
+++ b/pom.xml
@@ -233,6 +233,11 @@
                 <artifactId>org.apache.karaf.shell.console</artifactId>
                 <version>${karaf.version}</version>
             </dependency>
+            <dependency>
+                <groupId>org.apache.karaf.shell</groupId>
+                <artifactId>org.apache.karaf.shell.table</artifactId>
+                <version>${karaf.version}</version>
+            </dependency>
 
             <dependency>
                 <groupId>org.apache.karaf.tooling</groupId>

Reply via email to