Added Topology WebSocket server implementation.
It allows remote applications (e.g., the GUI) to subscribe
for Topology-related events.
The URL is:
ws://hostname:8081/ws/onos/topology

When a new client opens a socket, the server transmits the whole topology.
From that moment on, the server sends topology events (deltas) if there
are any changes in the topology. Currently, all objects are encoded in JSON.

The default WebSocket port number is set to 8081
(configurable in conf/onos.properties)

NOTE: In the current implementation, the initial fetching of the topology,
and adding topology events for transmission are done by two different threads.
Hence, when a new client opens a socket, there is a small window of time
when some of the added events are "moving back in time".
The final result of applying all events will be technically correct, but
those "moving back in time" events are semantically incorrect.
This issue will be addressed in later iterations / refactoring of the
implementation.

Change-Id: Id2cbf72d1384208201d916e0aa1e5926659878ee
diff --git a/src/main/java/net/onrc/onos/apps/websocket/WebSocketManager.java b/src/main/java/net/onrc/onos/apps/websocket/WebSocketManager.java
new file mode 100644
index 0000000..9cddfae
--- /dev/null
+++ b/src/main/java/net/onrc/onos/apps/websocket/WebSocketManager.java
@@ -0,0 +1,100 @@
+package net.onrc.onos.apps.websocket;
+
+import javax.websocket.DeploymentException;
+import javax.websocket.server.ServerContainer;
+
+import net.onrc.onos.core.topology.ITopologyService;
+
+import org.eclipse.jetty.server.Server;
+import org.eclipse.jetty.servlet.ServletContextHandler;
+import org.eclipse.jetty.websocket.jsr356.server.deploy.WebSocketServerContainerInitializer;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+/**
+ * The WebSocket manager class.
+ * There is a single instance for all WebSocket endpoints.
+ */
+class WebSocketManager {
+    protected static ITopologyService topologyService;
+
+    private static final Logger log =
+        LoggerFactory.getLogger(WebSocketManager.class);
+    private int webSocketPort;
+    private JettyServer jettyServer;
+
+    /**
+     * Constructor.
+     *
+     * @param topologyService the Topology Service to use.
+     * @param webSocketPort the WebSocket port to use.
+     */
+    @SuppressFBWarnings(value = "ST_WRITE_TO_STATIC_FROM_INSTANCE_METHOD",
+                        justification = "The writing to WebSocketManager.topologyService happens only once on startup")
+    WebSocketManager(ITopologyService topologyService,
+                     int webSocketPort) {
+        WebSocketManager.topologyService = topologyService;
+        this.webSocketPort = webSocketPort;
+    }
+
+    /**
+     * Startup processing.
+     */
+    void startup() {
+        log.debug("Starting WebSocket server on port {}", webSocketPort);
+
+        jettyServer = new JettyServer(webSocketPort);
+        this.jettyServer.start();
+    }
+
+    /**
+     * Class for creating the WebSocket server and associated state.
+     */
+    static class JettyServer extends Thread {
+        private Server server;
+        private ServletContextHandler context;
+        private ServerContainer container;
+
+        /**
+         * Constructor.
+         *
+         * @param port the port to listen on.
+         */
+        JettyServer(final int port) {
+            server = new Server(port);
+
+            // Initialize the context handler
+            context = new ServletContextHandler(ServletContextHandler.SESSIONS);
+            context.setContextPath("/ws/onos");
+            server.setHandler(context);
+
+            // Initialize the WebSocket layer
+            container =
+                WebSocketServerContainerInitializer.configureContext(context);
+            try {
+                container.addEndpoint(TopologyWebSocket.class);
+            } catch (DeploymentException e) {
+                log.debug("Exception adding WebSocket endpoint: ", e);
+            }
+        }
+
+        /**
+         * Run the thread.
+         */
+        @Override
+        public void run() {
+            try {
+                this.server.start();
+            } catch (final Exception e) {
+                log.debug("Exception starting the WebSocket server: ", e);
+            }
+            try {
+                this.server.join();
+            } catch (final InterruptedException e) {
+                log.debug("Exception joining the WebSocket server: ", e);
+            }
+        }
+    }
+}