GUI -- Cleaned up websocket code.
- isolated new WebSocket() call, so we can mock.
Change-Id: Id1225e2c65732e750b289224e838a326c79f02a4
diff --git a/web/gui/src/main/webapp/app/fw/remote/websocket.js b/web/gui/src/main/webapp/app/fw/remote/websocket.js
index 3229250..6445d36 100644
--- a/web/gui/src/main/webapp/app/fw/remote/websocket.js
+++ b/web/gui/src/main/webapp/app/fw/remote/websocket.js
@@ -21,32 +21,116 @@
'use strict';
// injected refs
- var fs, $log;
+ var $log, $loc, fs, ufs, wsock, vs;
// internal state
- var ws, sws, sid = 0,
- handlers = {};
+ var ws = null, // web socket reference
+ wsUp = false, // web socket is good to go
+ sid = 0, // event sequence identifier
+ handlers = {}, // event handler bindings
+ pendingEvents = [], // events TX'd while socket not up
+ url; // web socket URL
+
+ // ==========================
+ // === Web socket callbacks
+
+ function handleOpen() {
+ $log.info('Web socket open');
+ $log.debug('Sending ' + pendingEvents.length + ' pending event(s)...');
+ pendingEvents.forEach(function (ev) {
+ _send(ev);
+ });
+ pendingEvents = [];
+ wsUp = true;
+ }
+
+ // Handles the specified (incoming) message using handler bindings.
+ function handleMessage(msgEvent) {
+ var ev, h;
+
+ try {
+ ev = JSON.parse(msgEvent.data);
+ $log.debug(' *Rx* >> ', ev.event, ev.payload);
+
+ if (h = handlers[ev.event]) {
+ h(ev.payload);
+ } else {
+ $log.warn('Unhandled event:', ev);
+ }
+
+ } catch (e) {
+ $log.error('Message.data is (probably) not valid JSON', msgEvent);
+ }
+ }
+
+ function handleClose() {
+ $log.info('Web socket closed');
+ wsUp = false;
+
+ // FIXME: implement controller failover logic
+
+ // If no controllers left to contact, show the Veil...
+ vs.show([
+ 'Oops!',
+ 'Web-socket connection to server closed...',
+ 'Try refreshing the page.'
+ ]);
+ }
+
+
+ // ==============================
+ // === Private Helper Functions
+
+ function _send(ev) {
+ $log.debug(' *Tx* >> ', ev.event, ev.payload);
+ ws.send(JSON.stringify(ev));
+ }
+
+
+ // ===================
+ // === API Functions
+
+ // Required for unit tests to set to known state
function resetSid() {
sid = 0;
}
+ // Currently supported opts:
+ // wsport: web socket port (other than default 8181)
+ function createWebSocket(opts) {
+ var wsport = (opts && opts.wsport) || null;
+
+ url = ufs.wsUrl('core', wsport);
+
+ $log.debug('Attempting to open websocket to: ' + url);
+ ws = wsock.newWebSocket(url);
+ if (ws) {
+ ws.onopen = handleOpen;
+ ws.onmessage = handleMessage;
+ ws.onclose = handleClose;
+ }
+ // Note: Wsock logs an error if the new WebSocket call fails
+ }
+
// Binds the specified message handlers.
+ // keys are the event IDs
+ // values are the API on which the handler function is a property
function bindHandlers(handlerMap) {
var m = d3.map(handlerMap),
dups = [];
- m.forEach(function (key, value) {
- var fn = fs.isF(value[key]);
+ m.forEach(function (eventId, api) {
+ var fn = fs.isF(api[eventId]);
if (!fn) {
- $log.warn(key + ' binding not a function on ' + value);
+ $log.warn(eventId + ' handler not a function');
return;
}
- if (handlers[key]) {
- dups.push(key);
+ if (handlers[eventId]) {
+ dups.push(eventId);
} else {
- handlers[key] = fn;
+ handlers[eventId] = fn;
}
});
if (dups.length) {
@@ -55,116 +139,46 @@
}
// Unbinds the specified message handlers.
+ // Expected that the same map will be used, but we only care about keys
function unbindHandlers(handlerMap) {
var m = d3.map(handlerMap);
- m.forEach(function (key) {
- delete handlers[key];
+
+ m.forEach(function (eventId) {
+ delete handlers[eventId];
});
}
- // Formulates an event message and sends it via the shared web-socket.
+ // Formulates an event message and sends it via the web-socket.
+ // If the websocket is not up yet, we store it in a pending list.
function sendEvent(evType, payload) {
- var p = payload || {};
- if (sws) {
- $log.debug(' *Tx* >> ', evType, payload);
- sws.send({
+ var ev = {
event: evType,
sid: ++sid,
- payload: p
- });
+ payload: payload || {}
+ };
+
+ if (wsUp) {
+ _send(ev);
} else {
- $log.warn('sendEvent: no websocket open:', evType, payload);
+ pendingEvents.push(ev);
}
}
- // Handles the specified message using handler bindings.
- function handleMessage(msgEvent) {
- var ev;
- try {
- ev = JSON.parse(msgEvent.data);
- $log.debug(' *Rx* >> ', ev.event, ev.payload);
- dispatchToHandler(ev);
- } catch (e) {
- $log.error('message is not valid JSON', msgEvent);
- }
- }
-
- // Dispatches the message to the appropriate handler.
- function dispatchToHandler(event) {
- var handler = handlers[event.event];
- if (handler) {
- handler(event.payload);
- } else {
- $log.warn('unhandled event:', event);
- }
- }
-
- function handleOpen() {
- $log.info('web socket open');
- // FIXME: implement calling external hooks
- }
-
- function handleClose() {
- $log.info('web socket closed');
- // FIXME: implement reconnect logic
- }
-
+ // ============================
+ // ===== Definition of module
angular.module('onosRemote')
.factory('WebSocketService',
- ['$log', '$location', 'UrlFnService', 'FnService',
+ ['$log', '$location', 'FnService', 'UrlFnService', 'WSock',
+ 'VeilService',
- function (_$log_, $loc, ufs, _fs_) {
- fs = _fs_;
+ function (_$log_, _$loc_, _fs_, _ufs_, _wsock_, _vs_) {
$log = _$log_;
-
- // Creates a web socket for the given path, returning a "handle".
- // opts contains the event handler callbacks, etc.
- function createWebSocket(path, opts) {
- var o = opts || {},
- wsport = opts && opts.wsport,
- fullUrl = ufs.wsUrl(path, wsport),
- api = {
- meta: { path: fullUrl, ws: null },
- send: send,
- close: close
- };
-
- try {
- ws = new WebSocket(fullUrl);
- api.meta.ws = ws;
- } catch (e) {
- }
-
- $log.debug('Attempting to open websocket to: ' + fullUrl);
-
- if (ws) {
- ws.onopen = handleOpen;
- ws.onmessage = handleMessage;
- ws.onclose = handleClose;
- }
-
- // Sends a formulated event message via the backing web-socket.
- function send(ev) {
- if (ev && ws) {
- ws.send(JSON.stringify(ev));
- } else if (!ws) {
- $log.warn('ws.send() no web socket open!', fullUrl, ev);
- }
- }
-
- // Closes the backing web-socket.
- function close() {
- if (ws) {
- ws.close();
- ws = null;
- api.meta.ws = null;
- }
- }
-
- sws = api; // Make the shared web-socket accessible
- return api;
- }
+ $loc = _$loc_;
+ fs = _fs_;
+ ufs = _ufs_;
+ wsock = _wsock_;
+ vs = _vs_;
return {
resetSid: resetSid,
@@ -173,6 +187,7 @@
unbindHandlers: unbindHandlers,
sendEvent: sendEvent
};
- }]);
+ }
+ ]);
}());
diff --git a/web/gui/src/main/webapp/app/fw/remote/wsock.js b/web/gui/src/main/webapp/app/fw/remote/wsock.js
new file mode 100644
index 0000000..0c58a79
--- /dev/null
+++ b/web/gui/src/main/webapp/app/fw/remote/wsock.js
@@ -0,0 +1,42 @@
+/*
+ * 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.
+ */
+
+/*
+ ONOS GUI -- Remote -- Web Socket Wrapper Service
+
+ This service provided specifically so that it can be mocked in unit tests.
+ */
+(function () {
+ 'use strict';
+
+ angular.module('onosRemote')
+ .factory('WSock', ['$log', function ($log) {
+
+ function newWebSocket(url) {
+ var ws = null;
+ try {
+ ws = new WebSocket(url);
+ } catch (e) {
+ $log.error('Unable to create web socket:', e);
+ }
+ return ws;
+ }
+
+ return {
+ newWebSocket: newWebSocket
+ };
+ }]);
+}());
diff --git a/web/gui/src/main/webapp/app/view/topo/topoEvent.js b/web/gui/src/main/webapp/app/view/topo/topoEvent.js
index 10b6909..e7f29d3 100644
--- a/web/gui/src/main/webapp/app/view/topo/topoEvent.js
+++ b/web/gui/src/main/webapp/app/view/topo/topoEvent.js
@@ -27,15 +27,15 @@
'use strict';
// injected refs
- var $log, vs, wss, tps, tis, tfs, tss, tts;
+ var $log, wss, tps, tis, tfs, tss, tts;
// internal state
- var handlers;
+ var handlerMap;
// ==========================
- function createHandlers() {
- handlers = {
+ function createHandlerMap() {
+ handlerMap = {
showSummary: tps,
showDetails: tss,
@@ -58,17 +58,14 @@
};
}
- var nilApi = {};
-
angular.module('ovTopo')
.factory('TopoEventService',
- ['$log', '$location', 'VeilService', 'WebSocketService',
+ ['$log', '$location', 'WebSocketService',
'TopoPanelService', 'TopoInstService', 'TopoForceService',
'TopoSelectService', 'TopoTrafficService',
- function (_$log_, $loc, _vs_, _wss_, _tps_, _tis_, _tfs_, _tss_, _tts_) {
+ function (_$log_, $loc, _wss_, _tps_, _tis_, _tfs_, _tss_, _tts_) {
$log = _$log_;
- vs = _vs_;
wss = _wss_;
tps = _tps_;
tis = _tis_;
@@ -76,18 +73,18 @@
tss = _tss_;
tts = _tts_;
- createHandlers();
+ createHandlerMap();
- // FIXME: need to handle async socket open to avoid race
function start() {
- wss.bindHandlers(handlers);
+ wss.bindHandlers(handlerMap);
wss.sendEvent('topoStart');
+ wss.sendEvent('requestSummary');
$log.debug('topo comms started');
}
function stop() {
- wss.unbindHandlers();
wss.sendEvent('topoStop');
+ wss.unbindHandlers(handlerMap);
$log.debug('topo comms stopped');
}
diff --git a/web/gui/src/main/webapp/index.html b/web/gui/src/main/webapp/index.html
index cab570a..05154c7 100644
--- a/web/gui/src/main/webapp/index.html
+++ b/web/gui/src/main/webapp/index.html
@@ -53,8 +53,8 @@
<script src="app/fw/remote/remote.js"></script>
<script src="app/fw/remote/urlfn.js"></script>
<script src="app/fw/remote/rest.js"></script>
+ <script src="app/fw/remote/wsock.js"></script>
<script src="app/fw/remote/websocket.js"></script>
- <script src="app/fw/remote/wsevent.js"></script>
<script src="app/fw/widget/widget.js"></script>
<script src="app/fw/widget/table.js"></script>
diff --git a/web/gui/src/main/webapp/onos.js b/web/gui/src/main/webapp/onos.js
index 97f3d06..c1338f6 100644
--- a/web/gui/src/main/webapp/onos.js
+++ b/web/gui/src/main/webapp/onos.js
@@ -84,10 +84,9 @@
flash.initFlash();
qhs.initQuickHelp();
- // TODO: register handlers for initial messages: instances, settings, etc.
+ // TODO: register handler for user settings, etc.
- // TODO: opts?
- wss.createWebSocket('core', {
+ wss.createWebSocket({
wsport: $location.search().wsport
});