| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you 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. |
| */ |
| /** |
| * @module QDR |
| */ |
| var QDR = (function(QDR) { |
| |
| // The QDR service handles the connection to |
| // the server in the background |
| QDR.module.factory("QDRService", ['$rootScope', '$http', '$timeout', '$resource', '$location', function($rootScope, $http, $timeout, $resource, $location) { |
| var self = { |
| |
| rhea: require("rhea"), |
| timeout: 10, // seconds to wait before assuming a request has failed |
| updateInterval: 2000, // milliseconds between background updates |
| connectActions: [], |
| disconnectActions: [], |
| updatedActions: {}, |
| updating: false, // are we updating the node list in the background |
| maxCorrelatorDepth: 10, // max number of outstanding requests to allow |
| |
| /* |
| * @property message |
| * The proton message that is used to send commands |
| * and receive responses |
| */ |
| sender: undefined, |
| receiver: undefined, |
| version: undefined, |
| sendable: false, |
| |
| schema: undefined, |
| |
| connected: false, |
| gotTopology: false, |
| errorText: undefined, |
| connectionError: undefined, |
| |
| addConnectAction: function(action) { |
| if (angular.isFunction(action)) { |
| self.connectActions.push(action); |
| } |
| }, |
| addDisconnectAction: function(action) { |
| if (angular.isFunction(action)) { |
| self.disconnectActions.push(action); |
| } |
| }, |
| delDisconnectAction: function(action) { |
| if (angular.isFunction(action)) { |
| var index = self.disconnectActions.indexOf(action) |
| if (index >= 0) |
| self.disconnectActions.splice(index, 1) |
| } |
| }, |
| addUpdatedAction: function(key, action) { |
| if (angular.isFunction(action)) { |
| self.updatedActions[key] = action; |
| } |
| }, |
| delUpdatedAction: function(key) { |
| if (key in self.updatedActions) |
| delete self.updatedActions[key]; |
| }, |
| |
| executeConnectActions: function() { |
| self.connectActions.forEach(function(action) { |
| try { |
| action.apply(); |
| } catch (e) { |
| // in case the page that registered the handler has been unloaded |
| QDR.log.info(e.message) |
| } |
| }); |
| self.connectActions = []; |
| }, |
| executeDisconnectActions: function() { |
| self.disconnectActions.forEach(function(action) { |
| try { |
| action.apply(); |
| } catch (e) { |
| // in case the page that registered the handler has been unloaded |
| } |
| }); |
| self.disconnectActions = []; |
| }, |
| executeUpdatedActions: function() { |
| for (action in self.updatedActions) { |
| // try { |
| self.updatedActions[action].apply(); |
| /* } catch (e) { |
| QDR.log.debug("caught error executing updated actions") |
| console.dump(e) |
| delete self.updatedActions[action] |
| } |
| */ |
| } |
| }, |
| redirectWhenConnected: function(org) { |
| $location.path(QDR.pluginRoot + "/connect") |
| $location.search('org', org); |
| }, |
| |
| notifyTopologyDone: function() { |
| if (!self.gotTopology) { |
| QDR.log.debug("topology was just initialized"); |
| //console.dump(self.topology._nodeInfo) |
| self.gotTopology = true; |
| //$rootScope.$apply(); |
| } else { |
| //QDR.log.debug("topology model was just updated"); |
| } |
| self.executeUpdatedActions(); |
| |
| }, |
| |
| isConnected: function() { |
| return self.connected; |
| }, |
| |
| versionCheck: function (minVer) { |
| var verparts = self.version.split('.') |
| var minparts = minVer.split('.') |
| try { |
| for (var i=0; i<minparts.length; ++i) { |
| if (parseInt(minVer[i] > parseInt(verparts[i]))) |
| return false |
| } |
| } catch (e) { |
| QDR.log.debug("error doing version check between: " + self.version + " and " + minVer + " " + e.message) |
| return false |
| } |
| return true |
| }, |
| |
| correlator: { |
| _objects: {}, |
| _correlationID: 0, |
| |
| corr: function() { |
| var id = ++this._correlationID + ""; |
| this._objects[id] = { |
| resolver: null |
| } |
| return id; |
| }, |
| request: function() { |
| //QDR.log.debug("correlator:request"); |
| return this; |
| }, |
| then: function(id, resolver, error) { |
| //QDR.log.debug("registered then resolver for correlationID: " + id); |
| if (error) { |
| //QDR.log.debug("then received an error. deleting correlator") |
| delete this._objects[id]; |
| return; |
| } |
| this._objects[id].resolver = resolver; |
| }, |
| // called by receiver's on('message') handler when a response arrives |
| resolve: function(context) { |
| var correlationID = context.message.properties.correlation_id; |
| this._objects[correlationID].resolver(context.message.body, context); |
| delete this._objects[correlationID]; |
| }, |
| depth: function () { |
| return Object.keys(this._objects).length |
| } |
| }, |
| |
| onSubscription: function() { |
| self.executeConnectActions(); |
| var org = $location.search() |
| if (org) |
| org = org.org |
| if (org && org.length > 0 && org !== "connect") { |
| self.getSchema(function () { |
| self.setUpdateEntities([]) |
| self.topology.get() |
| self.addUpdatedAction('onSub', function () { |
| self.delUpdatedAction('onSub') |
| $timeout( function () { |
| $location.path(QDR.pluginRoot + '/' + org) |
| $location.search('org', null) |
| $location.replace() |
| }) |
| }) |
| }); |
| } |
| }, |
| |
| startUpdating: function() { |
| self.stopUpdating(true); |
| QDR.log.info("startUpdating called") |
| self.updating = true; |
| self.topology.get(); |
| }, |
| stopUpdating: function(silent) { |
| self.updating = false; |
| if (self.topology._getTimer) { |
| clearTimeout(self.topology._getTimer) |
| self.topology._getTimer = null; |
| } |
| if (self.topology._waitTimer) { |
| clearTimeout(self.topology._waitTimer) |
| self.topology._waitTimer = null; |
| } |
| if (self.topology._gettingTopo) { |
| if (self.topology.q) |
| self.topology.q.abort() |
| } |
| if (!silent) |
| QDR.log.info("stopUpdating called") |
| }, |
| |
| cleanUp: function() {}, |
| error: function(line) { |
| if (line.num) { |
| QDR.log.debug("error - num: ", line.num, " message: ", line.message); |
| } else { |
| QDR.log.debug("error - message: ", line.message); |
| } |
| }, |
| disconnected: function(line) { |
| QDR.log.debug("Disconnected from QDR server"); |
| self.executeDisconnectActions(); |
| }, |
| |
| nameFromId: function(id) { |
| return id.split('/')[3]; |
| }, |
| |
| humanify: function(s) { |
| if (!s || s.length === 0) |
| return s; |
| var t = s.charAt(0).toUpperCase() + s.substr(1).replace(/[A-Z]/g, ' $&'); |
| return t.replace(".", " "); |
| }, |
| pretty: function(v) { |
| var formatComma = d3.format(","); |
| if (!isNaN(parseFloat(v)) && isFinite(v)) |
| return formatComma(v); |
| return v; |
| }, |
| |
| nodeNameList: function() { |
| var nl = []; |
| for (var id in self.topology._nodeInfo) { |
| nl.push(self.nameFromId(id)); |
| } |
| return nl.sort(); |
| }, |
| |
| nodeIdList: function() { |
| var nl = []; |
| for (var id in self.topology._nodeInfo) { |
| nl.push(id); |
| } |
| return nl.sort(); |
| }, |
| |
| nodeList: function() { |
| var nl = []; |
| for (var id in self.topology._nodeInfo) { |
| nl.push({ |
| name: self.nameFromId(id), |
| id: id |
| }); |
| } |
| return nl; |
| }, |
| |
| isLargeNetwork: function () { |
| return Object.keys(self.topology._nodeInfo).length >= 12 |
| }, |
| isMSIE: function () { |
| return (document.documentMode || /Edge/.test(navigator.userAgent)) |
| }, |
| |
| // given an attribute name array, find the value at the same index in the values array |
| valFor: function(aAr, vAr, key) { |
| var idx = aAr.indexOf(key); |
| if ((idx > -1) && (idx < vAr.length)) { |
| return vAr[idx]; |
| } |
| return null; |
| }, |
| |
| isArtemis: function(d) { |
| return d.nodeType === 'route-container' && d.properties.product === 'apache-activemq-artemis'; |
| }, |
| |
| isQpid: function(d) { |
| return d.nodeType === 'on-demand' && (d.properties && d.properties.product === 'qpid-cpp'); |
| }, |
| |
| isAConsole: function(properties, connectionId, nodeType, key) { |
| return self.isConsole({ |
| properties: properties, |
| connectionId: connectionId, |
| nodeType: nodeType, |
| key: key |
| }) |
| }, |
| isConsole: function(d) { |
| // use connection properties if available |
| return (d && d['properties'] && d['properties']['console_identifier'] === 'Dispatch console') |
| }, |
| |
| flatten: function(attributes, result) { |
| var flat = {} |
| attributes.forEach(function(attr, i) { |
| if (result && result.length > i) |
| flat[attr] = result[i] |
| }) |
| return flat; |
| }, |
| isConsoleLink: function(link) { |
| // find the connection for this link |
| var conns = self.topology.nodeInfo()[link.nodeId]['.connection'] |
| var connIndex = conns.attributeNames.indexOf("identity") |
| var linkCons = conns.results.filter(function(conn) { |
| return conn[connIndex] === link.connectionId; |
| }) |
| var conn = self.flatten(conns.attributeNames, linkCons[0]); |
| |
| return self.isConsole(conn) |
| }, |
| |
| quiesceLink: function(nodeId, name) { |
| function gotMethodResponse(nodeName, entity, response, context) { |
| var statusCode = context.message.application_properties.statusCode; |
| if (statusCode < 200 || statusCode >= 300) { |
| Core.notification('error', context.message.statusDescription); |
| QDR.log.info('Error ' + context.message.statusDescription) |
| } |
| } |
| var attributes = { |
| adminStatus: 'disabled', |
| name: name |
| }; |
| self.sendMethod(nodeId, "router.link", attributes, "UPDATE", undefined, gotMethodResponse) |
| }, |
| addr_text: function(addr) { |
| if (!addr) |
| return "-" |
| if (addr[0] == 'M') |
| return addr.substring(2) |
| else |
| return addr.substring(1) |
| }, |
| addr_class: function(addr) { |
| if (!addr) return "-" |
| if (addr[0] == 'M') return "mobile" |
| if (addr[0] == 'R') return "router" |
| if (addr[0] == 'A') return "area" |
| if (addr[0] == 'L') return "local" |
| if (addr[0] == 'C') return "link-incoming" |
| if (addr[0] == 'D') return "link-outgoing" |
| if (addr[0] == 'T') return "topo" |
| return "unknown: " + addr[0] |
| }, |
| identity_clean: function(identity) { |
| if (!identity) |
| return "-" |
| var pos = identity.indexOf('/') |
| if (pos >= 0) |
| return identity.substring(pos + 1) |
| return identity |
| }, |
| |
| queueDepth: function () { |
| var qdepth = self.maxCorrelatorDepth - self.correlator.depth() |
| if (qdepth <= 0) |
| qdepth = 1; |
| //QDR.log.debug("queueDepth requested " + qdepth + "(" + self.correlator.depth() + ")") |
| return qdepth; |
| }, |
| // check if all nodes have this entity. if not, get them |
| initEntity: function (entity, callback) { |
| var callNeeded = Object.keys(self.topology._nodeInfo).some( function (node) { |
| return !angular.isDefined(self.topology._nodeInfo[node][entity]) |
| }) |
| if (callNeeded) { |
| self.loadEntity(entity, callback) |
| } else |
| callback() |
| }, |
| |
| // get/refresh entities for all nodes |
| loadEntity: function (entities, callback) { |
| if (Object.prototype.toString.call(entities) !== '[object Array]') { |
| entities = [entities] |
| } |
| var q = QDR.queue(self.queueDepth()) |
| for (node in self.topology._nodeInfo) { |
| for (var i=0; i<entities.length; ++i) { |
| var entity = entities[i] |
| q.defer(self.ensureNodeInfo, node, entity, [], q) |
| } |
| } |
| q.await(function (error) { |
| clearTimeout(self.topology._waitTimer) |
| callback(); |
| }) |
| }, |
| |
| // enusre all the topology nones have all these entities |
| ensureAllEntities: function (entityAttribs, callback, extra) { |
| self.ensureEntities(Object.keys(self.topology._nodeInfo), entityAttribs, callback, extra) |
| }, |
| |
| // ensure these nodes have all these entities. don't fetch unless forced to |
| ensureEntities: function (nodes, entityAttribs, callback, extra) { |
| if (Object.prototype.toString.call(entityAttribs) !== '[object Array]') { |
| entityAttribs = [entityAttribs] |
| } |
| if (Object.prototype.toString.call(nodes) !== '[object Array]') { |
| nodes = [nodes] |
| } |
| var q = QDR.queue(self.queueDepth()) |
| for (var n=0; n<nodes.length; ++n) { |
| for (var i=0; i<entityAttribs.length; ++i) { |
| var ea = entityAttribs[i] |
| // if we don'e already have the entity or we want to force a refresh |
| if (!self.topology._nodeInfo[nodes[n]][ea.entity] || ea.force) |
| q.defer(self.ensureNodeInfo, nodes[n], ea.entity, ea.attrs || [], q) |
| } |
| } |
| q.await(function (error) { |
| clearTimeout(self.topology._waitTimer) |
| callback(extra); |
| }) |
| }, |
| |
| // queue up a request to get certain attributes for one entity for a node and return the results |
| fetchEntity: function (node, entity, attrs, callback) { |
| var results = {} |
| var gotResponse = function (nodeName, dotentity, response) { |
| results = response |
| } |
| var q = QDR.queue(self.queueDepth()) |
| q.defer(self.fetchNodeInfo, node, entity, attrs, q, gotResponse) |
| q.await(function (error) { |
| callback(node, entity, results) |
| }) |
| }, |
| |
| // get/refreshes entities for all topology.nodes |
| // call doneCallback when all data is available |
| // optionally supply a resultCallBack that will be called as each result is avaialble |
| // if a resultCallBack is supplied, the calling function is responsible for accumulating the responses |
| // otherwise the responses will be returned to the doneCallback as an object |
| fetchAllEntities: function (entityAttribs, doneCallback, resultCallback) { |
| var q = QDR.queue(self.queueDepth()) |
| var results = {} |
| if (!resultCallback) { |
| resultCallback = function (nodeName, dotentity, response) { |
| if (!results[nodeName]) |
| results[nodeName] = {} |
| results[nodeName][dotentity] = angular.copy(response); |
| } |
| } |
| var gotAResponse = function (nodeName, dotentity, response) { |
| resultCallback(nodeName, dotentity, response) |
| } |
| if (Object.prototype.toString.call(entityAttribs) !== '[object Array]') { |
| entityAttribs = [entityAttribs] |
| } |
| var nodes = Object.keys(self.topology._nodeInfo) |
| for (var n=0; n<nodes.length; ++n) { |
| for (var i=0; i<entityAttribs.length; ++i) { |
| var ea = entityAttribs[i] |
| q.defer(self.fetchNodeInfo, nodes[n], ea.entity, ea.attrs || [], q, gotAResponse) |
| } |
| } |
| q.await(function (error) { |
| doneCallback(results); |
| }) |
| }, |
| |
| setUpdateEntities: function (entities) { |
| self.topology._autoUpdatedEntities = entities |
| }, |
| addUpdateEntity: function (entity) { |
| if (self.topology._autoUpdatedEntities.indexOf(entity) == -1) |
| self.topology._autoUpdatedEntities.push(entity) |
| }, |
| delUpdateEntity: function (entity) { |
| var index = self.topology._autoUpdatedEntities.indexOf(entity) |
| if (index != -1) |
| self.topology._autoUpdatedEntities.splice(index, 1) |
| }, |
| |
| /* |
| * send the management messages that build up the topology |
| * |
| * |
| */ |
| topology: { |
| _gettingTopo: false, |
| _nodeInfo: {}, |
| _lastNodeInfo: {}, |
| _waitTimer: null, |
| _getTimer: null, |
| _autoUpdatedEntities: [], |
| q: null, |
| |
| nodeInfo: function() { |
| return self.topology._nodeInfo |
| }, |
| |
| get: function() { |
| if (self.topology._gettingTopo) { |
| QDR.log.debug("asked to get topology but was already getting it") |
| if (self.topology.q) |
| self.topology.q.abort() |
| } |
| self.topology.q = null |
| if (!self.connected) { |
| QDR.log.debug("topology get failed because !self.connected") |
| return; |
| } |
| if (self.topology._getTimer) { |
| clearTimeout(self.topology._getTimer) |
| self.topology._getTimer = null |
| } |
| |
| //QDR.log.info("starting get topology with correlator.depth of " + self.correlator.depth()) |
| self.topology._gettingTopo = true; |
| self.errorText = undefined; |
| |
| // get the list of nodes to query. |
| // once this completes, we will get the info for each node returned |
| self.getRemoteNodeInfo(function(response, context) { |
| if (Object.prototype.toString.call(response) === '[object Array]') { |
| // remove dropped nodes |
| var keys = Object.keys(self.topology._nodeInfo) |
| for (var i=0; i<keys.length; ++i) { |
| if (response.indexOf(keys[i]) < 0) { |
| delete self.topology._nodeInfo[keys[i]] |
| } |
| } |
| // add any new nodes |
| // if there is only one node, it will not be returned |
| if (response.length === 0) { |
| var parts = self.receiver.remote.attach.source.address.split('/') |
| parts[4] = '$management' |
| response.push(parts.join('/')) |
| //QDR.log.info("GET-MGMT-NODES returned an empty list. Using ") |
| //console.dump(response) |
| } |
| for (var i=0; i<response.length; ++i) { |
| if (!angular.isDefined(self.topology._nodeInfo[response[i]])) { |
| self.topology._nodeInfo[angular.copy(response[i])] = {}; |
| } |
| } |
| // also refresh any entities that were requested |
| self.topology.q = QDR.queue(self.queueDepth()) |
| for (var i=0; i<self.topology._autoUpdatedEntities.length; ++i) { |
| var entity = self.topology._autoUpdatedEntities[i] |
| //QDR.log.debug("queuing requests for all nodes for " + entity) |
| for (node in self.topology._nodeInfo) { |
| self.topology.q.defer(self.ensureNodeInfo, node, entity, [], self.topology.q) |
| } |
| } |
| self.topology.q.await(function (error) { |
| self.topology._gettingTopo = false; |
| self.topology.q = null |
| self.topology.ondone(error) |
| }) |
| }; |
| }); |
| }, |
| |
| cleanUp: function(obj) { |
| if (obj) |
| delete obj; |
| }, |
| timedOut: function(q) { |
| // a node dropped out. this happens when the get-mgmt-nodex |
| // results contains more nodes than actually respond within |
| // the timeout |
| QDR.log.debug("timed out waiting for management responses"); |
| // note: can't use 'this' in a timeout handler |
| self.topology.miniDump("state at timeout"); |
| q.abort() |
| //self.topology.onerror(Error("management responses are not consistent")); |
| }, |
| |
| addNodeInfo: function(id, entity, values, q) { |
| clearTimeout(self.topology._waitTimer) |
| // save the results in the nodeInfo object |
| if (id) { |
| if (!(id in self.topology._nodeInfo)) { |
| self.topology._nodeInfo[id] = {}; |
| } |
| // copy the values to allow garbage collector to reclaim their memory |
| self.topology._nodeInfo[id][entity] = angular.copy(values) |
| } |
| self.topology.cleanUp(values); |
| }, |
| ondone: function(waserror) { |
| clearTimeout(self.topology._getTimer); |
| clearTimeout(self.topology._waitTimer); |
| self.topology._waitTimer = null; |
| if (self.updating) |
| self.topology._getTimer = setTimeout(self.topology.get, self.updateInterval); |
| //if (!waserror) |
| self.notifyTopologyDone(); |
| }, |
| dump: function(prefix) { |
| if (prefix) |
| QDR.log.info(prefix); |
| QDR.log.info("---"); |
| for (var key in self.topology._nodeInfo) { |
| QDR.log.info(key); |
| console.dump(self.topology._nodeInfo[key]); |
| QDR.log.info("---"); |
| } |
| }, |
| miniDump: function(prefix) { |
| if (prefix) |
| QDR.log.info(prefix); |
| QDR.log.info("---"); |
| console.dump(Object.keys(self.topology._nodeInfo)); |
| QDR.log.info("---"); |
| }, |
| onerror: function(err) { |
| self.topology._gettingTopo = false; |
| QDR.log.debug("Err:" + err); |
| //self.executeDisconnectActions(); |
| } |
| |
| }, |
| getRemoteNodeInfo: function(callback) { |
| //QDR.log.debug("getRemoteNodeInfo called"); |
| |
| setTimeout(function () { |
| var ret; |
| // first get the list of remote node names |
| self.correlator.request( |
| ret = self.sendMgmtQuery('GET-MGMT-NODES') |
| ).then(ret.id, function(response, context) { |
| callback(response, context); |
| self.topology.cleanUp(response); |
| }, ret.error); |
| }, 1) |
| }, |
| |
| // sends a request and updates the topology.nodeInfo object with the response |
| // should only be called from a q.defer() statement |
| ensureNodeInfo: function (nodeId, entity, attrs, q, callback) { |
| //QDR.log.debug("queuing request for " + nodeId + " " + entity) |
| self.getNodeInfo(nodeId, entity, attrs, q, function (nodeName, dotentity, response) { |
| //QDR.log.debug("got response for " + nodeId + " " + entity) |
| self.topology.addNodeInfo(nodeName, dotentity, response, q) |
| callback(null) |
| }) |
| return { |
| abort: function() { |
| delete self.topology._nodeInfo[nodeId] |
| //self.topology._nodeInfo[nodeId][entity] = {attributeNames: [], results: [[]]}; |
| } |
| } |
| }, |
| |
| // sends request and returns the response |
| // should only be called from a q.defer() statement |
| fetchNodeInfo: function (nodeId, entity, attrs, q, heartbeat, callback) { |
| self.getNodeInfo(nodeId, entity, attrs, q, function (nodeName, dotentity, response) { |
| heartbeat(nodeName, dotentity, response) |
| callback(null) |
| }) |
| }, |
| |
| getMultipleNodeInfo: function(nodeNames, entity, attrs, callback, selectedNodeId, aggregate) { |
| if (!angular.isDefined(aggregate)) |
| aggregate = true; |
| var responses = {}; |
| var gotNodesResult = function(nodeName, dotentity, response) { |
| responses[nodeName] = response; |
| } |
| |
| var q = QDR.queue(self.queueDepth()) |
| nodeNames.forEach(function(id) { |
| q.defer(self.fetchNodeInfo, id, '.' + entity, attrs, q, gotNodesResult) |
| }) |
| q.await(function (error) { |
| if (aggregate) |
| self.aggregateNodeInfo(nodeNames, entity, selectedNodeId, responses, callback); |
| else { |
| callback(nodeNames, entity, responses) |
| } |
| }) |
| }, |
| |
| aggregateNodeInfo: function(nodeNames, entity, selectedNodeId, responses, callback) { |
| //QDR.log.debug("got all results for " + entity); |
| // aggregate the responses |
| var newResponse = {}; |
| var thisNode = responses[selectedNodeId]; |
| newResponse['attributeNames'] = thisNode.attributeNames; |
| newResponse['results'] = thisNode.results; |
| newResponse['aggregates'] = []; |
| for (var i = 0; i < thisNode.results.length; ++i) { |
| var result = thisNode.results[i]; |
| var vals = []; |
| result.forEach(function(val) { |
| vals.push({ |
| sum: val, |
| detail: [] |
| }) |
| }) |
| newResponse.aggregates.push(vals); |
| } |
| var nameIndex = thisNode.attributeNames.indexOf("name"); |
| var ent = self.schema.entityTypes[entity]; |
| var ids = Object.keys(responses); |
| ids.sort(); |
| ids.forEach(function(id) { |
| var response = responses[id]; |
| var results = response.results; |
| results.forEach(function(result) { |
| // find the matching result in the aggregates |
| var found = newResponse.aggregates.some(function(aggregate, j) { |
| if (aggregate[nameIndex].sum === result[nameIndex]) { |
| // result and aggregate are now the same record, add the graphable values |
| newResponse.attributeNames.forEach(function(key, i) { |
| if (ent.attributes[key] && ent.attributes[key].graph) { |
| if (id != selectedNodeId) |
| aggregate[i].sum += result[i]; |
| } |
| aggregate[i].detail.push({ |
| node: self.nameFromId(id) + ':', |
| val: result[i] |
| }) |
| }) |
| return true; // stop looping |
| } |
| return false; // continute looking for the aggregate record |
| }) |
| if (!found) { |
| // this attribute was not found in the aggregates yet |
| // because it was not in the selectedNodeId's results |
| var vals = []; |
| result.forEach(function(val) { |
| vals.push({ |
| sum: val, |
| detail: [{ |
| node: self.nameFromId(id), |
| val: val |
| }] |
| }) |
| }) |
| newResponse.aggregates.push(vals) |
| } |
| }) |
| }) |
| callback(nodeNames, entity, newResponse); |
| }, |
| |
| |
| getSchema: function(callback) { |
| //QDR.log.info("getting schema"); |
| var ret; |
| self.correlator.request( |
| ret = self.sendMgmtQuery('GET-SCHEMA') |
| ).then(ret.id, function(response) { |
| //QDR.log.info("Got schema response"); |
| // remove deprecated |
| for (var entityName in response.entityTypes) { |
| var entity = response.entityTypes[entityName] |
| if (entity.deprecated) { |
| // deprecated entity |
| delete response.entityTypes[entityName] |
| } else { |
| for (var attributeName in entity.attributes) { |
| var attribute = entity.attributes[attributeName] |
| if (attribute.deprecated) { |
| // deprecated attribute |
| delete response.entityTypes[entityName].attributes[attributeName] |
| } |
| } |
| } |
| } |
| self.schema = response; |
| }, ret.error); |
| callback() |
| }, |
| |
| getNodeInfo: function(nodeName, entity, attrs, q, callback) { |
| //QDR.log.debug("getNodeInfo called with nodeName: " + nodeName + " and entity " + entity); |
| var timedOut = function (q) { |
| q.abort() |
| } |
| var atimer = setTimeout(timedOut, self.timeout * 1000, q); |
| var ret; |
| self.correlator.request( |
| ret = self.sendQuery(nodeName, entity, attrs) |
| ).then(ret.id, function(response) { |
| clearTimeout(atimer) |
| callback(nodeName, entity, response); |
| }, ret.error); |
| }, |
| |
| sendMethod: function(nodeId, entity, attrs, operation, props, callback) { |
| setTimeout(function () { |
| var ret; |
| self.correlator.request( |
| ret = self._sendMethod(nodeId, entity, attrs, operation, props) |
| ).then(ret.id, function(response, context) { |
| callback(nodeId, entity, response, context); |
| }, ret.error); |
| }, 1) |
| }, |
| |
| _fullAddr: function(toAddr) { |
| var toAddrParts = toAddr.split('/'); |
| if (toAddrParts.shift() != "amqp:") { |
| self.topology.error(Error("unexpected format for router address: " + toAddr)); |
| return; |
| } |
| var fullAddr = toAddrParts.join('/'); |
| return fullAddr; |
| }, |
| |
| _sendMethod: function(toAddr, entity, attrs, operation, props) { |
| var ret = { |
| id: self.correlator.corr() |
| }; |
| var fullAddr = self._fullAddr(toAddr); |
| if (!self.sender || !self.sendable) { |
| ret.error = "no sender" |
| return ret; |
| } |
| try { |
| var application_properties = { |
| operation: operation |
| } |
| if (entity) { |
| var ent = self.schema.entityTypes[entity]; |
| var fullyQualifiedType = ent ? ent.fullyQualifiedType : entity; |
| application_properties.type = fullyQualifiedType || entity; |
| } |
| if (attrs.name) |
| application_properties.name = attrs.name; |
| if (props) { |
| jQuery.extend(application_properties, props); |
| } |
| var msg = { |
| body: attrs, |
| properties: { |
| to: fullAddr, |
| reply_to: self.receiver.remote.attach.source.address, |
| correlation_id: ret.id |
| }, |
| application_properties: application_properties |
| } |
| self.sender.send(msg); |
| //console.dump("------- method called -------") |
| //console.dump(msg) |
| } catch (e) { |
| error = "error sending: " + e; |
| QDR.log.error(error) |
| ret.error = error; |
| } |
| return ret; |
| }, |
| |
| sendQuery: function(toAddr, entity, attrs, operation) { |
| operation = operation || "QUERY" |
| var fullAddr = self._fullAddr(toAddr); |
| |
| var body; |
| if (attrs) { |
| body = { |
| "attributeNames": attrs, |
| } |
| } else { |
| body = { |
| "attributeNames": [], |
| } |
| } |
| if (entity[0] === '.') |
| entity = entity.substr(1, entity.length - 1) |
| var prefix = "org.apache.qpid.dispatch." |
| var configs = ["address", "autoLink", "linkRoute"] |
| if (configs.indexOf(entity) > -1) |
| prefix += "router.config." |
| return self._send(body, fullAddr, operation, prefix + entity); |
| }, |
| |
| sendMgmtQuery: function(operation) { |
| return self._send([], "/$management", operation); |
| }, |
| |
| _send: function(body, to, operation, entityType) { |
| var ret = { |
| id: self.correlator.corr() |
| }; |
| if (!self.sender || !self.sendable) { |
| ret.error = "no sender" |
| return ret; |
| } |
| try { |
| var application_properties = { |
| operation: operation, |
| type: "org.amqp.management", |
| name: "self" |
| }; |
| if (entityType) |
| application_properties.entityType = entityType; |
| |
| self.sender.send({ |
| body: body, |
| properties: { |
| to: to, |
| reply_to: self.receiver.remote.attach.source.address, |
| correlation_id: ret.id |
| }, |
| application_properties: application_properties |
| }) |
| } catch (e) { |
| error = "error sending: " + e; |
| QDR.log.error(error) |
| ret.error = error; |
| } |
| return ret; |
| }, |
| |
| disconnect: function() { |
| self.connection.close(); |
| self.connected = false |
| self.errorText = "Disconnected." |
| }, |
| |
| connectionTimer: null, |
| |
| testConnect: function (options, timeout, callback) { |
| var connection; |
| var allowDelete = true; |
| var reconnect = angular.isDefined(options.reconnect) ? options.reconnect : false |
| var baseAddress = options.address + ':' + options.port; |
| var protocol = "ws" |
| if ($location.protocol() === "https") |
| protocol = "wss" |
| QDR.log.info("testConnect called with reconnect " + reconnect + " using " + protocol + " protocol") |
| try { |
| var ws = self.rhea.websocket_connect(WebSocket); |
| connection = self.rhea.connect({ |
| connection_details: ws(protocol + "://" + baseAddress, ["binary"]), |
| reconnect: reconnect, |
| properties: { |
| console_identifier: 'Dispatch console' |
| } |
| } |
| ); |
| } catch (e) { |
| QDR.log.debug("exception caught on test connect " + e) |
| self.errorText = "Connection failed " |
| callback({error: e}) |
| return |
| } |
| // called when initial connecting fails, and when connection is dropped after connecting |
| connection.on('disconnected', function(context) { |
| if (allowDelete) { |
| delete connection |
| connection.options.reconnect = false |
| //QDR.log.info("connection.on(disconnected) called") |
| callback({error: "failed to connect"}) |
| } |
| }) |
| connection.on("connection_open", function (context) { |
| allowDelete = false; |
| callback({connection: connection, context: context}) |
| }) |
| }, |
| |
| connect: function(options) { |
| var connection; |
| self.topologyInitialized = false; |
| if (!self.connected) { |
| var okay = { |
| connection: false, |
| sender: false, |
| receiver: false |
| } |
| var sender, receiver |
| self.connectionError = undefined; |
| |
| var stop = function(context) { |
| //self.stopUpdating(); |
| okay.sender = false; |
| okay.receiver = false; |
| okay.connected = false; |
| self.connected = false; |
| self.sender = null; |
| self.receiver = null; |
| self.sendable = false; |
| self.gotTopology = false; |
| } |
| var maybeStart = function() { |
| if (okay.connection && okay.sender && okay.receiver && self.sendable && !self.connected) { |
| //QDR.log.info("okay to start") |
| self.connected = true; |
| self.connection = connection; |
| self.sender = sender; |
| self.receiver = receiver; |
| self.gotTopology = false; |
| self.onSubscription(); |
| } |
| } |
| var onDisconnect = function() { |
| //QDR.log.warn("Disconnected"); |
| self.connectionError = true; |
| stop(); |
| self.executeDisconnectActions(); |
| } |
| |
| // called after connection.open event is fired or connection error has happened |
| var connectionCallback = function (options) { |
| //QDR.log.info('connectionCallback called') |
| if (!options.error) { |
| //QDR.log.info('there was no error') |
| connection = options.connection |
| self.version = options.context.connection.properties.version |
| QDR.log.debug("connection_opened") |
| okay.connection = true; |
| okay.receiver = false; |
| okay.sender = false; |
| |
| connection.on('disconnected', function(context) { |
| //QDR.log.info("connection.on(disconnected) called") |
| self.errorText = "Unable to connect" |
| onDisconnect(); |
| }) |
| connection.on('connection_close', function(context) { |
| //QDR.log.info("connection closed") |
| self.errorText = "Disconnected" |
| onDisconnect(); |
| }) |
| |
| sender = connection.open_sender(); |
| sender.on('sender_open', function(context) { |
| //QDR.log.info("sender_opened") |
| okay.sender = true |
| maybeStart() |
| }) |
| sender.on('sendable', function(context) { |
| //QDR.log.debug("sendable") |
| self.sendable = true; |
| maybeStart(); |
| }) |
| |
| receiver = connection.open_receiver({ |
| source: { |
| dynamic: true |
| } |
| }); |
| receiver.on('receiver_open', function(context) { |
| //QDR.log.info("receiver_opened") |
| if (receiver.remote && receiver.remote.attach && receiver.remote.attach.source) { |
| okay.receiver = true; |
| maybeStart() |
| } |
| }) |
| receiver.on("message", function(context) { |
| self.correlator.resolve(context); |
| }); |
| } else { |
| //QDR.log.info("there was an error " + options.error) |
| self.errorText = "Unable to connect" |
| onDisconnect(); |
| } |
| } |
| |
| QDR.log.debug("****** calling rhea.connect ********") |
| if (!options.connection) { |
| QDR.log.debug("rhea.connect was not passed an existing connection") |
| options.reconnect = true |
| self.testConnect(options, 5000, connectionCallback) |
| } else { |
| QDR.log.debug("rhea.connect WAS passed an existing connection") |
| connectionCallback(options) |
| } |
| } |
| } |
| } |
| return self; |
| }]); |
| |
| return QDR; |
| |
| }(QDR || {})); |
| |
| (function() { |
| console.dump = function(o) { |
| if (window.JSON && window.JSON.stringify) |
| QDR.log.info(JSON.stringify(o, undefined, 2)); |
| else |
| console.log(o); |
| }; |
| })(); |
| |
| function ngGridFlexibleHeightPlugin (opts) { |
| var self = this; |
| self.grid = null; |
| self.scope = null; |
| self.init = function (scope, grid, services) { |
| self.domUtilityService = services.DomUtilityService; |
| self.grid = grid; |
| self.scope = scope; |
| var recalcHeightForData = function () { setTimeout(innerRecalcForData, 1); }; |
| var innerRecalcForData = function () { |
| var gridId = self.grid.gridId; |
| var footerPanelSel = '.' + gridId + ' .ngFooterPanel'; |
| if (!self.grid.$topPanel || !self.grid.$canvas) |
| return; |
| var extraHeight = self.grid.$topPanel.height() + $(footerPanelSel).height(); |
| var naturalHeight = self.grid.$canvas.height() + 1; |
| if (opts != null) { |
| if (opts.minHeight != null && (naturalHeight + extraHeight) < opts.minHeight) { |
| naturalHeight = opts.minHeight - extraHeight - 2; |
| } |
| if (opts.maxHeight != null && (naturalHeight + extraHeight) > opts.maxHeight) { |
| naturalHeight = opts.maxHeight; |
| } |
| } |
| |
| var newViewportHeight = naturalHeight + 3; |
| if (!self.scope.baseViewportHeight || self.scope.baseViewportHeight !== newViewportHeight) { |
| self.grid.$viewport.css('height', newViewportHeight + 'px'); |
| self.grid.$root.css('height', (newViewportHeight + extraHeight) + 'px'); |
| self.scope.baseViewportHeight = newViewportHeight; |
| self.domUtilityService.RebuildGrid(self.scope, self.grid); |
| } |
| }; |
| self.scope.catHashKeys = function () { |
| var hash = '', |
| idx; |
| for (idx in self.scope.renderedRows) { |
| hash += self.scope.renderedRows[idx].$$hashKey; |
| } |
| return hash; |
| }; |
| self.scope.$watch('catHashKeys()', innerRecalcForData); |
| self.scope.$watch(self.grid.config.data, recalcHeightForData); |
| }; |
| } |
| |
| if (!String.prototype.startsWith) { |
| String.prototype.startsWith = function (searchString, position) { |
| return this.substr(position || 0, searchString.length) === searchString |
| } |
| } |
| |
| if (!String.prototype.endsWith) { |
| String.prototype.endsWith = function(searchString, position) { |
| var subjectString = this.toString(); |
| if (typeof position !== 'number' || !isFinite(position) || Math.floor(position) !== position || position > subjectString.length) { |
| position = subjectString.length; |
| } |
| position -= searchString.length; |
| var lastIndex = subjectString.lastIndexOf(searchString, position); |
| return lastIndex !== -1 && lastIndex === position; |
| }; |
| } |
| |
| // https://tc39.github.io/ecma262/#sec-array.prototype.findIndex |
| if (!Array.prototype.findIndex) { |
| Object.defineProperty(Array.prototype, 'findIndex', { |
| value: function(predicate) { |
| // 1. Let O be ? ToObject(this value). |
| if (this == null) { |
| throw new TypeError('"this" is null or not defined'); |
| } |
| |
| var o = Object(this); |
| |
| // 2. Let len be ? ToLength(? Get(O, "length")). |
| var len = o.length >>> 0; |
| |
| // 3. If IsCallable(predicate) is false, throw a TypeError exception. |
| if (typeof predicate !== 'function') { |
| throw new TypeError('predicate must be a function'); |
| } |
| |
| // 4. If thisArg was supplied, let T be thisArg; else let T be undefined. |
| var thisArg = arguments[1]; |
| |
| // 5. Let k be 0. |
| var k = 0; |
| |
| // 6. Repeat, while k < len |
| while (k < len) { |
| // a. Let Pk be ! ToString(k). |
| // b. Let kValue be ? Get(O, Pk). |
| // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)). |
| // d. If testResult is true, return k. |
| var kValue = o[k]; |
| if (predicate.call(thisArg, kValue, k, o)) { |
| return k; |
| } |
| // e. Increase k by 1. |
| k++; |
| } |
| |
| // 7. Return -1. |
| return -1; |
| } |
| }); |
| } |