You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@qpid.apache.org by ea...@apache.org on 2016/10/25 19:32:10 UTC

qpid-dispatch git commit: DISPATCH-543 Request only info needed to calculate the topology so the graph can be drawn quicker

Repository: qpid-dispatch
Updated Branches:
  refs/heads/master 6e051ce75 -> d2c326ac3


DISPATCH-543 Request only info needed to calculate the topology so the graph can be drawn quicker


Project: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/repo
Commit: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/commit/d2c326ac
Tree: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/tree/d2c326ac
Diff: http://git-wip-us.apache.org/repos/asf/qpid-dispatch/diff/d2c326ac

Branch: refs/heads/master
Commit: d2c326ac35899fa3fba75808270169b6083293f2
Parents: 6e051ce
Author: Ernest Allen <ea...@redhat.com>
Authored: Tue Oct 25 15:31:52 2016 -0400
Committer: Ernest Allen <ea...@redhat.com>
Committed: Tue Oct 25 15:31:52 2016 -0400

----------------------------------------------------------------------
 console/stand-alone/plugin/js/dispatchPlugin.js |   1 +
 console/stand-alone/plugin/js/qdrService.js     | 112 ++--
 console/stand-alone/plugin/js/qdrSettings.js    |   1 +
 console/stand-alone/plugin/js/qdrTopology.js    | 549 ++++++++++---------
 4 files changed, 359 insertions(+), 304 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/d2c326ac/console/stand-alone/plugin/js/dispatchPlugin.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/dispatchPlugin.js b/console/stand-alone/plugin/js/dispatchPlugin.js
index a565768..f3168c3 100644
--- a/console/stand-alone/plugin/js/dispatchPlugin.js
+++ b/console/stand-alone/plugin/js/dispatchPlugin.js
@@ -202,6 +202,7 @@ var QDR = (function(QDR) {
             })
           })
           QDR.log.debug("requesting a topology")
+          QDRService.setUpdateEntities([])
           QDRService.topology.get()
         })
 			});

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/d2c326ac/console/stand-alone/plugin/js/qdrService.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrService.js b/console/stand-alone/plugin/js/qdrService.js
index 68001aa..ed1d2fc 100644
--- a/console/stand-alone/plugin/js/qdrService.js
+++ b/console/stand-alone/plugin/js/qdrService.js
@@ -28,7 +28,7 @@ var QDR = (function(QDR) {
 
       rhea: require("rhea"),
 
-      timeout: 4,             // seconds to wait before assuming a request has failed
+      timeout: 10,             // seconds to wait before assuming a request has failed
       updateInterval: 2000,   // milliseconds between background updates
       connectActions: [],
       disconnectActions: [],
@@ -124,7 +124,7 @@ console.dump(e)
           self.gotTopology = true;
           //$rootScope.$apply();
         } else {
-          QDR.log.debug("topology model was just updated");
+          //QDR.log.debug("topology model was just updated");
         }
         self.executeUpdatedActions();
 
@@ -347,6 +347,12 @@ console.dump(e)
         return identity
       },
 
+      queueDepth: function () {
+        var qdepth = self.maxCorrelatorDepth - self.correlator.depth()
+        if (qdepth <= 0)
+          qdepth = 1;
+        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) {
@@ -363,10 +369,7 @@ console.dump(e)
         if (Object.prototype.toString.call(entities) !== '[object Array]') {
           entities = [entities]
         }
-        var qdepth = self.maxCorrelatorDepth - self.correlator.depth()
-        if (qdepth <= 0)
-          qdepth = 1;
-        var q = queue(qdepth)
+        var q = queue(self.queueDepth())
         for (node in self.topology._nodeInfo) {
           for (var i=0; i<entities.length; ++i) {
             var entity = entities[i]
@@ -378,6 +381,33 @@ console.dump(e)
         })
       },
 
+      // enusre all the topology nones have all these entities
+      ensureAllEntities: function (entityAttribs, callback) {
+        self.ensureEntities(Object.keys(self.topology._nodeInfo), entityAttribs, callback)
+      },
+
+      // ensure these nodes have all these entities. don't fetch unless forced to
+      ensureEntities: function (nodes, entityAttribs, callback) {
+        if (Object.prototype.toString.call(entityAttribs) !== '[object Array]') {
+          entityAttribs = [entityAttribs]
+        }
+        if (Object.prototype.toString.call(nodes) !== '[object Array]') {
+          nodes = [nodes]
+        }
+        var q = 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) {
+          callback();
+        })
+      },
+
       setUpdateEntities: function (entities) {
         self.topology._autoUpdatedEntities = entities
       },
@@ -404,7 +434,7 @@ console.dump(e)
         _waitTimer: null,
         _getTimer: null,
         _autoUpdatedEntities: [],
-        _lastRequestTime: null,
+        q: null,
 
         nodeInfo: function() {
           return self.topology._nodeInfo
@@ -413,8 +443,10 @@ console.dump(e)
         get: function() {
           if (self.topology._gettingTopo) {
             QDR.log.debug("asked to get topology but was already getting it")
-            return;
+            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;
@@ -453,23 +485,21 @@ console.dump(e)
               }
 
               // also refresh any entities that were requested
-              var qdepth = self.maxCorrelatorDepth - self.correlator.depth()
-              if (qdepth <= 0)
-                qdepth = 1;
-              q = queue(qdepth)
-              for (node in self.topology._nodeInfo) {
-                for (var i=0; i<self.topology._autoUpdatedEntities.length; ++i) {
-                  var entity = self.topology._autoUpdatedEntities[i]
-                  self.topology.expect(node, entity)
-                  q.defer(self.ensureNodeInfo, node, entity, [], q)
+              self.topology.q = 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.expect(node, entity)
+                  self.topology.q.defer(self.ensureNodeInfo, node, entity, [], self.topology.q)
                 }
               }
-              self.topology._lastRequestTime = Date.now()
-              self.topology._waitTimer = setTimeout(self.topology.timedOut, self.timeout * 1000, q);
-              q.await(function (error) {
-QDR.log.debug("Done awaiting for topology. error is " + error)
-                if (!error)
-                  self.topology.ondone();
+              clearTimeout(self.topology._waitTimer)
+              self.topology._waitTimer = setTimeout(self.topology.timedOut, self.timeout * 1000, self.topology.q);
+              self.topology.q.await(function (error) {
+//QDR.log.debug("Done awaiting for topology. error is " + error)
+                self.topology._gettingTopo = false;
+                self.topology.ondone(error)
               })
             };
           });
@@ -488,11 +518,11 @@ QDR.log.debug("Done awaiting for topology. error is " + error)
           // note: can't use 'this' in a timeout handler
           self.topology.miniDump("state at timeout");
           // check if _nodeInfo is consistent
-          if (self.topology.isConsistent()) {
+          //if (self.topology.isConsistent()) {
             q.abort()
-            self.topology.ondone();
-            return;
-          }
+            //self.topology.ondone();
+          //  return;
+          //}
           self.topology.onerror(Error("management responses are not consistent"));
         },
         isConsistent: function() {
@@ -524,7 +554,7 @@ QDR.log.debug("Done awaiting for topology. error is " + error)
           return true;
         },
 
-        addNodeInfo: function(id, entity, values) {
+        addNodeInfo: function(id, entity, values, q) {
           if (self.topology._waitTimer)
             clearTimeout(self.topology._waitTimer)
           self.topology._waitTimer = setTimeout(self.topology.timedOut, self.timeout * 1000, q);
@@ -559,13 +589,14 @@ QDR.log.debug("Done awaiting for topology. error is " + error)
           if (self.topology._expected[id].indexOf(key) == -1)
             self.topology._expected[id].push(key);
         },
-        ondone: function() {
+        ondone: function(waserror) {
+          clearTimeout(self.topology._getTimer);
           clearTimeout(self.topology._waitTimer);
           self.topology._waitTimer = null;
-          self.topology._gettingTopo = false;
           if (self.updating)
             self.topology._getTimer = setTimeout(self.topology.get, self.updateInterval);
-          self.notifyTopologyDone();
+          if (!waserror)
+            self.notifyTopologyDone();
         },
         dump: function(prefix) {
           if (prefix)
@@ -589,8 +620,7 @@ QDR.log.debug("Done awaiting for topology. error is " + error)
         onerror: function(err) {
           self.topology._gettingTopo = false;
           QDR.log.debug("Err:" + err);
-          self.executeDisconnectActions();
-
+          //self.executeDisconnectActions();
         }
 
       },
@@ -607,22 +637,12 @@ QDR.log.debug("Done awaiting for topology. error is " + error)
         }, ret.error);
       },
 
-      makeMgmtCalls: function(id) {
-QDR.log.debug("called makeMgmtCalls with id of " + id)
-        var keys = [".router", ".connection", ".container", ".router.node", ".listener", ".router.link"];
-        $.each(keys, function(i, key) {
-          self.topology.expect(id, key);
-          self.getNodeInfo(id, key, [], self.topology.addNodeInfo);
-        });
-      },
-
       // should only be called from a q.defer() statement
       ensureNodeInfo: function (nodeId, entity, attrs, q, callback) {
-        QDR.log.debug("queuing request for " + nodeId + " " + entity)
+        //QDR.log.debug("queuing request for " + nodeId + " " + entity)
         self.getNodeInfo(nodeId, entity, attrs, function (nodeName, dotentity, response) {
-          QDR.log.debug("got response for " + nodeId + " " + entity)
-          self.topology.addNodeInfo(nodeName, dotentity, response)
-          //self.topology._nodeInfo[nodeName][dotentity] = response
+          //QDR.log.debug("got response for " + nodeId + " " + entity)
+          self.topology.addNodeInfo(nodeName, dotentity, response, q)
           callback(null)
         })
         return {

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/d2c326ac/console/stand-alone/plugin/js/qdrSettings.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrSettings.js b/console/stand-alone/plugin/js/qdrSettings.js
index f81818b..e33be52 100644
--- a/console/stand-alone/plugin/js/qdrSettings.js
+++ b/console/stand-alone/plugin/js/qdrSettings.js
@@ -106,6 +106,7 @@ QDR.log.debug(QDR.pluginRoot + "/" + goto)
             })
           })
           QDR.log.debug("requesting a topology")
+          QDRService.setUpdateEntities([])
           QDRService.topology.get()
         })
       });

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/d2c326ac/console/stand-alone/plugin/js/qdrTopology.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrTopology.js b/console/stand-alone/plugin/js/qdrTopology.js
index 44e9d02..36f9292 100644
--- a/console/stand-alone/plugin/js/qdrTopology.js
+++ b/console/stand-alone/plugin/js/qdrTopology.js
@@ -42,6 +42,7 @@ var QDR = (function(QDR) {
     };
     $scope.form = ''
     $scope.$on('showEntityForm', function(event, args) {
+QDR.log.debug("showEntityForm")
       var attributes = args.attributes;
       var entityTypes = QDRService.schema.entityTypes[args.entity].attributes;
       attributes.forEach(function(attr) {
@@ -120,6 +121,7 @@ var QDR = (function(QDR) {
           $scope.linkData = obj.entity.linkData;
           $scope.connectionId = obj.entity.connectionId;
           var visibleLen = Math.min(obj.entity.linkData.length, 10)
+          QDR.log.debug("visibleLen is " + visibleLen)
           var left = parseInt(d3.select('#multiple_details').style("left"))
           var detailsDiv = d3.select('#link_details')
           detailsDiv
@@ -557,9 +559,11 @@ var QDR = (function(QDR) {
         }
       }
 
-      var getLinkDir = function (connection, onode) {
-        // find all the liinks associated with this connection on this router(id)
+      var getLinkDir = function (id, connection, onode) {
         var links = onode[".router.link"]
+        if (!links) {
+          return "unknown"
+        }
         var inCount = 0, outCount = 0
         links.results.forEach( function (linkResult) {
           var link = QDRService.flatten(links.attributeNames, linkResult)
@@ -577,6 +581,7 @@ var QDR = (function(QDR) {
           return "out"
         return connection.dir
       }
+
       var savePositions = function () {
         d3.selectAll('#SVG_ID circle.inter-router')
           .each( function (d) {
@@ -587,65 +592,9 @@ var QDR = (function(QDR) {
             });
           })
       }
-      // initialize the nodes and links array from the QDRService.topology._nodeInfo object
-      var initForceGraph = function() {
-        nodes = [];
-        links = [];
-
-        var oldSelectedNode = selected_node
-        var oldMouseoverNode = mouseover_node
-        mouseover_node = null;
-        selected_node = null;
-        selected_link = null;
-
-        savePositions();
-        d3.select("#SVG_ID").remove();
-        svg = d3.select('#topology')
-          .append('svg')
-          .attr("id", "SVG_ID")
-          .attr('width', width)
-          .attr('height', height)
-          .on("contextmenu", function(d) {
-            if (d3.event.defaultPrevented)
-              return;
-            d3.event.preventDefault();
-            if ($scope.addingNode.step != 0)
-              return;
-            if (d3.select('#svg_context_menu').style('display') !== 'block')
-              $(document).click();
-            d3.select('#svg_context_menu')
-              .style('left', (mouseX + $(document).scrollLeft()) + "px")
-              .style('top', (mouseY + $(document).scrollTop()) + "px")
-              .style('display', 'block');
-          })
-          .on('click', function(d) {
-            removeCrosssection()
-          });
-
-        $(document).keyup(function(e) {
-          if (e.keyCode === 27) {
-            removeCrosssection()
-          }
-        });
-
-        // the legend
-        d3.select("#svg_legend svg").remove();
-        lsvg = d3.select("#svg_legend")
-          .append('svg')
-          .attr('id', 'svglegend')
-        lsvg = lsvg.append('svg:g')
-          .attr('transform', 'translate(' + (radii['inter-router'] + 2) + ',' + (radii['inter-router'] + 2) + ')')
-          .selectAll('g');
-
-        // mouse event vars
-        mousedown_link = null;
-        mousedown_node = null;
-        mouseup_node = null;
 
-        // initialize the list of nodes
+      var initializeNodes = function (nodeInfo, nodeCount, height) {
         var yInit = 10;
-        var nodeInfo = QDRService.topology.nodeInfo();
-        var nodeCount = Object.keys(nodeInfo).length;
         for (var id in nodeInfo) {
           var name = QDRService.nameFromId(id);
           // if we have any new nodes, animate the force graph to position them
@@ -657,15 +606,16 @@ var QDR = (function(QDR) {
               y: 200 + yInit,
               fixed: false
             };
+            yInit *= -1;
           }
           if (position.y > height)
             position.y = 200 - yInit;
           nodes.push(aNode(id, name, "inter-router", nodeInfo, nodes.length, position.x, position.y, undefined, position.fixed));
-          yInit *= -1;
           //QDR.log.debug("adding node " + nodes.length-1);
         }
+      }
 
-        // initialize the list of links
+      var initializeLinks = function (nodeInfo, unknowns) {
         var source = 0;
         var client = 1;
         for (var id in nodeInfo) {
@@ -682,7 +632,7 @@ var QDR = (function(QDR) {
             var dir = connection.dir
             if (role == "inter-router") {
               var connId = connection.container
-              var target = getContainerIndex(connId);
+              var target = getContainerIndex(connId, nodeInfo);
               if (target >= 0)
                 getLink(source, target, dir);
             } else if (role == "normal" || role == "on-demand") {
@@ -705,25 +655,28 @@ var QDR = (function(QDR) {
               var node = aNode(id, name, role, nodeInfo, nodes.length, position.x, position.y, j, position.fixed, properties)
               var nodeType = QDRService.isAConsole(properties, connection.identity, role, node.key) ? "console" : "client"
               if (role === 'normal') {
-                var cdir = getLinkDir(connection, onode)
-                node.user = connection.user
-                node.isEncrypted = connection.isEncrypted
-                node.host = connection.host
-                node.connectionId = connection.identity
-                node.cdir = cdir
-                // determine arrow direction by using the link directions
-                if (!normalsParent[nodeType+cdir]) {
-                  normalsParent[nodeType+cdir] = node;
-                  nodes.push(node);
-                  node.normals = [node];
-                  // now add a link
-                  getLink(source, nodes.length - 1, cdir, "small");
-                  client++;
+                var cdir = getLinkDir(id, connection, onode)
+                if (cdir !== 'unknown') {
+                  node.user = connection.user
+                  node.isEncrypted = connection.isEncrypted
+                  node.host = connection.host
+                  node.connectionId = connection.identity
+                  node.cdir = cdir
+                  // determine arrow direction by using the link directions
+                  if (!normalsParent[nodeType+cdir]) {
+                    normalsParent[nodeType+cdir] = node;
+                    nodes.push(node);
+                    node.normals = [node];
+                    // now add a link
+                    getLink(source, nodes.length - 1, cdir, "small");
+                    client++;
+                  } else {
+                    normalsParent[nodeType+cdir].normals.push(node)
+                  }
                 } else {
-                  normalsParent[nodeType+cdir].normals.push(node)
+                  unknowns.push(node)
                 }
               } else {
-//QDR.log.debug("on-demand client found")
                 nodes.push(node)
                   // now add a link
                 getLink(source, nodes.length - 1, dir, "small");
@@ -733,6 +686,71 @@ var QDR = (function(QDR) {
           }
           source++;
         }
+      }
+
+      // initialize the nodes and links array from the QDRService.topology._nodeInfo object
+      var initForceGraph = function() {
+        nodes = [];
+        links = [];
+
+        var oldSelectedNode = selected_node
+        var oldMouseoverNode = mouseover_node
+        mouseover_node = null;
+        selected_node = null;
+        selected_link = null;
+
+        savePositions();
+        d3.select("#SVG_ID").remove();
+        svg = d3.select('#topology')
+          .append('svg')
+          .attr("id", "SVG_ID")
+          .attr('width', width)
+          .attr('height', height)
+          .on("contextmenu", function(d) {
+            if (d3.event.defaultPrevented)
+              return;
+            d3.event.preventDefault();
+            if ($scope.addingNode.step != 0)
+              return;
+            if (d3.select('#svg_context_menu').style('display') !== 'block')
+              $(document).click();
+            d3.select('#svg_context_menu')
+              .style('left', (mouseX + $(document).scrollLeft()) + "px")
+              .style('top', (mouseY + $(document).scrollTop()) + "px")
+              .style('display', 'block');
+          })
+          .on('click', function(d) {
+            removeCrosssection()
+          });
+
+        $(document).keyup(function(e) {
+          if (e.keyCode === 27) {
+            removeCrosssection()
+          }
+        });
+
+        // the legend
+        d3.select("#svg_legend svg").remove();
+        lsvg = d3.select("#svg_legend")
+          .append('svg')
+          .attr('id', 'svglegend')
+        lsvg = lsvg.append('svg:g')
+          .attr('transform', 'translate(' + (radii['inter-router'] + 2) + ',' + (radii['inter-router'] + 2) + ')')
+          .selectAll('g');
+
+        // mouse event vars
+        mousedown_link = null;
+        mousedown_node = null;
+        mouseup_node = null;
+
+        // initialize the list of nodes
+        var nodeInfo = QDRService.topology.nodeInfo();
+        var nodeCount = Object.keys(nodeInfo).length;
+        initializeNodes(nodeInfo, nodeCount, height)
+
+        // initialize the list of links
+        var unknowns = []
+        initializeLinks(nodeInfo, unknowns)
 
         $scope.schema = QDRService.schema;
         // init D3 force layout
@@ -744,13 +762,13 @@ var QDR = (function(QDR) {
             if (d.target.nodeType === 'inter-router')
               return 80
             if (d.target.cdir === 'both')
-              return 70
-            return 70
+              return 25
+            return 15
           })
           .charge(function(d) {
-            return (d.nodeType === 'inter-router') ? -3000 : -300
+            return (d.nodeType === 'inter-router') ? -3000 : -750
           })
-          .friction(.10)
+          .friction(.50)
           .gravity(0.0001)
           .on('tick', tick)
           .start()
@@ -821,94 +839,93 @@ var QDR = (function(QDR) {
           d3.selectAll('circle.inter-router').each(function (d) {
             if (d.key === oldMouseoverNode.key) {
               mouseover_node = d
-              QDRService.initEntity(".router.node", function () {
+              QDRService.ensureAllEntities([{entity: ".router.node", attrs: ["id","nextHop"]}], function () {
                 nextHop(selected_node, d);
                 restart();
               })
             }
           })
         }
-
-        setTimeout(function() {
+        setTimeout(function () {
           updateForm(Object.keys(QDRService.topology.nodeInfo())[0], 'router', 0);
-        }, 10)
+        })
+
+        // if any clients don't yet have link directions, get the links for those nodes and restart the graph
+        var unknownNodes = unknowns.map( function (un) {
+          return un.key
+        })
+        if (unknownNodes.length) {
+          QDRService.ensureEntities(unknownNodes, {entity: ".router.link"}, function () {
+            // now that all nodes with clients have link info, update the nodes
+            animate = true;
+            initForceGraph();
+          })
+        }
       }
 
       function updateForm(key, entity, resultIndex) {
         var nodeInfo = QDRService.topology.nodeInfo();
-        var onode = nodeInfo[key]
-        if (onode) {
-          var nodeResults = onode['.' + entity].results[resultIndex]
-          var nodeAttributes = onode['.' + entity].attributeNames
-          var attributes = nodeResults.map(function(row, i) {
-              return {
-                attributeName: nodeAttributes[i],
-                attributeValue: row
-              }
+        if (key in nodeInfo) {
+          QDRService.ensureEntities(key, [
+            {entity: '.'+entity},
+            {entity: '.listener', attrs: ["role", "port"]}], function () {
+            var onode = nodeInfo[key]
+            var nodeResults = onode['.' + entity].results[resultIndex]
+            var nodeAttributes = onode['.' + entity].attributeNames
+            var attributes = nodeResults.map(function(row, i) {
+                return {
+                  attributeName: nodeAttributes[i],
+                  attributeValue: row
+                }
+              })
+              // sort by attributeName
+            attributes.sort(function(a, b) {
+              return a.attributeName.localeCompare(b.attributeName)
             })
-            // sort by attributeName
-          attributes.sort(function(a, b) {
-            return a.attributeName.localeCompare(b.attributeName)
-          })
 
-          // move the Name first
-          var nameIndex = attributes.findIndex(function(attr) {
-            return attr.attributeName === 'name'
-          })
-          if (nameIndex >= 0)
-            attributes.splice(0, 0, attributes.splice(nameIndex, 1)[0]);
-
-          // get the list of ports this router is listening on
-          if (entity === 'router') {
-            var listeners = onode['.listener'].results;
-            var listenerAttributes = onode['.listener'].attributeNames;
-            var normals = listeners.filter(function(listener) {
-              return QDRService.valFor(listenerAttributes, listener, 'role') === 'normal';
+            // move the Name first
+            var nameIndex = attributes.findIndex(function(attr) {
+              return attr.attributeName === 'name'
             })
-            var ports = []
-            normals.forEach(function(normalListener) {
-                ports.push(QDRService.valFor(listenerAttributes, normalListener, 'port'))
+            if (nameIndex >= 0)
+              attributes.splice(0, 0, attributes.splice(nameIndex, 1)[0]);
+
+            // get the list of ports this router is listening on
+            if (entity === 'router') {
+              var listeners = onode['.listener'].results;
+              var listenerAttributes = onode['.listener'].attributeNames;
+              var normals = listeners.filter(function(listener) {
+                return QDRService.valFor(listenerAttributes, listener, 'role') === 'normal';
               })
-              // add as 2nd row
-            if (ports.length) {
-              attributes.splice(1, 0, {
-                attributeName: 'Listening on',
-                attributeValue: ports,
-                description: 'The port on which this router is listening for connections'
-              });
+              var ports = []
+              normals.forEach(function(normalListener) {
+                  ports.push(QDRService.valFor(listenerAttributes, normalListener, 'port'))
+                })
+                // add as 2nd row
+              if (ports.length) {
+                attributes.splice(1, 0, {
+                  attributeName: 'Listening on',
+                  attributeValue: ports,
+                  description: 'The port on which this router is listening for connections'
+                });
+              }
             }
-          }
-          $scope.$broadcast('showEntityForm', {
-            entity: entity,
-            attributes: attributes
+            $scope.$broadcast('showEntityForm', {
+              entity: entity,
+              attributes: attributes
+            })
+            if (!$scope.$$phase) $scope.$apply()
           })
         }
-        if (!$scope.$$phase) $scope.$apply()
       }
 
-      function getContainerIndex(_id) {
+      function getContainerIndex(_id, nodeInfo) {
         var nodeIndex = 0;
-        var nodeInfo = QDRService.topology.nodeInfo();
         for (var id in nodeInfo) {
-          var node = nodeInfo[id]['.router'];
-          // there should be only one router entity for each node, so using results[0] should be fine
-          if (QDRService.valFor(node.attributeNames, node.results[0], "id") === _id)
-            return nodeIndex;
-          if (QDRService.valFor(node.attributeNames, node.results[0], "routerId") === _id)
+          if (QDRService.nameFromId(id) === _id)
             return nodeIndex;
-          nodeIndex++
-        }
-        // there was no router.id that matched, check deprecated router.routerId
-        nodeIndex = 0;
-        for (var id in nodeInfo) {
-          var node = nodeInfo[id]['.container'];
-          if (node) {
-            if (QDRService.valFor(node.attributeNames, node.results[0], "containerName") === _id)
-              return nodeIndex;
-          }
-          nodeIndex++
+          ++nodeIndex;
         }
-        //QDR.log.warn("unable to find containerIndex for " + _id);
         return -1;
       }
 
@@ -1335,7 +1352,7 @@ var QDR = (function(QDR) {
 
               svgg.transition().attr("transform", "translate(2,2) scale(1)")
             }
-            QDRService.loadEntity(".router.link", showCrosssection)
+            QDRService.initEntity(".router.link", showCrosssection)
           })
 
         // remove old links
@@ -1446,7 +1463,7 @@ var QDR = (function(QDR) {
             }
             clerAllHighlights()
             // we need .router.node info to highlight hops
-            QDRService.initEntity(".router.node", function () {
+            QDRService.ensureAllEntities([{entity: ".router.node", attrs: ["id","nextHop"]}], function () {
               mouseover_node = d  // save this node in case the topology changes so we can restore the highlights
               nextHop(selected_node, d);
               restart();
@@ -1663,13 +1680,18 @@ var QDR = (function(QDR) {
           node.cdir = "out"
           legendNodes.push(node)
         }
+        if (!svg.selectAll('circle.client.inout').empty()) {
+          var node = aNode("Sender/Receiver", "", "normal", undefined, 4, 0, 0, 0, false, {})
+          node.cdir = "both"
+          legendNodes.push(node)
+        }
         if (!svg.selectAll('circle.qpid-cpp').empty()) {
-          legendNodes.push(aNode("Qpid broker", "", "on-demand", undefined, 4, 0, 0, 0, false, {
+          legendNodes.push(aNode("Qpid broker", "", "on-demand", undefined, 5, 0, 0, 0, false, {
             product: 'qpid-cpp'
           }))
         }
         if (!svg.selectAll('circle.artemis').empty()) {
-          legendNodes.push(aNode("Artemis broker", "", "on-demand", undefined, 5, 0, 0, 0, false, {}))
+          legendNodes.push(aNode("Artemis broker", "", "on-demand", undefined, 6, 0, 0, 0, false, {}))
         }
         lsvg = lsvg.data(legendNodes, function(d) {
           return d.id;
@@ -1721,118 +1743,123 @@ var QDR = (function(QDR) {
       }
 
       var startUpdateConnectionsGrid = function(d) {
+        // called after each topology update
         var extendConnections = function() {
-          $scope.multiData = []
-          var normals = d.normals;
-          // find updated normals for d
-          d3.selectAll('.normal')
-            .each(function(newd) {
-              if (newd.id == d.id && newd.name == d.name) {
-                normals = newd.normals;
-              }
-            });
-          if (normals) {
-            normals.forEach(function(n) {
-              var nodeInfo = QDRService.topology.nodeInfo();
-              var links = nodeInfo[n.key]['.router.link'];
-              var linkTypeIndex = links.attributeNames.indexOf('linkType');
-              var connectionIdIndex = links.attributeNames.indexOf('connectionId');
-              n.linkData = [];
-              links.results.forEach(function(link) {
-                if (link[linkTypeIndex] === 'endpoint' && link[connectionIdIndex] === n.connectionId) {
-                  var l = {};
-                  l.owningAddr = QDRService.valFor(links.attributeNames, link, 'owningAddr');
-                  l.dir = QDRService.valFor(links.attributeNames, link, 'linkDir');
-                  if (l.owningAddr && l.owningAddr.length > 2)
-                    if (l.owningAddr[0] === 'M')
-                      l.owningAddr = l.owningAddr.substr(2)
-                    else
-                      l.owningAddr = l.owningAddr.substr(1)
-
-                  l.deliveryCount = QDRService.pretty(QDRService.valFor(links.attributeNames, link, 'deliveryCount'));
-                  l.uncounts = QDRService.pretty(QDRService.valFor(links.attributeNames, link, 'undeliveredCount') +
-                      QDRService.valFor(links.attributeNames, link, 'unsettledCount'))
-                    //l.undeliveredCount = QDRService.pretty(QDRService.valFor(links.attributeNames, link, 'undeliveredCount'));
-                    //l.unsettledCount = QDRService.pretty(QDRService.valFor(links.attributeNames, link, 'unsettledCount'));
-                  l.adminStatus = QDRService.valFor(links.attributeNames, link, 'adminStatus');
-                  l.operStatus = QDRService.valFor(links.attributeNames, link, 'operStatus');
-                  l.identity = QDRService.valFor(links.attributeNames, link, 'identity')
-                  l.connectionId = QDRService.valFor(links.attributeNames, link, 'connectionId')
-                  l.nodeId = n.key
-                  l.type = QDRService.valFor(links.attributeNames, link, 'type')
-                  l.name = QDRService.valFor(links.attributeNames, link, 'name')
-
-                  // TODO: remove this fake quiescing/reviving logic when the routers do the work
-                  initConnState(n.connectionId)
-                  if ($scope.quiesceState[n.connectionId].linkStates[l.identity])
-                    l.adminStatus = $scope.quiesceState[n.connectionId].linkStates[l.identity];
-                  if ($scope.quiesceState[n.connectionId].state == 'quiescing') {
-                    if (l.adminStatus === 'enabled') {
-                      // 25% chance of switching
-                      var chance = Math.floor(Math.random() * 2);
-                      if (chance == 1) {
-                        l.adminStatus = 'disabled';
-                        $scope.quiesceState[n.connectionId].linkStates[l.identity] = 'disabled';
+          // force a fetch of the links for this node
+          QDRService.ensureEntities(d.key, {entity: ".router.link", force: true}, function () {
+            // the links for this node are now available
+            $scope.multiData = []
+            var normals = d.normals;
+            // find updated normals for d
+            d3.selectAll('.normal')
+              .each(function(newd) {
+                if (newd.id == d.id && newd.name == d.name) {
+                  normals = newd.normals;
+                }
+              });
+            if (normals) {
+              normals.forEach(function(n) {
+                var nodeInfo = QDRService.topology.nodeInfo();
+                var links = nodeInfo[n.key]['.router.link'];
+                var linkTypeIndex = links.attributeNames.indexOf('linkType');
+                var connectionIdIndex = links.attributeNames.indexOf('connectionId');
+                n.linkData = [];
+                links.results.forEach(function(link) {
+                  if (link[linkTypeIndex] === 'endpoint' && link[connectionIdIndex] === n.connectionId) {
+                    var l = {};
+                    l.owningAddr = QDRService.valFor(links.attributeNames, link, 'owningAddr');
+                    l.dir = QDRService.valFor(links.attributeNames, link, 'linkDir');
+                    if (l.owningAddr && l.owningAddr.length > 2)
+                      if (l.owningAddr[0] === 'M')
+                        l.owningAddr = l.owningAddr.substr(2)
+                      else
+                        l.owningAddr = l.owningAddr.substr(1)
+
+                    l.deliveryCount = QDRService.pretty(QDRService.valFor(links.attributeNames, link, 'deliveryCount'));
+                    l.uncounts = QDRService.pretty(QDRService.valFor(links.attributeNames, link, 'undeliveredCount') +
+                        QDRService.valFor(links.attributeNames, link, 'unsettledCount'))
+                      //l.undeliveredCount = QDRService.pretty(QDRService.valFor(links.attributeNames, link, 'undeliveredCount'));
+                      //l.unsettledCount = QDRService.pretty(QDRService.valFor(links.attributeNames, link, 'unsettledCount'));
+                    l.adminStatus = QDRService.valFor(links.attributeNames, link, 'adminStatus');
+                    l.operStatus = QDRService.valFor(links.attributeNames, link, 'operStatus');
+                    l.identity = QDRService.valFor(links.attributeNames, link, 'identity')
+                    l.connectionId = QDRService.valFor(links.attributeNames, link, 'connectionId')
+                    l.nodeId = n.key
+                    l.type = QDRService.valFor(links.attributeNames, link, 'type')
+                    l.name = QDRService.valFor(links.attributeNames, link, 'name')
+
+                    // TODO: remove this fake quiescing/reviving logic when the routers do the work
+                    initConnState(n.connectionId)
+                    if ($scope.quiesceState[n.connectionId].linkStates[l.identity])
+                      l.adminStatus = $scope.quiesceState[n.connectionId].linkStates[l.identity];
+                    if ($scope.quiesceState[n.connectionId].state == 'quiescing') {
+                      if (l.adminStatus === 'enabled') {
+                        // 25% chance of switching
+                        var chance = Math.floor(Math.random() * 2);
+                        if (chance == 1) {
+                          l.adminStatus = 'disabled';
+                          $scope.quiesceState[n.connectionId].linkStates[l.identity] = 'disabled';
+                        }
                       }
                     }
-                  }
-                  if ($scope.quiesceState[n.connectionId].state == 'reviving') {
-                    if (l.adminStatus === 'disabled') {
-                      // 25% chance of switching
-                      var chance = Math.floor(Math.random() * 2);
-                      if (chance == 1) {
-                        l.adminStatus = 'enabled';
-                        $scope.quiesceState[n.connectionId].linkStates[l.identity] = 'enabled';
+                    if ($scope.quiesceState[n.connectionId].state == 'reviving') {
+                      if (l.adminStatus === 'disabled') {
+                        // 25% chance of switching
+                        var chance = Math.floor(Math.random() * 2);
+                        if (chance == 1) {
+                          l.adminStatus = 'enabled';
+                          $scope.quiesceState[n.connectionId].linkStates[l.identity] = 'enabled';
+                        }
                       }
                     }
-                  }
-                  QDR.log.debug("pushing link state for " + l.owningAddr + " status: " + l.adminStatus)
+                    QDR.log.debug("pushing link state for " + l.owningAddr + " status: " + l.adminStatus)
 
-                  n.linkData.push(l)
-                }
+                    n.linkData.push(l)
+                  }
+                })
+                $scope.multiData.push(n)
+                if (n.connectionId == $scope.connectionId)
+                  $scope.linkData = n.linkData;
+                initConnState(n.connectionId)
+                $scope.multiDetails.updateState(n)
               })
-              $scope.multiData.push(n)
-              if (n.connectionId == $scope.connectionId)
-                $scope.linkData = n.linkData;
-              initConnState(n.connectionId)
-              $scope.multiDetails.updateState(n)
-            })
-          }
-          $scope.$apply();
+            }
+            $scope.$apply();
 
-          d3.select('#multiple_details')
-            .style({
-              height: ((normals.length + 1) * 30) + 40 + "px",
-              'overflow-y': normals.length > 10 ? 'scroll' : 'hidden'
-            })
+            d3.select('#multiple_details')
+              .style({
+                height: ((normals.length + 1) * 30) + 40 + "px",
+                'overflow-y': normals.length > 10 ? 'scroll' : 'hidden'
+              })
+          })
         }
-
-        QDRService.addUpdateEntity(".router.link")
-        QDRService.loadEntity(".router.link", function () {
-          QDRService.addUpdatedAction("normalsStats", extendConnections)
-          extendConnections();
-          clearPopups();
-          var display = 'block'
-          var left = mouseX + $(document).scrollLeft()
-          if (d.normals.length === 1) {
-            display = 'none'
-            left = left - 30;
-            mouseY = mouseY - 20
-          }
-          d3.select('#multiple_details')
-            .style({
-              display: display,
-              opacity: 1,
-              left: (mouseX + $(document).scrollLeft()) + "px",
-              top: (mouseY + $(document).scrollTop()) + "px"
-            })
-          if (d.normals.length === 1) {
-            // simulate a click on the connection to popup the link details
+        // register a notification function for when the topology is updated
+        QDRService.addUpdatedAction("normalsStats", extendConnections)
+        // call the function that gets the links right now
+        extendConnections();
+        clearPopups();
+        var display = 'block'
+        var left = mouseX + $(document).scrollLeft()
+        if (d.normals.length === 1) {
+          display = 'none'
+          left = left - 30;
+          mouseY = mouseY - 20
+        }
+        d3.select('#multiple_details')
+          .style({
+            display: display,
+            opacity: 1,
+            left: (mouseX + $(document).scrollLeft()) + "px",
+            top: (mouseY + $(document).scrollTop()) + "px"
+          })
+        if (d.normals.length === 1) {
+          // simulate a click on the connection to popup the link details
+          QDRService.ensureEntities(d.key, {entity: ".router.link", force: true}, function () {
             $scope.multiDetails.showLinksList({
               entity: d
             })
-          }
-        })
+          })
+        }
       }
       var stopUpdateConnectionsGrid = function() {
         QDRService.delUpdateEntity([".router.link"])
@@ -1941,29 +1968,33 @@ var QDR = (function(QDR) {
       // the scope
       $scope.$on("$destroy", function(event) {
         //QDR.log.debug("scope on destroy");
+        savePositions();
         QDRService.stopUpdating();
+        QDRService.delUpdatedAction("normalsStats");
         QDRService.delUpdatedAction("topology");
         d3.select("#SVG_ID").remove();
         window.removeEventListener('resize', resize);
       });
 
       function handleInitialUpdate() {
-        QDRService.delUpdatedAction("topologyInitialized")
-        // now we only need to update connections during steady-state
+        // we only need to update connections during steady-state
         QDRService.setUpdateEntities([".connection"])
         // we currently have all entities available on all routers
         saveChanged();
         animate = true;
         initForceGraph();
-        QDRService.initEntity(".router.node", function () {})
+        // after the graph is displayed fetch all .router.node info. This is done so highlighting between nodes
+        // doesn't incur a delay
+        QDRService.ensureAllEntities([{entity: ".router.node", attrs: ["id","nextHop"]}], function () {})
+        // call this function every time a background update is done
         QDRService.addUpdatedAction("topology", function() {
           var changed = hasChanged()
-          // there is a new node, we need to get all of it's entities before drawing the svg
+          // there is a new node, we need to get all of it's entities before drawing the graph
           if (changed > 0) {
             QDRService.delUpdatedAction("topology")
             setupInitialUpdate()
           } else if (changed === -1) {
-            // we lost a node, we can draw the new svg immediately
+            // we lost a node (or a client), we can draw the new svg immediately
             saveChanged();
             animate = true;
             initForceGraph();
@@ -1975,9 +2006,11 @@ var QDR = (function(QDR) {
       }
 
       function setupInitialUpdate() {
-//QDR.log.debug("setting up initial update for topology. requesting all entities for all routers")
-        QDRService.setUpdateEntities([".connection", ".router.link", ".router", ".listener"])
-        QDRService.addUpdatedAction("topologyInitialized", handleInitialUpdate)
+        // make sure all router nodes have .connection info. if not then fetch any missing info
+        QDRService.ensureAllEntities(
+          //[{entity: ".connection"}, {entity: ".router.link", attrs: ["linkType","connectionId","linkDir"]}],
+          [{entity: ".connection"}],
+            handleInitialUpdate)
       }
       setupInitialUpdate();
       QDRService.startUpdating();


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@qpid.apache.org
For additional commands, e-mail: commits-help@qpid.apache.org