Adding ability for the server to notify clients about GUI additions/removals.

Change-Id: I505f68c33cb9cf7b875b53792f8442ba0cf0662a
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 9bfa3e3..3bf6c27 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
@@ -56,9 +56,10 @@
 @Service
 public class UiExtensionManager implements UiExtensionService, SpriteService {
 
-    private static final ClassLoader CL =
-            UiExtensionManager.class.getClassLoader();
+    private static final ClassLoader CL = UiExtensionManager.class.getClassLoader();
     private static final String CORE = "core";
+    private static final String GUI_ADDED = "guiAdded";
+    private static final String GUI_REMOVED = "guiRemoved";
 
     private final Logger log = LoggerFactory.getLogger(getClass());
 
@@ -146,6 +147,7 @@
             for (UiView view : extension.views()) {
                 views.put(view.id(), extension);
             }
+            UiWebSocketServlet.sendToAll(GUI_ADDED, null);
         }
     }
 
@@ -153,8 +155,8 @@
     public synchronized void unregister(UiExtension extension) {
         checkPermission(UI_WRITE);
         extensions.remove(extension);
-        extension.views().stream()
-                .map(UiView::id).collect(toSet()).forEach(views::remove);
+        extension.views().stream().map(UiView::id).collect(toSet()).forEach(views::remove);
+        UiWebSocketServlet.sendToAll(GUI_REMOVED, null);
     }
 
     @Override
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 449cdec..d2d6705 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
@@ -170,9 +170,8 @@
         if (sid > 0) {
             message.put("sid", sid);
         }
-        message.set("payload", payload);
+        message.set("payload", payload != null ? payload : mapper.createObjectNode());
         sendMessage(message);
-
     }
 
     // Creates new message handlers.
diff --git a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocketServlet.java b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocketServlet.java
index ffc558d..564b07f 100644
--- a/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocketServlet.java
+++ b/web/gui/src/main/java/org/onosproject/ui/impl/UiWebSocketServlet.java
@@ -15,6 +15,7 @@
  */
 package org.onosproject.ui.impl;
 
+import com.fasterxml.jackson.databind.node.ObjectNode;
 import org.eclipse.jetty.websocket.WebSocket;
 import org.eclipse.jetty.websocket.WebSocketServlet;
 import org.onlab.osgi.DefaultServiceDirectory;
@@ -76,6 +77,18 @@
         return socket;
     }
 
+    /**
+     * Sends the specified message to all the GUI clients.
+     *
+     * @param type    message type
+     * @param payload message payload
+     */
+    static void sendToAll(String type, ObjectNode payload) {
+        if (instance != null) {
+            instance.sockets.forEach(ws -> ws.sendMessage(type, 0, payload));
+        }
+    }
+
     // Task for pruning web-sockets that are idle.
     private class Pruner extends TimerTask {
         @Override
diff --git a/web/gui/src/main/webapp/app/fw/mast/mast.js b/web/gui/src/main/webapp/app/fw/mast/mast.js
index 1cde58c..50cfe9d 100644
--- a/web/gui/src/main/webapp/app/fw/mast/mast.js
+++ b/web/gui/src/main/webapp/app/fw/mast/mast.js
@@ -27,8 +27,16 @@
     var mastHeight = 36,
         padMobile = 16;
 
-    angular.module('onosMast', ['onosNav'])
-        .controller('MastCtrl', ['$log', 'NavService', function (_$log_, ns) {
+    var dialogId = 'app-dialog',
+        dialogOpts = {
+            edge: 'left'
+        };
+
+        angular.module('onosMast', ['onosNav'])
+        .controller('MastCtrl', ['$log', '$window', 'NavService',
+                                    'DialogService', 'WebSocketService',
+
+        function (_$log_, $window, ns, ds, wss) {
             var self = this;
 
             $log = _$log_;
@@ -36,6 +44,36 @@
             // initialize mast controller here...
             self.radio = null;
 
+            function triggerRefresh(action) {
+                function createConfirmationText() {
+                    var content = ds.createDiv();
+                    content.append('p').text(action + ' Press OK to update the GUI.');
+                    return content;
+                }
+
+
+                function dOk() {
+                    $log.debug('Refreshing GUI');
+                    $window.location.reload();
+                }
+
+                function dCancel() {
+                    $log.debug('Canceling GUI refresh');
+                }
+
+                ds.openDialog(dialogId, dialogOpts)
+                    .setTitle('Confirm GUI Refresh')
+                    .addContent(createConfirmationText())
+                    .addOk(dOk)
+                    .addCancel(dCancel)
+                    .bindKeys();
+            }
+
+            wss.bindHandlers({
+                'guiAdded': function () { triggerRefresh('New GUI components were added.') },
+                'guiRemoved': function () { triggerRefresh('Some GUI components were removed.') }
+            });
+
             // delegate to NavService
             self.toggleNav = function () {
                 ns.toggleNav();