Pushing UiModelEvents up to the client...

Change-Id: Ic94c84b96df18bdd2c4072a5209f89bada8ab36f
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 2815d80..96ebadb 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
@@ -30,6 +30,7 @@
 import org.onosproject.ui.UiMessageHandlerFactory;
 import org.onosproject.ui.UiTopoLayoutService;
 import org.onosproject.ui.UiTopoOverlayFactory;
+import org.onosproject.ui.impl.topo.Topo2Jsonifier;
 import org.onosproject.ui.impl.topo.UiTopoSession;
 import org.onosproject.ui.impl.topo.model.UiSharedTopologyModel;
 import org.onosproject.ui.model.topo.UiTopoLayout;
@@ -90,9 +91,11 @@
     public UiWebSocket(ServiceDirectory directory, String userName) {
         this.directory = directory;
         this.userName = userName;
-        this.topoSession =
-                new UiTopoSession(this, directory.get(UiSharedTopologyModel.class),
-                        directory.get(UiTopoLayoutService.class));
+
+        Topo2Jsonifier t2json = new Topo2Jsonifier(directory);
+        UiSharedTopologyModel sharedModel = directory.get(UiSharedTopologyModel.class);
+        UiTopoLayoutService layoutService = directory.get(UiTopoLayoutService.class);
+        this.topoSession = new UiTopoSession(this, t2json, sharedModel, layoutService);
     }
 
     @Override
@@ -252,6 +255,7 @@
                         handler.messageTypes().forEach(type -> handlers.put(type, handler));
 
                         // need to inject the overlay cache into topology message handler
+                        // TODO: code for Topo2ViewMessageHandler required here
                         if (handler instanceof TopologyViewMessageHandler) {
                             ((TopologyViewMessageHandler) handler).setOverlayCache(overlayCache);
                         }
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
index c9e29b9..ebc20fe 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/Topo2Jsonifier.java
@@ -70,7 +70,7 @@
  * Facility for creating JSON messages to send to the topology view in the
  * Web client.
  */
-class Topo2Jsonifier {
+public class Topo2Jsonifier {
 
     private static final String E_DEF_NOT_LAST =
             "UiNode.LAYER_DEFAULT not last in layer list";
@@ -113,7 +113,7 @@
      *
      * @param directory service directory
      */
-    Topo2Jsonifier(ServiceDirectory directory) {
+    public Topo2Jsonifier(ServiceDirectory directory) {
         this.directory = checkNotNull(directory, "Directory cannot be null");
 
         clusterService = directory.get(ClusterService.class);
@@ -266,7 +266,13 @@
         return result;
     }
 
-    private ObjectNode jsonEvent(UiModelEvent modelEvent) {
+    /**
+     * Creates a JSON representation of a UI model event.
+     *
+     * @param modelEvent the source model event
+     * @return a JSON representation of that event
+     */
+    public ObjectNode jsonEvent(UiModelEvent modelEvent) {
         ObjectNode payload = objectNode();
         payload.put(TYPE, enumToString(modelEvent.type()));
         payload.put(SUBJECT, modelEvent.subject().idAsString());
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java b/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
index 2b27347..79e262b 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/topo/UiTopoSession.java
@@ -16,6 +16,7 @@
 
 package org.onosproject.ui.impl.topo;
 
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.onosproject.net.region.RegionId;
 import org.onosproject.ui.UiTopoLayoutService;
 import org.onosproject.ui.impl.UiWebSocket;
@@ -51,10 +52,13 @@
  */
 public class UiTopoSession implements UiModelListener {
 
+    private static final String TOPO2_UI_MODEL_EVENT = "topo2UiModelEvent";
+
     private final Logger log = LoggerFactory.getLogger(getClass());
 
     private final UiWebSocket webSocket;
     private final String username;
+    private final Topo2Jsonifier t2json;
 
     final UiSharedTopologyModel sharedModel;
 
@@ -65,17 +69,21 @@
     private boolean messagesEnabled;
 
     /**
-     * Creates a new topology session for the specified web socket connection.
+     * Creates a new topology session for the specified web socket connection,
+     * and references to JSONifier, shared model, and layout service.
      *
      * @param webSocket     web socket
+     * @param jsonifier     JSONifier instance
      * @param model         share topology model
      * @param layoutService topology layout service
      */
     public UiTopoSession(UiWebSocket webSocket,
+                         Topo2Jsonifier jsonifier,
                          UiSharedTopologyModel model,
                          UiTopoLayoutService layoutService) {
         this.webSocket = webSocket;
         this.username = webSocket.userName();
+        this.t2json = jsonifier;
         this.sharedModel = model;
         this.layoutService = layoutService;
     }
@@ -84,6 +92,7 @@
     UiTopoSession() {
         webSocket = null;
         username = null;
+        t2json = null;
         sharedModel = null;
     }
 
@@ -122,7 +131,15 @@
     @Override
     public void event(UiModelEvent event) {
         log.debug("Event received: {}", event);
-        // TODO: handle model events from the cache...
+        ObjectNode payload = t2json.jsonEvent(event);
+
+        // TODO: add filtering for relevant objects only...
+        // TO Decide: Since the session holds the state of what is being
+        //   displayed on the client, we should filter out any model events
+        //   that are not relevant, and only send up events for objects that
+        //   are currently being viewed by the user.
+
+        webSocket.sendMessage(TOPO2_UI_MODEL_EVENT, 0, payload);
     }
 
     /**
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Event.js b/web/gui/src/main/webapp/app/view/topo2/topo2Event.js
index 705173c..138d866 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Event.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Event.js
@@ -42,7 +42,9 @@
             topo2CurrentLayout: t2fs,
             topo2CurrentRegion: t2fs,
             topo2PeerRegions: t2fs,
-            topo2StartDone: t2fs
+            topo2StartDone: t2fs,
+
+            topo2UiModelEvent: t2fs
 
             // Add further event names / module references as needed
         };
diff --git a/web/gui/src/main/webapp/app/view/topo2/topo2Force.js b/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
index 852e0a5..57a5f89 100644
--- a/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
+++ b/web/gui/src/main/webapp/app/view/topo2/topo2Force.js
@@ -146,6 +146,14 @@
         $log.debug('>> topo2StartDone event:', data);
     }
 
+    function modelEvent(data) {
+        $log.debug('>> topo2UiModelEvent event:', data);
+        // TODO: Interpret the event and update our topo model state (if needed)
+        // To Decide: Can we assume that the server will only send events
+        //    related to objects that we are currently showing?
+        //    (e.g. filtered by subregion contents?)
+    }
+
     function showMastership(masterId) {
         if (masterId) {
             showMastershipFor(masterId);
@@ -264,6 +272,8 @@
                 topo2CurrentRegion: currentRegion,
                 topo2StartDone: startDone,
 
+                topo2UiModelEvent: modelEvent,
+
                 showMastership: showMastership,
                 topo2PeerRegions: topo2PeerRegions,