Add CLI for viewing FPM connections.

Change-Id: I7e9e320b662a826cd2c0d49477b45110094d8e79
diff --git a/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmMessageListener.java b/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmInfoService.java
similarity index 71%
rename from apps/routing/src/main/java/org/onosproject/routing/fpm/FpmMessageListener.java
rename to apps/routing/src/main/java/org/onosproject/routing/fpm/FpmInfoService.java
index db8a6cd..39335e5 100644
--- a/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmMessageListener.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmInfoService.java
@@ -16,17 +16,13 @@
 
 package org.onosproject.routing.fpm;
 
-import org.onosproject.routing.fpm.protocol.FpmHeader;
+import java.net.SocketAddress;
+import java.util.Map;
 
 /**
- * Listener for FPM messages.
+ * Created by jono on 2/2/16.
  */
-public interface FpmMessageListener {
+public interface FpmInfoService {
 
-    /**
-     * Handles an FPM message.
-     *
-     * @param fpmMessage FPM message
-     */
-    void fpmMessage(FpmHeader fpmMessage);
+    Map<SocketAddress, Long> peers();
 }
diff --git a/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmMessageListener.java b/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmListener.java
similarity index 60%
copy from apps/routing/src/main/java/org/onosproject/routing/fpm/FpmMessageListener.java
copy to apps/routing/src/main/java/org/onosproject/routing/fpm/FpmListener.java
index db8a6cd..0a96a9f 100644
--- a/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmMessageListener.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmListener.java
@@ -18,10 +18,12 @@
 
 import org.onosproject.routing.fpm.protocol.FpmHeader;
 
+import java.net.SocketAddress;
+
 /**
- * Listener for FPM messages.
+ * Listener for events from the route source.
  */
-public interface FpmMessageListener {
+public interface FpmListener {
 
     /**
      * Handles an FPM message.
@@ -29,4 +31,19 @@
      * @param fpmMessage FPM message
      */
     void fpmMessage(FpmHeader fpmMessage);
+
+    /**
+     * Signifies that a new peer has attempted to initiate an FPM connection.
+     *
+     * @param address remote address of the peer
+     * @return true if the connection should be admitted, otherwise false
+     */
+    boolean peerConnected(SocketAddress address);
+
+    /**
+     * Signifies that an FPM connection has been disconnected.
+     *
+     * @param address remote address of the peer
+     */
+    void peerDisconnected(SocketAddress address);
 }
diff --git a/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmManager.java b/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmManager.java
index efda592..914a3d9 100644
--- a/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmManager.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmManager.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.routing.fpm;
 
+import com.google.common.collect.ImmutableMap;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.Deactivate;
@@ -46,6 +47,7 @@
 import org.slf4j.LoggerFactory;
 
 import java.net.InetSocketAddress;
+import java.net.SocketAddress;
 import java.util.Collections;
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -58,13 +60,15 @@
  */
 @Service
 @Component(immediate = true, enabled = false)
-public class FpmManager implements RouteSourceService {
+public class FpmManager implements RouteSourceService, FpmInfoService {
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     private ServerBootstrap serverBootstrap;
     private Channel serverChannel;
     private ChannelGroup allChannels = new DefaultChannelGroup();
 
+    private Map<SocketAddress, Long> peers = new ConcurrentHashMap<>();
+
     private Map<IpPrefix, RouteEntry> fpmRoutes = new ConcurrentHashMap<>();
 
     private RouteListener routeListener;
@@ -209,11 +213,31 @@
         routeListener.update(Collections.singletonList(routeUpdate));
     }
 
-    private class InternalFpmListener implements FpmMessageListener {
+    @Override
+    public Map<SocketAddress, Long> peers() {
+        return ImmutableMap.copyOf(peers);
+    }
+
+    private class InternalFpmListener implements FpmListener {
         @Override
         public void fpmMessage(FpmHeader fpmMessage) {
             FpmManager.this.fpmMessage(fpmMessage);
         }
+
+        @Override
+        public boolean peerConnected(SocketAddress address) {
+            if (peers.keySet().contains(address)) {
+                return false;
+            }
+
+            peers.put(address, System.currentTimeMillis());
+            return true;
+        }
+
+        @Override
+        public void peerDisconnected(SocketAddress address) {
+            peers.remove(address);
+        }
     }
 
 }
diff --git a/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmSessionHandler.java b/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmSessionHandler.java
index 95ce760..5fd2533 100644
--- a/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmSessionHandler.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmSessionHandler.java
@@ -35,7 +35,7 @@
 
     private static Logger log = LoggerFactory.getLogger(FpmSessionHandler.class);
 
-    private final FpmMessageListener fpmListener;
+    private final FpmListener fpmListener;
 
     private Channel channel;
 
@@ -44,7 +44,7 @@
      *
      * @param fpmListener listener for FPM messages
      */
-    public FpmSessionHandler(FpmMessageListener fpmListener) {
+    public FpmSessionHandler(FpmListener fpmListener) {
         this.fpmListener = checkNotNull(fpmListener);
     }
 
@@ -59,26 +59,27 @@
     public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e)
             throws Exception {
         log.error("Exception thrown while handling FPM message", e.getCause());
-        channel.close();
+        if (channel != null) {
+            channel.close();
+        }
         handleDisconnect();
     }
 
     @Override
     public void channelOpen(ChannelHandlerContext ctx, ChannelStateEvent e)
             throws Exception {
-        if (this.channel != null) {
+        if (!fpmListener.peerConnected(ctx.getChannel().getRemoteAddress())) {
             log.error("Received new FPM connection while already connected");
             ctx.getChannel().close();
             return;
         }
 
-        this.channel = ctx.getChannel();
+        channel = ctx.getChannel();
     }
 
     @Override
     public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e)
             throws Exception {
-        super.channelConnected(ctx, e);
     }
 
     @Override
@@ -94,6 +95,7 @@
     }
 
     private void handleDisconnect() {
-        this.channel = null;
+        fpmListener.peerDisconnected(channel.getRemoteAddress());
+        channel = null;
     }
 }
diff --git a/apps/routing/src/main/java/org/onosproject/routing/fpm/cli/FpmConnectionsList.java b/apps/routing/src/main/java/org/onosproject/routing/fpm/cli/FpmConnectionsList.java
new file mode 100644
index 0000000..9917a3f
--- /dev/null
+++ b/apps/routing/src/main/java/org/onosproject/routing/fpm/cli/FpmConnectionsList.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2016 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.routing.fpm.cli;
+
+import org.apache.karaf.shell.commands.Command;
+import org.onlab.util.Tools;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.routing.fpm.FpmInfoService;
+
+import java.net.InetSocketAddress;
+
+/**
+ * Displays the current FPM connections.
+ */
+@Command(scope = "onos", name = "fpm-connections",
+        description = "Displays the current FPM connections")
+public class FpmConnectionsList extends AbstractShellCommand {
+
+    private static final String FORMAT = "%s:%s connected since %s";
+
+    @Override
+    protected void execute() {
+        FpmInfoService fpmInfo = get(FpmInfoService.class);
+
+        fpmInfo.peers().forEach((socketAddress, timestamp) -> {
+            if (socketAddress instanceof InetSocketAddress) {
+                InetSocketAddress inet = (InetSocketAddress) socketAddress;
+
+                print(FORMAT, inet.getHostString(), inet.getPort(), Tools.timeAgo(timestamp));
+            } else {
+                print("Unknown data format");
+            }
+        });
+    }
+}
diff --git a/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmMessageListener.java b/apps/routing/src/main/java/org/onosproject/routing/fpm/cli/package-info.java
similarity index 67%
copy from apps/routing/src/main/java/org/onosproject/routing/fpm/FpmMessageListener.java
copy to apps/routing/src/main/java/org/onosproject/routing/fpm/cli/package-info.java
index db8a6cd..f6ea39a 100644
--- a/apps/routing/src/main/java/org/onosproject/routing/fpm/FpmMessageListener.java
+++ b/apps/routing/src/main/java/org/onosproject/routing/fpm/cli/package-info.java
@@ -14,19 +14,7 @@
  * limitations under the License.
  */
 
-package org.onosproject.routing.fpm;
-
-import org.onosproject.routing.fpm.protocol.FpmHeader;
-
 /**
- * Listener for FPM messages.
+ * FPM-related CLI commands.
  */
-public interface FpmMessageListener {
-
-    /**
-     * Handles an FPM message.
-     *
-     * @param fpmMessage FPM message
-     */
-    void fpmMessage(FpmHeader fpmMessage);
-}
+package org.onosproject.routing.fpm.cli;
diff --git a/apps/routing/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/routing/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index ab9c620..cb75124 100644
--- a/apps/routing/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/apps/routing/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -49,5 +49,8 @@
     <command>
       <action class="org.onosproject.routing.cli.RemovePeerCommand"/>
     </command>
+    <command>
+      <action class="org.onosproject.routing.fpm.cli.FpmConnectionsList"/>
+    </command>
   </command-bundle>
 </blueprint>