GUI -- Cleaned up websocket code.
- isolated new WebSocket() call, so we can mock. Change-Id: Id1225e2c65732e750b289224e838a326c79f02a4
Showing
5 changed files
with
172 additions
and
119 deletions
| ... | @@ -21,32 +21,116 @@ | ... | @@ -21,32 +21,116 @@ |
| 21 | 'use strict'; | 21 | 'use strict'; |
| 22 | 22 | ||
| 23 | // injected refs | 23 | // injected refs |
| 24 | - var fs, $log; | 24 | + var $log, $loc, fs, ufs, wsock, vs; |
| 25 | 25 | ||
| 26 | // internal state | 26 | // internal state |
| 27 | - var ws, sws, sid = 0, | 27 | + var ws = null, // web socket reference |
| 28 | - handlers = {}; | 28 | + wsUp = false, // web socket is good to go |
| 29 | + sid = 0, // event sequence identifier | ||
| 30 | + handlers = {}, // event handler bindings | ||
| 31 | + pendingEvents = [], // events TX'd while socket not up | ||
| 32 | + url; // web socket URL | ||
| 29 | 33 | ||
| 34 | + | ||
| 35 | + // ========================== | ||
| 36 | + // === Web socket callbacks | ||
| 37 | + | ||
| 38 | + function handleOpen() { | ||
| 39 | + $log.info('Web socket open'); | ||
| 40 | + $log.debug('Sending ' + pendingEvents.length + ' pending event(s)...'); | ||
| 41 | + pendingEvents.forEach(function (ev) { | ||
| 42 | + _send(ev); | ||
| 43 | + }); | ||
| 44 | + pendingEvents = []; | ||
| 45 | + wsUp = true; | ||
| 46 | + } | ||
| 47 | + | ||
| 48 | + // Handles the specified (incoming) message using handler bindings. | ||
| 49 | + function handleMessage(msgEvent) { | ||
| 50 | + var ev, h; | ||
| 51 | + | ||
| 52 | + try { | ||
| 53 | + ev = JSON.parse(msgEvent.data); | ||
| 54 | + $log.debug(' *Rx* >> ', ev.event, ev.payload); | ||
| 55 | + | ||
| 56 | + if (h = handlers[ev.event]) { | ||
| 57 | + h(ev.payload); | ||
| 58 | + } else { | ||
| 59 | + $log.warn('Unhandled event:', ev); | ||
| 60 | + } | ||
| 61 | + | ||
| 62 | + } catch (e) { | ||
| 63 | + $log.error('Message.data is (probably) not valid JSON', msgEvent); | ||
| 64 | + } | ||
| 65 | + } | ||
| 66 | + | ||
| 67 | + function handleClose() { | ||
| 68 | + $log.info('Web socket closed'); | ||
| 69 | + wsUp = false; | ||
| 70 | + | ||
| 71 | + // FIXME: implement controller failover logic | ||
| 72 | + | ||
| 73 | + // If no controllers left to contact, show the Veil... | ||
| 74 | + vs.show([ | ||
| 75 | + 'Oops!', | ||
| 76 | + 'Web-socket connection to server closed...', | ||
| 77 | + 'Try refreshing the page.' | ||
| 78 | + ]); | ||
| 79 | + } | ||
| 80 | + | ||
| 81 | + | ||
| 82 | + // ============================== | ||
| 83 | + // === Private Helper Functions | ||
| 84 | + | ||
| 85 | + function _send(ev) { | ||
| 86 | + $log.debug(' *Tx* >> ', ev.event, ev.payload); | ||
| 87 | + ws.send(JSON.stringify(ev)); | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + | ||
| 91 | + // =================== | ||
| 92 | + // === API Functions | ||
| 93 | + | ||
| 94 | + // Required for unit tests to set to known state | ||
| 30 | function resetSid() { | 95 | function resetSid() { |
| 31 | sid = 0; | 96 | sid = 0; |
| 32 | } | 97 | } |
| 33 | 98 | ||
| 99 | + // Currently supported opts: | ||
| 100 | + // wsport: web socket port (other than default 8181) | ||
| 101 | + function createWebSocket(opts) { | ||
| 102 | + var wsport = (opts && opts.wsport) || null; | ||
| 103 | + | ||
| 104 | + url = ufs.wsUrl('core', wsport); | ||
| 105 | + | ||
| 106 | + $log.debug('Attempting to open websocket to: ' + url); | ||
| 107 | + ws = wsock.newWebSocket(url); | ||
| 108 | + if (ws) { | ||
| 109 | + ws.onopen = handleOpen; | ||
| 110 | + ws.onmessage = handleMessage; | ||
| 111 | + ws.onclose = handleClose; | ||
| 112 | + } | ||
| 113 | + // Note: Wsock logs an error if the new WebSocket call fails | ||
| 114 | + } | ||
| 115 | + | ||
| 34 | // Binds the specified message handlers. | 116 | // Binds the specified message handlers. |
| 117 | + // keys are the event IDs | ||
| 118 | + // values are the API on which the handler function is a property | ||
| 35 | function bindHandlers(handlerMap) { | 119 | function bindHandlers(handlerMap) { |
| 36 | var m = d3.map(handlerMap), | 120 | var m = d3.map(handlerMap), |
| 37 | dups = []; | 121 | dups = []; |
| 38 | 122 | ||
| 39 | - m.forEach(function (key, value) { | 123 | + m.forEach(function (eventId, api) { |
| 40 | - var fn = fs.isF(value[key]); | 124 | + var fn = fs.isF(api[eventId]); |
| 41 | if (!fn) { | 125 | if (!fn) { |
| 42 | - $log.warn(key + ' binding not a function on ' + value); | 126 | + $log.warn(eventId + ' handler not a function'); |
| 43 | return; | 127 | return; |
| 44 | } | 128 | } |
| 45 | 129 | ||
| 46 | - if (handlers[key]) { | 130 | + if (handlers[eventId]) { |
| 47 | - dups.push(key); | 131 | + dups.push(eventId); |
| 48 | } else { | 132 | } else { |
| 49 | - handlers[key] = fn; | 133 | + handlers[eventId] = fn; |
| 50 | } | 134 | } |
| 51 | }); | 135 | }); |
| 52 | if (dups.length) { | 136 | if (dups.length) { |
| ... | @@ -55,116 +139,46 @@ | ... | @@ -55,116 +139,46 @@ |
| 55 | } | 139 | } |
| 56 | 140 | ||
| 57 | // Unbinds the specified message handlers. | 141 | // Unbinds the specified message handlers. |
| 142 | + // Expected that the same map will be used, but we only care about keys | ||
| 58 | function unbindHandlers(handlerMap) { | 143 | function unbindHandlers(handlerMap) { |
| 59 | var m = d3.map(handlerMap); | 144 | var m = d3.map(handlerMap); |
| 60 | - m.forEach(function (key) { | 145 | + |
| 61 | - delete handlers[key]; | 146 | + m.forEach(function (eventId) { |
| 147 | + delete handlers[eventId]; | ||
| 62 | }); | 148 | }); |
| 63 | } | 149 | } |
| 64 | 150 | ||
| 65 | - // Formulates an event message and sends it via the shared web-socket. | 151 | + // Formulates an event message and sends it via the web-socket. |
| 152 | + // If the websocket is not up yet, we store it in a pending list. | ||
| 66 | function sendEvent(evType, payload) { | 153 | function sendEvent(evType, payload) { |
| 67 | - var p = payload || {}; | 154 | + var ev = { |
| 68 | - if (sws) { | ||
| 69 | - $log.debug(' *Tx* >> ', evType, payload); | ||
| 70 | - sws.send({ | ||
| 71 | event: evType, | 155 | event: evType, |
| 72 | sid: ++sid, | 156 | sid: ++sid, |
| 73 | - payload: p | 157 | + payload: payload || {} |
| 74 | - }); | 158 | + }; |
| 75 | - } else { | ||
| 76 | - $log.warn('sendEvent: no websocket open:', evType, payload); | ||
| 77 | - } | ||
| 78 | - } | ||
| 79 | - | ||
| 80 | - | ||
| 81 | - // Handles the specified message using handler bindings. | ||
| 82 | - function handleMessage(msgEvent) { | ||
| 83 | - var ev; | ||
| 84 | - try { | ||
| 85 | - ev = JSON.parse(msgEvent.data); | ||
| 86 | - $log.debug(' *Rx* >> ', ev.event, ev.payload); | ||
| 87 | - dispatchToHandler(ev); | ||
| 88 | - } catch (e) { | ||
| 89 | - $log.error('message is not valid JSON', msgEvent); | ||
| 90 | - } | ||
| 91 | - } | ||
| 92 | 159 | ||
| 93 | - // Dispatches the message to the appropriate handler. | 160 | + if (wsUp) { |
| 94 | - function dispatchToHandler(event) { | 161 | + _send(ev); |
| 95 | - var handler = handlers[event.event]; | ||
| 96 | - if (handler) { | ||
| 97 | - handler(event.payload); | ||
| 98 | } else { | 162 | } else { |
| 99 | - $log.warn('unhandled event:', event); | 163 | + pendingEvents.push(ev); |
| 100 | } | 164 | } |
| 101 | } | 165 | } |
| 102 | 166 | ||
| 103 | - function handleOpen() { | ||
| 104 | - $log.info('web socket open'); | ||
| 105 | - // FIXME: implement calling external hooks | ||
| 106 | - } | ||
| 107 | - | ||
| 108 | - function handleClose() { | ||
| 109 | - $log.info('web socket closed'); | ||
| 110 | - // FIXME: implement reconnect logic | ||
| 111 | - } | ||
| 112 | 167 | ||
| 168 | + // ============================ | ||
| 169 | + // ===== Definition of module | ||
| 113 | angular.module('onosRemote') | 170 | angular.module('onosRemote') |
| 114 | .factory('WebSocketService', | 171 | .factory('WebSocketService', |
| 115 | - ['$log', '$location', 'UrlFnService', 'FnService', | 172 | + ['$log', '$location', 'FnService', 'UrlFnService', 'WSock', |
| 173 | + 'VeilService', | ||
| 116 | 174 | ||
| 117 | - function (_$log_, $loc, ufs, _fs_) { | 175 | + function (_$log_, _$loc_, _fs_, _ufs_, _wsock_, _vs_) { |
| 118 | - fs = _fs_; | ||
| 119 | $log = _$log_; | 176 | $log = _$log_; |
| 120 | - | 177 | + $loc = _$loc_; |
| 121 | - // Creates a web socket for the given path, returning a "handle". | 178 | + fs = _fs_; |
| 122 | - // opts contains the event handler callbacks, etc. | 179 | + ufs = _ufs_; |
| 123 | - function createWebSocket(path, opts) { | 180 | + wsock = _wsock_; |
| 124 | - var o = opts || {}, | 181 | + vs = _vs_; |
| 125 | - wsport = opts && opts.wsport, | ||
| 126 | - fullUrl = ufs.wsUrl(path, wsport), | ||
| 127 | - api = { | ||
| 128 | - meta: { path: fullUrl, ws: null }, | ||
| 129 | - send: send, | ||
| 130 | - close: close | ||
| 131 | - }; | ||
| 132 | - | ||
| 133 | - try { | ||
| 134 | - ws = new WebSocket(fullUrl); | ||
| 135 | - api.meta.ws = ws; | ||
| 136 | - } catch (e) { | ||
| 137 | - } | ||
| 138 | - | ||
| 139 | - $log.debug('Attempting to open websocket to: ' + fullUrl); | ||
| 140 | - | ||
| 141 | - if (ws) { | ||
| 142 | - ws.onopen = handleOpen; | ||
| 143 | - ws.onmessage = handleMessage; | ||
| 144 | - ws.onclose = handleClose; | ||
| 145 | - } | ||
| 146 | - | ||
| 147 | - // Sends a formulated event message via the backing web-socket. | ||
| 148 | - function send(ev) { | ||
| 149 | - if (ev && ws) { | ||
| 150 | - ws.send(JSON.stringify(ev)); | ||
| 151 | - } else if (!ws) { | ||
| 152 | - $log.warn('ws.send() no web socket open!', fullUrl, ev); | ||
| 153 | - } | ||
| 154 | - } | ||
| 155 | - | ||
| 156 | - // Closes the backing web-socket. | ||
| 157 | - function close() { | ||
| 158 | - if (ws) { | ||
| 159 | - ws.close(); | ||
| 160 | - ws = null; | ||
| 161 | - api.meta.ws = null; | ||
| 162 | - } | ||
| 163 | - } | ||
| 164 | - | ||
| 165 | - sws = api; // Make the shared web-socket accessible | ||
| 166 | - return api; | ||
| 167 | - } | ||
| 168 | 182 | ||
| 169 | return { | 183 | return { |
| 170 | resetSid: resetSid, | 184 | resetSid: resetSid, |
| ... | @@ -173,6 +187,7 @@ | ... | @@ -173,6 +187,7 @@ |
| 173 | unbindHandlers: unbindHandlers, | 187 | unbindHandlers: unbindHandlers, |
| 174 | sendEvent: sendEvent | 188 | sendEvent: sendEvent |
| 175 | }; | 189 | }; |
| 176 | - }]); | 190 | + } |
| 191 | + ]); | ||
| 177 | 192 | ||
| 178 | }()); | 193 | }()); | ... | ... |
| 1 | +/* | ||
| 2 | + * Copyright 2015 Open Networking Laboratory | ||
| 3 | + * | ||
| 4 | + * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| 5 | + * you may not use this file except in compliance with the License. | ||
| 6 | + * You may obtain a copy of the License at | ||
| 7 | + * | ||
| 8 | + * http://www.apache.org/licenses/LICENSE-2.0 | ||
| 9 | + * | ||
| 10 | + * Unless required by applicable law or agreed to in writing, software | ||
| 11 | + * distributed under the License is distributed on an "AS IS" BASIS, | ||
| 12 | + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| 13 | + * See the License for the specific language governing permissions and | ||
| 14 | + * limitations under the License. | ||
| 15 | + */ | ||
| 16 | + | ||
| 17 | +/* | ||
| 18 | + ONOS GUI -- Remote -- Web Socket Wrapper Service | ||
| 19 | + | ||
| 20 | + This service provided specifically so that it can be mocked in unit tests. | ||
| 21 | + */ | ||
| 22 | +(function () { | ||
| 23 | + 'use strict'; | ||
| 24 | + | ||
| 25 | + angular.module('onosRemote') | ||
| 26 | + .factory('WSock', ['$log', function ($log) { | ||
| 27 | + | ||
| 28 | + function newWebSocket(url) { | ||
| 29 | + var ws = null; | ||
| 30 | + try { | ||
| 31 | + ws = new WebSocket(url); | ||
| 32 | + } catch (e) { | ||
| 33 | + $log.error('Unable to create web socket:', e); | ||
| 34 | + } | ||
| 35 | + return ws; | ||
| 36 | + } | ||
| 37 | + | ||
| 38 | + return { | ||
| 39 | + newWebSocket: newWebSocket | ||
| 40 | + }; | ||
| 41 | + }]); | ||
| 42 | +}()); |
| ... | @@ -27,15 +27,15 @@ | ... | @@ -27,15 +27,15 @@ |
| 27 | 'use strict'; | 27 | 'use strict'; |
| 28 | 28 | ||
| 29 | // injected refs | 29 | // injected refs |
| 30 | - var $log, vs, wss, tps, tis, tfs, tss, tts; | 30 | + var $log, wss, tps, tis, tfs, tss, tts; |
| 31 | 31 | ||
| 32 | // internal state | 32 | // internal state |
| 33 | - var handlers; | 33 | + var handlerMap; |
| 34 | 34 | ||
| 35 | // ========================== | 35 | // ========================== |
| 36 | 36 | ||
| 37 | - function createHandlers() { | 37 | + function createHandlerMap() { |
| 38 | - handlers = { | 38 | + handlerMap = { |
| 39 | showSummary: tps, | 39 | showSummary: tps, |
| 40 | 40 | ||
| 41 | showDetails: tss, | 41 | showDetails: tss, |
| ... | @@ -58,17 +58,14 @@ | ... | @@ -58,17 +58,14 @@ |
| 58 | }; | 58 | }; |
| 59 | } | 59 | } |
| 60 | 60 | ||
| 61 | - var nilApi = {}; | ||
| 62 | - | ||
| 63 | angular.module('ovTopo') | 61 | angular.module('ovTopo') |
| 64 | .factory('TopoEventService', | 62 | .factory('TopoEventService', |
| 65 | - ['$log', '$location', 'VeilService', 'WebSocketService', | 63 | + ['$log', '$location', 'WebSocketService', |
| 66 | 'TopoPanelService', 'TopoInstService', 'TopoForceService', | 64 | 'TopoPanelService', 'TopoInstService', 'TopoForceService', |
| 67 | 'TopoSelectService', 'TopoTrafficService', | 65 | 'TopoSelectService', 'TopoTrafficService', |
| 68 | 66 | ||
| 69 | - function (_$log_, $loc, _vs_, _wss_, _tps_, _tis_, _tfs_, _tss_, _tts_) { | 67 | + function (_$log_, $loc, _wss_, _tps_, _tis_, _tfs_, _tss_, _tts_) { |
| 70 | $log = _$log_; | 68 | $log = _$log_; |
| 71 | - vs = _vs_; | ||
| 72 | wss = _wss_; | 69 | wss = _wss_; |
| 73 | tps = _tps_; | 70 | tps = _tps_; |
| 74 | tis = _tis_; | 71 | tis = _tis_; |
| ... | @@ -76,18 +73,18 @@ | ... | @@ -76,18 +73,18 @@ |
| 76 | tss = _tss_; | 73 | tss = _tss_; |
| 77 | tts = _tts_; | 74 | tts = _tts_; |
| 78 | 75 | ||
| 79 | - createHandlers(); | 76 | + createHandlerMap(); |
| 80 | 77 | ||
| 81 | - // FIXME: need to handle async socket open to avoid race | ||
| 82 | function start() { | 78 | function start() { |
| 83 | - wss.bindHandlers(handlers); | 79 | + wss.bindHandlers(handlerMap); |
| 84 | wss.sendEvent('topoStart'); | 80 | wss.sendEvent('topoStart'); |
| 81 | + wss.sendEvent('requestSummary'); | ||
| 85 | $log.debug('topo comms started'); | 82 | $log.debug('topo comms started'); |
| 86 | } | 83 | } |
| 87 | 84 | ||
| 88 | function stop() { | 85 | function stop() { |
| 89 | - wss.unbindHandlers(); | ||
| 90 | wss.sendEvent('topoStop'); | 86 | wss.sendEvent('topoStop'); |
| 87 | + wss.unbindHandlers(handlerMap); | ||
| 91 | $log.debug('topo comms stopped'); | 88 | $log.debug('topo comms stopped'); |
| 92 | } | 89 | } |
| 93 | 90 | ... | ... |
| ... | @@ -53,8 +53,8 @@ | ... | @@ -53,8 +53,8 @@ |
| 53 | <script src="app/fw/remote/remote.js"></script> | 53 | <script src="app/fw/remote/remote.js"></script> |
| 54 | <script src="app/fw/remote/urlfn.js"></script> | 54 | <script src="app/fw/remote/urlfn.js"></script> |
| 55 | <script src="app/fw/remote/rest.js"></script> | 55 | <script src="app/fw/remote/rest.js"></script> |
| 56 | + <script src="app/fw/remote/wsock.js"></script> | ||
| 56 | <script src="app/fw/remote/websocket.js"></script> | 57 | <script src="app/fw/remote/websocket.js"></script> |
| 57 | - <script src="app/fw/remote/wsevent.js"></script> | ||
| 58 | 58 | ||
| 59 | <script src="app/fw/widget/widget.js"></script> | 59 | <script src="app/fw/widget/widget.js"></script> |
| 60 | <script src="app/fw/widget/table.js"></script> | 60 | <script src="app/fw/widget/table.js"></script> | ... | ... |
| ... | @@ -84,10 +84,9 @@ | ... | @@ -84,10 +84,9 @@ |
| 84 | flash.initFlash(); | 84 | flash.initFlash(); |
| 85 | qhs.initQuickHelp(); | 85 | qhs.initQuickHelp(); |
| 86 | 86 | ||
| 87 | - // TODO: register handlers for initial messages: instances, settings, etc. | 87 | + // TODO: register handler for user settings, etc. |
| 88 | 88 | ||
| 89 | - // TODO: opts? | 89 | + wss.createWebSocket({ |
| 90 | - wss.createWebSocket('core', { | ||
| 91 | wsport: $location.search().wsport | 90 | wsport: $location.search().wsport |
| 92 | }); | 91 | }); |
| 93 | 92 | ... | ... |
-
Please register or login to post a comment