Added support for "maps" cli command that displays meta information for various consistent maps in the system

Change-Id: I63e590a8520ac9d1238efe4ad0033dcba939e472
diff --git a/cli/src/main/java/org/onosproject/cli/net/MapsListCommand.java b/cli/src/main/java/org/onosproject/cli/net/MapsListCommand.java
new file mode 100644
index 0000000..13e8867
--- /dev/null
+++ b/cli/src/main/java/org/onosproject/cli/net/MapsListCommand.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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.onosproject.cli.net;
+
+import java.util.List;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.store.service.MapInfo;
+import org.onosproject.store.service.StorageAdminService;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ArrayNode;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+
+/**
+ * Command to list the various maps in the system.
+ */
+@Command(scope = "onos", name = "maps",
+        description = "Lists information about consistent maps in the system")
+public class MapsListCommand extends AbstractShellCommand {
+
+    // TODO: Add support to display different eventually
+    // consistent maps as well.
+
+    private static final String FMT = "%-20s %8s";
+
+    /**
+     * Displays map info as text.
+     *
+     * @param mapInfo map descriptions
+     */
+    private void displayMaps(List<MapInfo> mapInfo) {
+        print("------------------------------");
+        print(FMT, "Name", "Size");
+        print("------------------------------");
+
+
+        for (MapInfo info : mapInfo) {
+            print(FMT, info.name(), info.size());
+        }
+        if (mapInfo.size() > 0) {
+            print("------------------------------");
+        }
+    }
+
+    /**
+     * Converts list of map info into a JSON object.
+     *
+     * @param mapInfo map descriptions
+     */
+    private JsonNode json(List<MapInfo> mapInfo) {
+        ObjectMapper mapper = new ObjectMapper();
+        ArrayNode maps = mapper.createArrayNode();
+
+        // Create a JSON node for each map
+        mapInfo.stream()
+            .forEach(info -> {
+                ObjectNode map = mapper.createObjectNode();
+                map.put("name", info.name())
+                   .put("size", info.size());
+                maps.add(map);
+            });
+
+        return maps;
+    }
+
+    @Override
+    protected void execute() {
+        StorageAdminService storageAdminService = get(StorageAdminService.class);
+        List<MapInfo> mapInfo = storageAdminService.getMapInfo();
+        if (outputJson()) {
+            print("%s", json(mapInfo));
+        } else {
+            displayMaps(mapInfo);
+        }
+    }
+}
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index e065d31..4c0497b 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -230,6 +230,9 @@
             <action class="org.onosproject.cli.net.PartitionsListCommand"/>
         </command>
         <command>
+            <action class="org.onosproject.cli.net.MapsListCommand"/>
+        </command>
+        <command>
             <action class="org.onosproject.cli.net.ClusterDevicesCommand"/>
             <completers>
                 <ref component-id="clusterIdCompleter"/>
diff --git a/core/api/src/main/java/org/onosproject/store/service/MapInfo.java b/core/api/src/main/java/org/onosproject/store/service/MapInfo.java
new file mode 100644
index 0000000..5db1704
--- /dev/null
+++ b/core/api/src/main/java/org/onosproject/store/service/MapInfo.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2015 Open Networking Laboratory
+ *
+ * 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.onosproject.store.service;
+
+/**
+ * Metadata information for a consistent map.
+ */
+public class MapInfo {
+    private final String name;
+    private final int size;
+
+    public MapInfo(String name, int size) {
+        this.name = name;
+        this.size = size;
+    }
+
+    /**
+     * Returns the name of the map.
+     *
+     * @return map name
+     */
+    public String name() {
+        return name;
+    }
+
+    /**
+     * Returns the number of entries in the map.
+     *
+     * @return map size
+     */
+    public int size() {
+        return size;
+    }
+}
diff --git a/core/api/src/main/java/org/onosproject/store/service/StorageAdminService.java b/core/api/src/main/java/org/onosproject/store/service/StorageAdminService.java
index 427b953..572867c 100644
--- a/core/api/src/main/java/org/onosproject/store/service/StorageAdminService.java
+++ b/core/api/src/main/java/org/onosproject/store/service/StorageAdminService.java
@@ -28,4 +28,11 @@
      * @return list of partition information
      */
     List<PartitionInfo> getPartitionInfo();
+
+    /**
+     * Returns information about all the consistent maps in the system.
+     *
+     * @return list of map information
+     */
+    List<MapInfo> getMapInfo();
 }
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseManager.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseManager.java
index b939780..39d1bab 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseManager.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseManager.java
@@ -44,7 +44,9 @@
 import org.onosproject.store.cluster.messaging.ClusterCommunicationService;
 import org.onosproject.store.ecmap.EventuallyConsistentMapBuilderImpl;
 import org.onosproject.store.service.ConsistentMapBuilder;
+import org.onosproject.store.service.ConsistentMapException;
 import org.onosproject.store.service.EventuallyConsistentMapBuilder;
+import org.onosproject.store.service.MapInfo;
 import org.onosproject.store.service.PartitionInfo;
 import org.onosproject.store.service.StorageAdminService;
 import org.onosproject.store.service.StorageService;
@@ -58,8 +60,10 @@
 import java.util.Set;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 import java.util.stream.Collectors;
 
 import static org.slf4j.LoggerFactory.getLogger;
@@ -80,6 +84,7 @@
     private static final int DATABASE_STARTUP_TIMEOUT_SEC = 60;
     private static final int RAFT_ELECTION_TIMEOUT = 3000;
     private static final int RAFT_HEARTBEAT_TIMEOUT = 1500;
+    private static final int DATABASE_OPERATION_TIMEOUT_MILLIS = 5000;
 
     private ClusterCoordinator coordinator;
     private PartitionedDatabase partitionedDatabase;
@@ -294,4 +299,33 @@
     public <K, V> ConsistentMapBuilder<K, V> consistentMapBuilder() {
         return new DefaultConsistentMapBuilder<>(inMemoryDatabase, partitionedDatabase);
     }
+
+    @Override
+    public List<MapInfo> getMapInfo() {
+        List<MapInfo> maps = Lists.newArrayList();
+        maps.addAll(getMapInfo(inMemoryDatabase));
+        maps.addAll(getMapInfo(partitionedDatabase));
+        return maps;
+    }
+
+    private List<MapInfo> getMapInfo(Database database) {
+        return complete(database.tableNames())
+            .stream()
+            .map(name -> new MapInfo(name, complete(database.size(name))))
+            .filter(info -> info.size() > 0)
+            .collect(Collectors.toList());
+    }
+
+    private static <T> T complete(CompletableFuture<T> future) {
+        try {
+            return future.get(DATABASE_OPERATION_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            throw new ConsistentMapException.Interrupted();
+        } catch (TimeoutException e) {
+            throw new ConsistentMapException.Timeout();
+        } catch (ExecutionException e) {
+            throw new ConsistentMapException(e.getCause());
+        }
+    }
 }
\ No newline at end of file
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseProxy.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseProxy.java
index 20b736f..3ea06fb 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseProxy.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseProxy.java
@@ -30,6 +30,12 @@
  */
 public interface DatabaseProxy<K, V> {
 
+    /**
+     * Returns a set of all tables names.
+     * @return A completable future to be completed with the result once complete.
+     */
+    CompletableFuture<Set<String>> tableNames();
+
   /**
    * Gets the table size.
    *
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseState.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseState.java
index 1863496..097261d 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseState.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DatabaseState.java
@@ -44,6 +44,9 @@
   public void init(StateContext<DatabaseState<K, V>> context);
 
   @Query
+  Set<String> tableNames();
+
+  @Query
   int size(String tableName);
 
   @Query
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabase.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabase.java
index 9ffd1e8..c14e57c 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabase.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabase.java
@@ -61,6 +61,11 @@
     }
 
     @Override
+    public CompletableFuture<Set<String>> tableNames() {
+        return checkOpen(() -> proxy.tableNames());
+    }
+
+    @Override
     public CompletableFuture<Integer> size(String tableName) {
         return checkOpen(() -> proxy.size(tableName));
     }
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabaseState.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabaseState.java
index 837852d..e63a3d8 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabaseState.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/DefaultDatabaseState.java
@@ -19,6 +19,7 @@
 import java.util.Arrays;
 import java.util.Collection;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
@@ -71,6 +72,11 @@
     }
 
     @Override
+    public Set<String> tableNames() {
+        return new HashSet<>(tables.keySet());
+    }
+
+    @Override
     public int size(String tableName) {
       return getTableMap(tableName).size();
     }
diff --git a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/PartitionedDatabase.java b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/PartitionedDatabase.java
index cb4ddfc..ad049e6 100644
--- a/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/PartitionedDatabase.java
+++ b/core/store/dist/src/main/java/org/onosproject/store/consistent/impl/PartitionedDatabase.java
@@ -78,6 +78,17 @@
     }
 
     @Override
+    public CompletableFuture<Set<String>> tableNames() {
+        checkState(isOpen.get(), DB_NOT_OPEN);
+        Set<String> tableNames = Sets.newConcurrentHashSet();
+        return CompletableFuture.allOf(partitions
+                .stream()
+                .map(db -> db.tableNames().thenApply(tableNames::addAll))
+                .toArray(CompletableFuture[]::new))
+            .thenApply(v -> tableNames);
+    }
+
+    @Override
     public CompletableFuture<Integer> size(String tableName) {
         checkState(isOpen.get(), DB_NOT_OPEN);
         AtomicInteger totalSize = new AtomicInteger(0);