ONOS-1479 - GUI Topology Overlay Work - (WIP)
- UiExtension now uses Builder Pattern; added topology overlay factory.
- Refactored UiExtensionTest (and other classes) to use builder.
- Created UiTopoOverlayFactory, UiTopoOverlay, and TopoOverlayCache.
- Started implementation of TrafficOverlay.
- Inject TopoOverlayCache into TopologyViewMessageHandler; added TopoSelectOverlay request handler.
- Modified UiExtensionManager to create traffic overlay.
- Augmented UiWebSocket to create overlays on demand, and inject overlay cache into topo view message handler.
- added client side wiring to switch overlays.

Change-Id: I6f99596aefb3b87382517ce929d268a2447545ee
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TopoOverlayCache.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopoOverlayCache.java
new file mode 100644
index 0000000..2490041
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopoOverlayCache.java
@@ -0,0 +1,78 @@
+/*
+ * 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.ui.impl;
+
+import org.onosproject.ui.UiTopoOverlay;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A cache of {@link org.onosproject.ui.UiTopoOverlay}'s that were registered
+ * at the time the UI connection was established.
+ */
+public class TopoOverlayCache {
+
+    private final Map<String, UiTopoOverlay> overlays = new HashMap<>();
+
+    /**
+     * Adds a topology overlay to the cache.
+     *
+     * @param overlay   a topology overlay
+     */
+    public void add(UiTopoOverlay overlay) {
+        overlays.put(overlay.id(), overlay);
+    }
+
+    /**
+     * Invoked when the cache is no longer needed.
+     */
+    public void destroy() {
+        overlays.clear();
+    }
+
+    /**
+     * Switching currently selected overlay.
+     *
+     * @param deact identity of overlay to deactivate
+     * @param act identity of overlay to activate
+     */
+    public void switchOverlay(String deact, String act) {
+        UiTopoOverlay toDeactivate = getOverlay(deact);
+        UiTopoOverlay toActivate = getOverlay(act);
+        if (toDeactivate != null) {
+            toDeactivate.deactivate();
+        }
+        if (toActivate != null) {
+            toActivate.activate();
+        }
+    }
+
+    private UiTopoOverlay getOverlay(String id) {
+        return id == null ? null : overlays.get(id);
+    }
+
+    /**
+     * Returns the number of overlays in the cache.
+     *
+     * @return number of overlays
+     */
+    public int size() {
+        return overlays.size();
+    }
+}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
index a10e016..6d2295a 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TopologyViewMessageHandler.java
@@ -103,6 +103,7 @@
     private static final String SPRITE_DATA_REQ = "spriteDataRequest";
     private static final String TOPO_START = "topoStart";
     private static final String TOPO_HEARTBEAT = "topoHeartbeat";
+    private static final String TOPO_SELECT_OVERLAY = "topoSelectOverlay";
     private static final String TOPO_STOP = "topoStop";
 
 
@@ -135,6 +136,8 @@
     private final ExecutorService msgSender =
             newSingleThreadExecutor(groupedThreads("onos/gui", "msg-sender"));
 
+    private TopoOverlayCache overlayCache;
+
     private TimerTask trafficTask = null;
     private TrafficEvent trafficEvent = null;
 
@@ -172,6 +175,7 @@
         return ImmutableSet.of(
                 new TopoStart(),
                 new TopoHeartbeat(),
+                new TopoSelectOverlay(),
                 new TopoStop(),
                 new ReqSummary(),
                 new CancelSummary(),
@@ -195,6 +199,15 @@
         );
     }
 
+    /**
+     * Injects the topology overlay cache.
+     *
+     * @param overlayCache injected cache
+     */
+    void setOverlayCache(TopoOverlayCache overlayCache) {
+        this.overlayCache = overlayCache;
+    }
+
     // ==================================================================
 
     /**
@@ -231,6 +244,19 @@
         }
     }
 
+    private final class TopoSelectOverlay extends RequestHandler {
+        private TopoSelectOverlay() {
+            super(TOPO_SELECT_OVERLAY);
+        }
+
+        @Override
+        public void process(long sid, ObjectNode payload) {
+            String deact = string(payload, "deactivate");
+            String act = string(payload, "activate");
+            overlayCache.switchOverlay(deact, act);
+        }
+    }
+
     /**
      * @deprecated in Cardinal Release
      */
@@ -311,7 +337,7 @@
         @Override
         public void process(long sid, ObjectNode payload) {
             String type = string(payload, "class", "unknown");
-            String id = JsonUtils.string(payload, "id");
+            String id = string(payload, "id");
 
             if (type.equals("device")) {
                 sendMessage(deviceDetails(deviceId(id), sid));
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/TrafficOverlay.java b/web/gui/src/main/java/org/onosproject/ui/impl/TrafficOverlay.java
new file mode 100644
index 0000000..3c09bb4
--- /dev/null
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/TrafficOverlay.java
@@ -0,0 +1,36 @@
+/*
+ * 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.ui.impl;
+
+import org.onosproject.ui.UiTopoOverlay;
+
+/**
+ * Topology Overlay for network traffic.
+ */
+public class TrafficOverlay extends UiTopoOverlay {
+    private static final String TRAFFIC_ID = "traffic";
+
+    /**
+     * Constructs the traffic overlay.
+     */
+    public TrafficOverlay() {
+        super(TRAFFIC_ID);
+    }
+
+    // TODO : override init(), activate(), deactivate(), destroy()
+}
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java b/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
index 5706734..c3c9538 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/UiExtensionManager.java
@@ -30,6 +30,7 @@
 import org.onosproject.ui.UiExtension;
 import org.onosproject.ui.UiExtensionService;
 import org.onosproject.ui.UiMessageHandlerFactory;
+import org.onosproject.ui.UiTopoOverlayFactory;
 import org.onosproject.ui.UiView;
 import org.onosproject.ui.UiViewHidden;
 import org.onosproject.ui.impl.topo.OverlayService;
@@ -54,6 +55,10 @@
 public class UiExtensionManager
         implements UiExtensionService, SpriteService, OverlayService {
 
+    private static final ClassLoader CL =
+            UiExtensionManager.class.getClassLoader();
+    private static final String CORE = "core";
+
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     // List of all extensions
@@ -104,8 +109,16 @@
                         new ClusterViewMessageHandler()
                 );
 
-        return new UiExtension(coreViews, messageHandlerFactory, "core",
-                               UiExtensionManager.class.getClassLoader());
+        UiTopoOverlayFactory topoOverlayFactory =
+                () -> ImmutableList.of(
+                        new TrafficOverlay()
+                );
+
+        return new UiExtension.Builder(CL, coreViews)
+                .messageHandlerFactory(messageHandlerFactory)
+                .topoOverlayFactory(topoOverlayFactory)
+                .resourcePath(CORE)
+                .build();
     }
 
     @Activate
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
index b80c11f..761c2a3 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocket.java
@@ -27,6 +27,7 @@
 import org.onosproject.ui.UiExtensionService;
 import org.onosproject.ui.UiMessageHandlerFactory;
 import org.onosproject.ui.UiMessageHandler;
+import org.onosproject.ui.UiTopoOverlayFactory;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -58,6 +59,7 @@
     private long lastActive = System.currentTimeMillis();
 
     private Map<String, UiMessageHandler> handlers;
+    private TopoOverlayCache overlayCache;
 
     /**
      * Creates a new web-socket for serving data to GUI.
@@ -72,7 +74,7 @@
      * Issues a close on the connection.
      */
     synchronized void close() {
-        destroyHandlers();
+        destroyHandlersAndOverlays();
         if (connection.isOpen()) {
             connection.close();
         }
@@ -104,7 +106,7 @@
         this.connection = connection;
         this.control = (FrameConnection) connection;
         try {
-            createHandlers();
+            createHandlersAndOverlays();
             sendInstanceData();
             log.info("GUI client connected");
 
@@ -118,7 +120,7 @@
 
     @Override
     public synchronized void onClose(int closeCode, String message) {
-        destroyHandlers();
+        destroyHandlersAndOverlays();
         log.info("GUI client disconnected [close-code={}, message={}]",
                  closeCode, message);
     }
@@ -131,6 +133,7 @@
 
     @Override
     public void onMessage(String data) {
+        log.debug("onMessage: {}", data);
         lastActive = System.currentTimeMillis();
         try {
             ObjectNode message = (ObjectNode) mapper.reader().readTree(data);
@@ -172,8 +175,11 @@
     }
 
     // Creates new message handlers.
-    private synchronized void createHandlers() {
+    private synchronized void createHandlersAndOverlays() {
+        log.debug("creating handlers and overlays...");
         handlers = new HashMap<>();
+        overlayCache = new TopoOverlayCache();
+
         UiExtensionService service = directory.get(UiExtensionService.class);
         service.getExtensions().forEach(ext -> {
             UiMessageHandlerFactory factory = ext.messageHandlerFactory();
@@ -181,15 +187,33 @@
                 factory.newHandlers().forEach(handler -> {
                     handler.init(this, directory);
                     handler.messageTypes().forEach(type -> handlers.put(type, handler));
+
+                    // need to inject the overlay cache into topology message handler
+                    if (handler instanceof TopologyViewMessageHandler) {
+                        ((TopologyViewMessageHandler) handler).setOverlayCache(overlayCache);
+                    }
                 });
             }
+
+            UiTopoOverlayFactory overlayFactory = ext.topoOverlayFactory();
+            if (overlayFactory != null) {
+                overlayFactory.newOverlays().forEach(overlayCache::add);
+            }
         });
+        log.debug("#handlers = {}, #overlays = {}", handlers.size(),
+                  overlayCache.size());
     }
 
     // Destroys message handlers.
-    private synchronized void destroyHandlers() {
+    private synchronized void destroyHandlersAndOverlays() {
+        log.debug("destroying handlers and overlays...");
         handlers.forEach((type, handler) -> handler.destroy());
         handlers.clear();
+
+        if (overlayCache != null) {
+            overlayCache.destroy();
+            overlayCache = null;
+        }
     }
 
     // Sends cluster node/instance information to allow GUI to fail-over.