qpid-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From eal...@apache.org
Subject [03/11] qpid-dispatch git commit: DISPATCH-316 Hawtio and stand-alone consoles now use common code
Date Fri, 19 Aug 2016 16:59:10 GMT
http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/d98ae37e/console/stand-alone/plugin/js/qdrOverview.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrOverview.js b/console/stand-alone/plugin/js/qdrOverview.js
index d787a5e..d16b8e2 100644
--- a/console/stand-alone/plugin/js/qdrOverview.js
+++ b/console/stand-alone/plugin/js/qdrOverview.js
@@ -34,99 +34,118 @@ var QDR = (function (QDR) {
    *
    * Controller that handles the QDR overview page
    */
-	QDR.module.controller("QDR.OverviewController", ['$scope', 'uiGridConstants', 'QDRService', function($scope, uiGridConstants, QDRService) {
+	QDR.module.controller("QDR.OverviewController", ['$scope', 'QDRService', '$location', '$timeout', function($scope, QDRService, $location, $timeout) {
 
+		console.log("QDR.OverviewControll started with location of " + $location.path() + " and connection of  " + QDRService.connected);
+		var columnStateKey = 'QDRColumnKey.';
+		var OverviewExpandedKey = "QDROverviewExpanded"
+		var OverviewActivatedKey = "QDROverviewActivated"
+		var treeRoot;   // the dynatree root node. initialized once log data is received
 
-		if (!angular.isDefined(QDRService.schema))
-		    return;
-
-		var nodeIds = QDRService.nodeIdList();
-		var currentTimer;
+		// we want attributes to be listed first, so add it at index 0
+		$scope.subLevelTabs = [{
+		    content: '<i class="icon-list"></i> Attributes',
+		    title: "View the attribute values on your selection",
+		    isValid: function (workspace) { return true; },
+		    href: function () { return "#/" + QDR.pluginName + "/attributes"; },
+		    index: 0
+		},
+		{
+		    content: '<i class="icon-leaf"></i> Operations',
+		    title: "Execute operations on your selection",
+		    isValid: function (workspace) { return true; },
+		    href: function () { return "#/" + QDR.pluginName + "/operations"; },
+		    index: 1
+		}]
+		$scope.activeTab = $scope.subLevelTabs[0];
+		$scope.setActive = function (nav) {
+			$scope.activeTab = nav;
+		}
+		$scope.isValid = function (nav) {
+			return nav.isValid()
+		}
+		$scope.isActive = function (nav) {
+			return nav == $scope.activeTab;
+		}
+		$scope.linkFields = []
+		$scope.link = null;
 		var refreshInterval = 5000
-		var Folder = (function () {
-            function Folder(title) {
-                this.title = title;
-				this.children = [];
-				this.folder = true;
-            }
-            return Folder;
-        })();
-		var Leaf = (function () {
-            function Leaf(title) {
-                this.title = title;
-            }
-            return Leaf;
-        })();
 	    $scope.modes = [
 	    	{title: 'Overview', name: 'Overview', right: false}
 	    	];
 
+		$scope.tmplOverviewTree = QDR.templatePath + 'tmplOverviewTree.html';
 		$scope.templates =
-		    [ { name: 'Routers', url: 'routers.html'},
+		[     { name: 'Routers', url: 'routers.html'},
 		      { name: 'Router', url: 'router.html'},
               { name: 'Addresses', url: 'addresses.html'},
 		      { name: 'Address', url: 'address.html'},
+              { name: 'Links', url: 'links.html'},
+		      { name: 'Link', url: 'link.html'},
               { name: 'Connections', url: 'connections.html'},
 		      { name: 'Connection', url: 'connection.html'},
               { name: 'Logs', url: 'logs.html'},
-              { name: 'Log', url: 'log.html'} ];
+              { name: 'Log', url: 'log.html'}
+        ];
+		var topLevelChildren = [];
 
-		$scope.getGridHeight = function (data) {
-	       // add 1 for the header row
-	       return {height: (($scope[data.data].length + 1) * 30) + "px"};
+		$scope.allRouterSelected = function (row ) {
+			console.log("row selected" + row)
 		}
-		$scope.overview = new Folder("Overview");
+		function afterSelectionChange(rowItem, checkAll) {
+			var nodeId = rowItem.entity.nodeId;
+			$("#overtree").dynatree("getTree").activateKey(nodeId);
+        }
 
 		$scope.allRouterFields = [];
-		var allRouterCols = [
-			 {
-				 field: 'routerId',
-				 displayName: 'Router'
-			 },
-			 {
-				 field: 'area',
-				 displayName: 'Area'
-			 },
-			 {
-				 field: 'mode',
-				 displayName: 'Mode'
-			 },
-			 {
-				 field: 'connections',
-				 displayName: 'External connections'
-			 },
-             {
-                 field: 'addrCount',
-                 displayName: 'Address count'
-             },
-             {
-                 field: 'linkCount',
-                 displayName: 'Link count'
-             }
-		];
+		$scope.allRouterSelections = [];
 		$scope.allRouters = {
+		    saveKey: 'allRouters',
 			data: 'allRouterFields',
-			columnDefs: allRouterCols,
-			enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER,
-            enableVerticalScrollbar: uiGridConstants.scrollbars.NEVER,
+			columnDefs: [
+                 {
+                     field: 'routerId',
+                     saveKey: 'allRouters',
+                     displayName: 'Router'
+                 },
+                 {
+                     field: 'area',
+                     displayName: 'Area'
+                 },
+                 {
+                     field: 'mode',
+                     displayName: 'Mode'
+                 },
+                 {
+                     field: 'connections',
+                     displayName: 'External connections'
+                 },
+                 {
+                     field: 'addrCount',
+                     displayName: 'Address count'
+                 },
+                 {
+                     field: 'linkCount',
+                     displayName: 'Link count'
+                 }
+            ],
 			enableColumnResize: true,
 			multiSelect: false,
-			enableRowHeaderSelection: false,
-			noUnselect: true,
-			enableSelectAll: false,
-			enableRowSelection: true,
-			onRegisterApi: function (gridApi) {
-				gridApi.selection.on.rowSelectionChanged($scope, function(row) {
-					if (row.isSelected) {
-						var nodeId = row.entity.nodeId;
-						$("#overtree").fancytree("getTree").activateKey(nodeId);
-					}
-				});
-		    }
+			selectedItems: $scope.allRouterSelections,
+			afterSelectionChange: function(data) {
+				if (data.selected) {
+					var selItem = $scope.allRouterSelections[0]
+					var nodeId = selItem.nodeId
+					// activate Routers->nodeId in the tree
+					$("#overtree").dynatree("getTree").activateKey(nodeId);
+
+				}
+            }
 		};
+
 		// get info for all routers
 		var allRouterInfo = function () {
-			nodeIds = QDRService.nodeIdList()
+			var nodeIds = QDRService.nodeIdList()
 			var expected = Object.keys(nodeIds).length
 			var received = 0;
 			var allRouterFields = [];
@@ -145,63 +164,59 @@ var QDR = (function (QDR) {
 				if (expected == received) {
 					allRouterFields.sort ( function (a,b) { return a.routerId < b.routerId ? -1 : a.routerId > b.routerId ? 1 : 0})
 					// now get each router's node info
-					QDRService.getMultipleNodeInfo(nodeIds, "router", [], function (nodeIds, entity, response) {
-						var results = response.aggregates
-						results.forEach ( function (result) {
-
-							var routerId = QDRService.valFor(response.attributeNames, result, "routerId").sum
+					QDRService.getMultipleNodeInfo(nodeIds, "router", [], function (nodeIds, entity, responses) {
+						for(var r in responses) {
+							var result = responses[r]
+							var routerId = QDRService.valFor(result.attributeNames, result.results[0], "routerId")
 							allRouterFields.some( function (connField) {
 								if (routerId === connField.routerId) {
-									response.attributeNames.forEach ( function (attrName) {
-										connField[attrName] = QDRService.valFor(response.attributeNames, result, attrName).sum
+									result.attributeNames.forEach ( function (attrName) {
+										connField[attrName] = QDRService.valFor(result.attributeNames, result.results[0], attrName)
 									})
 									return true
 								}
 								return false
 							})
-						})
-						$scope.allRouterFields = allRouterFields
-						$scope.$apply()
-						if (currentTimer) {
-							clearTimeout(currentTimer)
 						}
-						currentTimer = setTimeout(allRouterInfo, refreshInterval);
-					}, nodeIds[0])
+						$scope.allRouterFields = allRouterFields
+					}, nodeIds[0], false)
 				}
 			}
-			nodeIds.forEach ( function (nodeId) {
+			nodeIds.forEach ( function (nodeId, i) {
 				QDRService.getNodeInfo(nodeId, ".connection", ["role"], gotNodeInfo)
 			})
-
+			loadColState($scope.allRouters)
+			updateRouterTree(nodeIds)
 		}
 
+		$scope.routerFields = []
+        $scope.routerGrid = {
+            saveKey: 'routerGrid',
+            data: 'routerFields',
+            columnDefs: [
+                 {
+                     field: 'attribute',
+                     displayName: 'Attribute',
+                     saveKey: 'routerGrid',
+                     width: '40%'
+                 },
+                 {
+                     field: 'value',
+                     displayName: 'Value',
+                     width: '40%'
+                 }
+            ],
+            enableColumnResize: true,
+            multiSelect: false
+        }
+
 		// get info for a single router
 		var routerInfo = function (node) {
 			$scope.router = node
-			$scope.routerFields = []
-			var cols = [
-				 {
-					 field: 'attribute',
-					 displayName: 'Attribute',
-					 width: '40%'
-				 },
-				 {
-					 field: 'value',
-					 displayName: 'Value',
-					 width: '40%'
-				 }
-			]
-			$scope.routerGrid = {
-				data: 'routerFields',
-				columnDefs: cols,
-				enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER,
-	            enableVerticalScrollbar: uiGridConstants.scrollbars.NEVER,
-				enableColumnResize: true,
-				multiSelect: false
-			}
 
+			$scope.routerFields = [];
 			$scope.allRouterFields.some( function (field) {
-				if (field.routerId === node.title) {
+				if (field.routerId === node.data.title) {
 					Object.keys(field).forEach ( function (key) {
 						if (key !== '$$hashKey')
 							$scope.routerFields.push({attribute: key, value: field[key]})
@@ -209,90 +224,90 @@ var QDR = (function (QDR) {
 					return true
 				}
 			})
-
-			$scope.$apply()
-			if (currentTimer) {
-				clearTimeout(currentTimer)
-				currentTimer = null
-			}
+	        loadColState($scope.routerGrid);
 		}
 
-		// get info for a all addresses
-		var allAddressInfo = function () {
-			$scope.addressFields = []
-			var addressCols = [
-				 {
-					 field: 'address',
-					 displayName: 'address'
-				 },
-				 {
-					 field: 'class',
-					 displayName: 'class'
-				 },
-				 {
-					 field: 'phase',
-					 displayName: 'phase',
-					 cellClass: 'grid-align-value'
-				 },
-				 {
-					 field: 'inproc',
-					 displayName: 'in-proc'
-				 },
-				 {
-					 field: 'local',
-					 displayName: 'local',
-					 cellClass: 'grid-align-value'
-				 },
-				 {
-					 field: 'remote',
-					 displayName: 'remote',
-					 cellClass: 'grid-align-value'
-				 },
-				 {
-					 field: 'in',
-					 displayName: 'in',
-					 cellClass: 'grid-align-value'
-				 },
-				 {
-					 field: 'out',
-					 displayName: 'out',
-					 cellClass: 'grid-align-value'
-				 }
-			]
-			$scope.selectedAddresses = []
-			$scope.addressGrid = {
-				data: 'addressFields',
-				columnDefs: addressCols,
-				enableColumnResize: true,
-				enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER,
-	            enableVerticalScrollbar: uiGridConstants.scrollbars.NEVER,
-				multiSelect: false,
-				enableRowHeaderSelection: false,
-				noUnselect: true,
-				enableSelectAll: false,
-				enableRowSelection: true,
-				onRegisterApi: function (gridApi) {
-					gridApi.selection.on.rowSelectionChanged($scope, function(row) {
-						if (row.isSelected) {
-							var key = row.entity.uid;
-							$("#overtree").fancytree("getTree").activateKey(key);
-						}
-					});
-			    }
-			}
+		$scope.addressesData = []
+		$scope.selectedAddresses = []
+		$scope.addressesGrid = {
+			saveKey: 'addressesGrid',
+			data: 'addressesData',
+			columnDefs: [
+                 {
+                     field: 'address',
+                    saveKey: 'addressesGrid',
+                     displayName: 'address'
+                 },
+                 {
+                     field: 'class',
+                     displayName: 'class'
+                 },
+                 {
+                     field: 'phase',
+                     displayName: 'phase',
+                     cellClass: 'grid-align-value'
+                 },
+                 {
+                     field: 'inproc',
+                     displayName: 'in-proc'
+                 },
+                 {
+                     field: 'local',
+                     displayName: 'local',
+                     cellClass: 'grid-align-value'
+                 },
+                 {
+                     field: 'remote',
+                     displayName: 'remote',
+                     cellClass: 'grid-align-value'
+                 },
+                 {
+                     field: 'in',
+                     displayName: 'in',
+                     cellClass: 'grid-align-value'
+                 },
+                 {
+                     field: 'out',
+                     displayName: 'out',
+                     cellClass: 'grid-align-value'
+                 }
+            ],
+			enableColumnResize: true,
+			multiSelect: false,
+			selectedItems: $scope.selectedAddresses,
+			afterSelectionChange: function(data) {
+				if (data.selected) {
+					var selItem = data.entity;
+					var nodeId = selItem.uid
+					$("#overtree").dynatree("getTree").activateKey(nodeId);
+				}
+            }
+		};
 
+		// get info for all addresses
+		var allAddressInfo = function () {
 			var gotAllAddressFields = function ( addressFields ) {
-				$scope.addressFields = 	addressFields
-				$scope.$apply()
-				if (currentTimer) {
-					clearTimeout(currentTimer)
+				// update the grid's data
+				addressFields.sort ( function (a,b) { return a.address < b.address ? -1 : a.address > b.address ? 1 : 0})
+				addressFields[0].title = addressFields[0].address
+				for (var i=1; i<addressFields.length; ++i) {
+					if (addressFields[i].address === addressFields[i-1].address) {
+						addressFields[i-1].title = addressFields[i-1].address + " (" + addressFields[i-1]['class'] + ")"
+						addressFields[i].title = addressFields[i].address + " (" + addressFields[i]['class'] + ")"
+					} else
+						addressFields[i].title = addressFields[i].address
 				}
-				currentTimer = setTimeout(allAddressInfo, refreshInterval);
+				$scope.addressesData = addressFields
+
+				// repopulate the tree's child nodes
+				updateAddressTree(addressFields)
 			}
 			getAllAddressFields(gotAllAddressFields)
+	        loadColState($scope.addressesGrid);
 		}
 
 		var getAllAddressFields = function (callback) {
+			var nodeIds = QDRService.nodeIdList()
 			var addr_class = function (addr) {
 				if (!addr) return "-"
 		        if (addr[0] == 'M')  return "mobile"
@@ -331,7 +346,6 @@ var QDR = (function (QDR) {
 			}
 
 			var addressFields = []
-			nodeIds = QDRService.nodeIdList()
 			QDRService.getMultipleNodeInfo(nodeIds, "router.address", [], function (nodeIds, entity, response) {
 				response.aggregates.forEach( function (result) {
 					var prettySum = function (field) {
@@ -365,139 +379,667 @@ var QDR = (function (QDR) {
 			}, nodeIds[0])
 		}
 
+		var updateLinkGrid = function ( linkFields ) {
+			$scope.linkFields = linkFields
+			// if we have a selected link
+			if ($scope.link) {
+				// find the selected link in the array of all links
+				var links = $scope.linkFields.filter(function (link) {
+					return link.name === $scope.link.data.fields.name;
+				})
+				if (links.length > 0) {
+					// linkInfo() is the function that is called by dynatree when a link is selected
+					// It is passed a dynatree node. We need to simulate that node type to update the link grid
+					linkInfo({data: {title: links[0].title, fields: links[0]}})
+				}
+			}
+		}
+
+		// get info for a all links
+		$scope.selectedLinks = []
+		$scope.linksGrid = {
+			saveKey: 'linksGrid',
+			data: 'linkFields',
+			columnDefs: [
+                 {
+                     field: 'link',
+                     displayName: 'Link',
+                     groupable:	false,
+                     saveKey: 'linksGrid',
+                     width: '12%'
+                 },
+                 {
+                     field: 'linkType',
+                     displayName: 'Link type',
+                     groupable:	false,
+                     width: '10%'
+                 },
+                 {
+                     field: 'linkDir',
+                     displayName: 'Link dir',
+                     groupable:	false,
+                     width: '8%'
+                 },
+                 {
+                     field: 'adminStatus',
+                     displayName: 'Admin status',
+                     groupable:	false,
+                     width: '10%'
+                 },
+                 {
+                     field: 'operStatus',
+                     displayName: 'Oper status',
+                     groupable:	false,
+                     width: '10%'
+                 },
+                 {
+                     field: 'deliveryCount',
+                     displayName: 'Delivery Count',
+                     groupable:	false,
+                     cellClass: 'grid-align-value',
+                     width: '12%'
+                 },
+                 {
+                     field: 'rate',
+                     displayName: 'Rate',
+                     groupable:	false,
+                     cellClass: 'grid-align-value',
+                     width: '8%'
+                 },
+                 {
+                     field: 'uncounts',
+                     displayName: 'Outstanding',
+                     groupable:	false,
+                     cellClass: 'grid-align-value',
+                     width: '10%'
+                 },
+                 {
+                     field: 'owningAddr',
+                     displayName: 'Address',
+                     groupable:	false,
+                     width: '20%'
+                 }
+            ],
+			enableColumnResize: true,
+			enableColumnReordering: true,
+			showColumnMenu: true,
+			rowTemplate: 'linkRowTemplate.html',
+			// aggregateTemplate: "linkAggTemplate.html",
+	        multiSelect: false,
+			selectedItems: $scope.selectedLinks,
+			afterSelectionChange: function(data) {
+				if (data.selected) {
+					var selItem = data.entity;
+					var nodeId = selItem.uid
+					$("#overtree").dynatree("getTree").activateKey(nodeId);
+				}
+            }
+		};
 
+
+		$scope.$on('ngGridEventColumns', function (e, columns) {
+			var saveInfo = columns.map( function (col) {
+				return [col.width, col.visible]
+			})
+			var saveKey = columns[0].colDef.saveKey
+			if (saveKey)
+                localStorage.setItem(columnStateKey+saveKey, JSON.stringify(saveInfo));
+        })
+
+        var loadColState = function (grid) {
+            if (!grid)
+                return;
+            var columns = localStorage.getItem(columnStateKey+grid.saveKey);
+            if (columns) {
+                var cols = JSON.parse(columns);
+                cols.forEach( function (col, index) {
+                    grid.columnDefs[index].width = col[0];
+                    grid.columnDefs[index].visible = col[1]
+                })
+            }
+        }
+		var allLinkInfo = function () {
+			getAllLinkFields([updateLinkGrid, updateLinkTree])
+	        loadColState($scope.linksGrid);
+		}
+
+		var getAllLinkFields = function (callbacks) {
+			var nodeIds = QDRService.nodeIdList()
+			var linkFields = []
+			var now = Date.now()
+			var rate = function (response, result) {
+				var name = QDRService.valFor(response.attributeNames, result, "linkName").sum
+				var oldname = $scope.linkFields.filter(function (link) {
+					return link.linkName === name
+				})
+				if (oldname.length > 0) {
+					var elapsed = (now - oldname[0].timestamp) / 1000;
+					var delivered = QDRService.valFor(response.attributeNames, result, "deliveryCount").sum - oldname[0].rawDeliveryCount
+					//QDR.log.debug("elapsed " + elapsed + " delivered " + delivered)
+					return elapsed > 0 ? parseFloat(Math.round((delivered/elapsed) * 100) / 100).toFixed(2) : 0;
+				} else {
+					//QDR.log.debug("unable to find old linkName")
+					return 0
+				}
+			}
+			QDRService.getMultipleNodeInfo(nodeIds, "router.link", [], function (nodeIds, entity, response) {
+				response.aggregates.forEach( function (result) {
+					var prettySum = function (field) {
+						var fieldIndex = response.attributeNames.indexOf(field)
+						if (fieldIndex < 0) {
+							return "-"
+						}
+						var val = result[fieldIndex].sum
+						return QDRService.pretty(val)
+					}
+					var uncounts = function () {
+						var und = QDRService.valFor(response.attributeNames, result, "undeliveredCount").sum
+						var uns = QDRService.valFor(response.attributeNames, result, "unsettledCount").sum
+						return und + uns
+					}
+					var nameIndex = response.attributeNames.indexOf('name')
+					var linkName = function () {
+						var details = result[nameIndex].detail
+						var names = []
+						details.forEach( function (detail) {
+							if (detail.node.endsWith(':'))
+								names.push(detail.node.slice(0, -1))
+							else
+								names.push(detail.node)
+						})
+						//var namestr = names.join('-')
+						var namestr = names.length > 0 ? names[0] : ""
+						return namestr + ':' + QDRService.valFor(response.attributeNames, result, "identity").sum
+					}
+					var fixAddress = function () {
+						var owningAddr = QDRService.valFor(response.attributeNames, result, "owningAddr").sum || ""
+						/*
+						     - "L*"  =>  "* (local)"
+						     - "M0*" =>  "* (direct)"
+						     - "M1*" =>  "* (dequeue)"
+						     - "MX*" =>  "* (phase X)"
+						*/
+						var address = undefined;
+						var starts = {'L': '(local)', 'M0': '(direct)', 'M1': '(dequeue)'}
+						for (var start in starts) {
+							if (owningAddr.startsWith(start)) {
+								var ends = owningAddr.substr(start.length)
+								address = ends + " " + starts[start]
+								break;
+							}
+						}
+						if (!address) {
+							// check for MX*
+							if (owningAddr.length > 3) {
+								if (owningAddr[0] === 'M') {
+									var phase = parseInt(owningAddress.substr(1))
+									if (phase && !isNaN(phase)) {
+										var phaseStr = phase + "";
+										var star = owningAddress.substr(2 + phaseStr.length)
+										address = star + " " + "(phase " + phaseStr + ")"
+									}
+								}
+							}
+						}
+						return address || owningAddr;
+					}
+
+					var adminStatus = QDRService.valFor(response.attributeNames, result, "adminStatus").sum
+					var operStatus = QDRService.valFor(response.attributeNames, result, "operStatus").sum
+					var linkName = linkName()
+					var linkType = QDRService.valFor(response.attributeNames, result, "linkType").sum
+					if ($scope.currentLinkFilter === "" || $scope.currentLinkFilter === linkType) {
+						linkFields.push({
+							link:       linkName,
+							title:      linkName,
+							uncounts:   uncounts(),
+							operStatus: operStatus,
+							adminStatus:adminStatus,
+							owningAddr: fixAddress(),
+							deliveryCount:prettySum("deliveryCount") + " ",
+							rawDeliveryCount: QDRService.valFor(response.attributeNames, result, "deliveryCount").sum,
+							name: QDRService.valFor(response.attributeNames, result, "name").sum,
+							linkName: QDRService.valFor(response.attributeNames, result, "linkName").sum,
+							capacity: QDRService.valFor(response.attributeNames, result, "capacity").sum,
+							connectionId: QDRService.valFor(response.attributeNames, result, "connectionId").sum,
+							linkDir: QDRService.valFor(response.attributeNames, result, "linkDir").sum,
+							linkType: linkType,
+							peer: QDRService.valFor(response.attributeNames, result, "peer").sum,
+							type: QDRService.valFor(response.attributeNames, result, "type").sum,
+							undeliveredCount: QDRService.valFor(response.attributeNames, result, "undeliveredCount").sum,
+							unsettledCount: QDRService.valFor(response.attributeNames, result, "unsettledCount").sum,
+							uid:     linkName,
+							timestamp: now,
+							rate: rate(response, result)
+						})
+					}
+				})
+				callbacks.forEach( function (cb) {
+					cb(linkFields)
+				})
+			}, nodeIds[0])
+		}
+
+		$scope.allConnectionFields = []
+		$scope.allConnectionSelections = [];
+		$scope.allConnectionGrid = {
+			saveKey: 'allConnGrid',
+            data: 'allConnectionFields',
+            columnDefs: [
+                 {
+                     field: 'host',
+                    saveKey: 'allConnGrid',
+                     displayName: 'host'
+                 },
+                 {
+                     field: 'container',
+                     displayName: 'container'
+                 },
+                 {
+                     field: 'role',
+                     displayName: 'role'
+                 },
+                 {
+                     field: 'dir',
+                     displayName: 'dir'
+                 },
+                 {
+                     field: 'security',
+                     displayName: 'security'
+                 },
+                 {
+                     field: 'authentication',
+                     displayName: 'authentication'
+                 }
+            ],
+            enableColumnResize: true,
+            multiSelect: false,
+            selectedItems: $scope.allConnectionSelections,
+            afterSelectionChange: function(data) {
+                if (data.selected) {
+                    var selItem = $scope.allConnectionSelections[0]
+                    var nodeId = selItem.host
+                    // activate Routers->nodeId in the tree
+                    $("#overtree").dynatree("getTree").activateKey(nodeId);
+                }
+            }
+        };
 		// get info for a all connections
 		var allConnectionInfo = function () {
-			$scope.allConnectionFields = []
-			var allConnectionCols = [
-				 {
-					 field: 'host',
-					 displayName: 'host'
-				 },
-				 {
-					 field: 'container',
-					 displayName: 'container'
-				 },
-				 {
-					 field: 'role',
-					 displayName: 'role'
-				 },
-				 {
-					 field: 'dir',
-					 displayName: 'dir'
-				 },
-				 {
-					 field: 'security',
-					 displayName: 'security'
-				 },
-				 {
-					 field: 'authentication',
-					 displayName: 'authentication'
-				 }
-			]
-			$scope.allConnectionGrid = {
-				data: 'allConnectionFields',
-				columnDefs: allConnectionCols,
-				enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER,
-	            enableVerticalScrollbar: uiGridConstants.scrollbars.NEVER,
-				enableColumnResize: true,
-				multiSelect: false,
-				enableRowHeaderSelection: false,
-				noUnselect: true,
-				enableSelectAll: false,
-				enableRowSelection: true,
-				onRegisterApi: function (gridApi) {
-					gridApi.selection.on.rowSelectionChanged($scope, function(row) {
-						if (row.isSelected) {
-							var host = row.entity.host;
-							$("#overtree").fancytree("getTree").activateKey(host);
-						}
-					});
-			    }
-			}
-			connections.children.forEach( function (connection) {
-				$scope.allConnectionFields.push(connection.fields)
+			var nodeIds = QDRService.nodeIdList()
+			updateConnectionTree(nodeIds)
+			$scope.allConnectionFields = [];;
+            var connectionNodes = $("#overtree").dynatree("getTree").getNodeByKey('Connections').getChildren()
+			connectionNodes.forEach( function (connection) {
+				$scope.allConnectionFields.push(connection.data.fields)
 			})
-			$scope.$apply()
-			if (currentTimer) {
-				clearTimeout(currentTimer)
-				currentTimer = null
-			}
+	        loadColState($scope.allConnectionGrid);
+		}
+
+		$scope.addressFields = []
+		$scope.addressGrid = {
+			saveKey: 'addGrid',
+			data: 'addressFields',
+			columnDefs: [
+                 {
+                     field: 'attribute',
+                     displayName: 'Attribute',
+                     saveKey: 'addGrid',
+                     width: '40%'
+                 },
+                 {
+                     field: 'value',
+                     displayName: 'Value',
+                     width: '40%'
+                 }
+            ],
+			enableColumnResize: true,
+			multiSelect: false
 		}
 
 		// get info for a single address
 		var addressInfo = function (address) {
+			if (!address)
+				return;
 			$scope.address = address
-			$scope.addressFields = []
-			var cols = [
-				 {
-					 field: 'attribute',
-					 displayName: 'Attribute',
-					 width: '40%'
-				 },
-				 {
-					 field: 'value',
-					 displayName: 'Value',
-					 width: '40%'
-				 }
-			]
-			$scope.addressGrid = {
-				data: 'addressFields',
-				columnDefs: cols,
-				enableColumnResize: true,
-				enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER,
-	            enableVerticalScrollbar: uiGridConstants.scrollbars.NEVER,
-				multiSelect: false
-			}
+			$scope.addressFields = [];
 
 			var fields = Object.keys(address.data.fields)
 			fields.forEach( function (field) {
 				if (field != "title" && field != "uid")
 					$scope.addressFields.push({attribute: field, value: address.data.fields[field]})
 			})
+	        loadColState($scope.addressGrid);
+		}
 
-			$scope.$apply()
-			if (currentTimer) {
-				clearTimeout(currentTimer)
-				currentTimer = null
-			}
+		$scope.singleLinkFields = []
+		$scope.linkGrid = {
+		    saveKey: 'linkGrid',
+			data: 'singleLinkFields',
+			columnDefs: [
+                 {
+                    field: 'attribute',
+                    displayName: 'Attribute',
+                     width: '40%'
+                 },
+                 {
+                    field: 'value',
+                    displayName: 'Value',
+                    width: '40%'
+                 }
+            ],
+			enableColumnResize: true,
+			multiSelect: false
+		}
+
+		// display the grid detail info for a single link
+		var linkInfo = function (link) {
+			if (!link)
+				return;
+			$scope.link = link
+
+			$scope.singleLinkFields = [];
+			var fields = Object.keys(link.data.fields)
+			var excludeFields = ["title", "uid", "uncounts", "rawDeliveryCount", "timestamp"]
+			fields.forEach( function (field) {
+				if (excludeFields.indexOf(field) == -1)
+					$scope.singleLinkFields.push({attribute: field, value: link.data.fields[field]})
+			})
+	        loadColState($scope.linkGrid);
 		}
 
 		// get info for a single connection
+		$scope.connectionModes = [{
+	        content: '<a><i class="icon-list"></i> Attriutes</a>',
+			id: 'attributes',
+			title: "View connection attributes",
+	        isValid: function () { return true; }
+	    },
+	    {
+	        content: '<a><i class="icon-link"></i> Links</a>',
+	        id: 'links',
+	        title: "Show links",
+	        isValid: function () { return true }
+	    }
+	    ];
+        $scope.currentMode = $scope.connectionModes[0];
+		$scope.isModeSelected = function (mode) {
+			return mode === $scope.currentMode;
+		}
+		$scope.connectionLinks = [];
+		var updateConnectionLinks = function () {
+			var n = $scope.connection.data.fields
+			var key = n.routerId
+			var nodeInfo = QDRService.topology.nodeInfo();
+			var links = nodeInfo[key]['.router.link'];
+			var linkTypeIndex = links.attributeNames.indexOf('linkType');
+			var connectionIdIndex = links.attributeNames.indexOf('connectionId');
+			$scope.connectionLinks = [];
+			links.results.forEach( function (link) {
+				if (link[linkTypeIndex] === 'endpoint' && link[connectionIdIndex] === n.identity) {
+					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.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.deliveryCount = QDRService.pretty(QDRService.valFor(links.attributeNames, link, 'deliveryCount'));
+						l.undeliveredCount = QDRService.pretty(QDRService.valFor(links.attributeNames, link, 'undeliveredCount'));
+						l.unsettledCount = QDRService.pretty(QDRService.valFor(links.attributeNames, link, 'unsettledCount'));
+*/
+					// ---------------------------------------------
+					// TODO: remove this fake quiescing/reviving logic when the routers do the work
+					if ($scope.quiesceState.linkStates[l.identity])
+						l.adminStatus = $scope.quiesceState.linkStates[l.identity];
+					if ($scope.quiesceState.operStates[l.identity])
+						l.operStatus = $scope.quiesceState.operStates[l.identity];
+					if ($scope.quiesceState.state == 'quiescing') {
+						if (l.adminStatus === 'enabled') {
+							var chance = Math.floor(Math.random() * 2);
+							if (chance == 1) {
+								l.adminStatus = 'disabled';
+								$scope.quiesceState.linkStates[l.identity] = 'disabled';
+								$scope.quiesceState.operStates[l.identity] = 'idle';
+							}
+						}
+					}
+					if ($scope.quiesceState.state == 'reviving') {
+						if (l.adminStatus === 'disabled') {
+							var chance = Math.floor(Math.random() * 2);
+							if (chance == 1) {
+								l.adminStatus = 'enabled';
+								$scope.quiesceState.linkStates[l.identity] = 'enabled';
+								$scope.quiesceState.operStates[l.identity] = 'up';
+							}
+						}
+					}
+					if ($scope.quiesceState.linkStates[l.identity] === 'disabled') {
+						l.unsettledCount = 0;
+						l.undeliveredCount = 0;
+						l.operState = 'idle'
+						$scope.quiesceState.operStates[l.identity] = l.operState
+					} else {
+						l.deliveryCount = QDRService.pretty(QDRService.valFor(links.attributeNames, link, 'deliveryCount'));
+						l.undeliveredCount = QDRService.pretty(QDRService.valFor(links.attributeNames, link, 'undeliveredCount'));
+						l.unsettledCount = QDRService.pretty(QDRService.valFor(links.attributeNames, link, 'unsettledCount'));
+						l.operState = 'up'
+						$scope.quiesceState.operStates[l.identity] = l.operState
+					}
+					// ---------------------------------------------
+
+					$scope.connectionLinks.push(l)
+				}
+			})
+			$scope.connectionLinksGrid.updateState()
+		}
+
+		$scope.selectMode = function (mode) {
+			$scope.currentMode = mode;
+			if (mode.id === 'links') {
+		        QDRService.addUpdatedAction("connectionLinks", updateConnectionLinks)
+				updateConnectionLinks();
+			} else {
+				QDRService.delUpdatedAction("connectionLinks");
+			}
+		}
+		$scope.isValid = function (mode) {
+			return mode.isValid()
+		}
+		$scope.quiesceState = {
+			state: 'enabled',
+			buttonText: 'Quiesce',
+			buttonDisabled: false,
+			linkStates: {},
+			operStates: {}
+		}
+		$scope.quiesceAllClicked = function () {
+			var state = $scope.quiesceState.state;
+			if (state === 'enabled') {
+				// start quiescing all links
+				$scope.quiesceState.state = 'quiescing';
+			} else if (state === 'quiesced') {
+				// start reviving all links
+				$scope.quiesceState.state = 'reviving';
+			}
+			$scope.connectionLinksGrid.updateState();
+		}
+		$scope.quiesceClass = function (row) {
+			var stateClassMap = {
+				enabled: 'btn-primary',
+				quiescing: 'btn-warning',
+				reviving: 'btn-warning',
+				quiesced: 'btn-danger'
+			}
+			return stateClassMap[$scope.quiesceState.state];
+		}
+		$scope.quiesceLinkClass = function (row) {
+			var stateClassMap = {
+				enabled: 'btn-primary',
+				disabled: 'btn-danger'
+			}
+			return stateClassMap[row.entity.adminStatus]
+		}
+		$scope.quiesceLink = function (row) {
+			var state = row.entity.adminStatus === 'enabled' ? 'disabled' : 'enabled';
+			var operState = state === 'enabled' ? 'up' : 'idle';
+			$scope.quiesceState.linkStates[row.entity.identity] = state;
+			$scope.quiesceState.operStates[row.entity.identity] = operState;
+		}
+		$scope.quiesceLinkDisabled = function (row) {
+			return false;
+		}
+		$scope.quiesceLinkText = function (row) {
+			return row.entity.adminStatus === 'disabled' ? "Revive" : "Quiesce";
+		}
+		$scope.quiesceHide = function () {
+			return $scope.connectionLinks.length == 0;
+		}
+		$scope.connectionLinksGrid = {
+		    saveKey: 'connLinksGrid',
+
+			data: 'connectionLinks',
+			updateState: function () {
+				var state = $scope.quiesceState.state;
+
+				// count enabled and disabled links for this connection
+				var enabled = 0, disabled = 0;
+				$scope.connectionLinks.forEach ( function (link) {
+					if (link.adminStatus === 'enabled')
+						++enabled;
+					if (link.adminStatus === 'disabled')
+						++disabled;
+				})
+
+				var linkCount = $scope.connectionLinks.length;
+				if (linkCount == 0) {
+					$scope.quiesceState.buttonText = 'Quiesce';
+					$scope.quiesceState.buttonDisabled = false;
+					$scope.quiesceState.state = 'enabled'
+					return;
+				}
+				// if state is quiescing and any links are enabled, button should say 'Quiescing' and be disabled
+				if (state === 'quiescing' && (enabled > 0)) {
+					$scope.quiesceState.buttonText = 'Quiescing';
+					$scope.quiesceState.buttonDisabled = true;
+				} else
+				// if state is enabled and all links are disabled, button should say Revive and be enabled. set state to quisced
+				// if state is quiescing and all links are disabled, button should say 'Revive' and be enabled. set state to quiesced
+				if ((state === 'quiescing' || state === 'enabled') && (disabled === linkCount)) {
+					$scope.quiesceState.buttonText = 'Revive';
+					$scope.quiesceState.buttonDisabled = false;
+					$scope.quiesceState.state = 'quiesced'
+				} else
+				// if state is reviving and any links are disabled, button should say 'Reviving' and be disabled
+				if (state === 'reviving' && (disabled > 0)) {
+					$scope.quiesceState.buttonText = 'Reviving';
+					$scope.quiesceState.buttonDisabled = true;
+				} else
+				// if state is reviving or quiesced and all links are enabled, button should say 'Quiesce' and be enabled. set state to enabled
+				if ((state === 'reviving' || state === 'quiesced') && (enabled === linkCount)) {
+					$scope.quiesceState.buttonText = 'Quiesce';
+					$scope.quiesceState.buttonDisabled = false;
+					$scope.quiesceState.state = 'enabled'
+				}
+
+				if ($scope.quiesceState.state === 'quiesced') {
+					d3.selectAll('.external.connection.dynatree-active')
+						.classed('quiesced', true)
+				} else {
+					d3.selectAll('.external.connection.dynatree-active.quiesced')
+						.classed('quiesced', false)
+				}
+			},
+            columnDefs: [
+			{
+				field: 'adminStatus',
+                cellTemplate: "titleCellTemplate.html",
+				headerCellTemplate: 'titleHeaderCellTemplate.html',
+			    saveKey: 'connLinksGrid',
+				displayName: 'Admin state'
+			},
+			{
+				field: 'operStatus',
+                cellTemplate: "titleCellTemplate.html",
+				headerCellTemplate: 'titleHeaderCellTemplate.html',
+				displayName: 'Oper state'
+			},
+			{
+				field: 'dir',
+                cellTemplate: "titleCellTemplate.html",
+				headerCellTemplate: 'titleHeaderCellTemplate.html',
+				displayName: 'dir'
+			},
+			{
+				field: 'owningAddr',
+                cellTemplate: "titleCellTemplate.html",
+				headerCellTemplate: 'titleHeaderCellTemplate.html',
+				displayName: 'Address'
+			},
+			{
+				field: 'deliveryCount',
+				displayName: 'Delivered',
+				headerCellTemplate: 'titleHeaderCellTemplate.html',
+				cellClass: 'grid-values'
+
+			},
+			{
+				field: 'undeliveredCount',
+				displayName: 'Undelivered',
+				headerCellTemplate: 'titleHeaderCellTemplate.html',
+				cellClass: 'grid-values'
+			},
+			{
+				field: 'unsettledCount',
+				displayName: 'Unsettled',
+				headerCellTemplate: 'titleHeaderCellTemplate.html',
+				cellClass: 'grid-values'
+			}/*,
+			{
+				cellClass: 'gridCellButton',
+				cellTemplate: '<button title="{{quiesceLinkText(row)}} this link" type="button" ng-class="quiesceLinkClass(row)" class="btn" ng-click="quiesceLink(row)" ng-disabled="quiesceLinkDisabled(row)">{{quiesceLinkText(row)}}</button>'
+			}*/
+			]
+		}
+
+		$scope.connectionFields = []
+		$scope.connectionGrid = {
+		    saveKey: 'connGrid',
+			data: 'connectionFields',
+			columnDefs: [
+                 {
+                     field: 'attribute',
+                     displayName: 'Attribute',
+                    saveKey: 'connGrid',
+                     width: '40%'
+                 },
+                 {
+                     field: 'value',
+                     displayName: 'Value',
+                     width: '40%'
+                 }
+            ],
+			enableColumnResize: true,
+			multiSelect: false
+		}
+
 		var connectionInfo = function (connection) {
 			$scope.connection = connection
-			$scope.connectionFields = []
-			var cols = [
-				 {
-					 field: 'attribute',
-					 displayName: 'Attribute',
-					 width: '40%'
-				 },
-				 {
-					 field: 'value',
-					 displayName: 'Value',
-					 width: '40%'
-				 }
-			]
-			$scope.connectionGrid = {
-				data: 'connectionFields',
-				columnDefs: cols,
-				enableColumnResize: true,
-				enableHorizontalScrollbar: uiGridConstants.scrollbars.NEVER,
-	            enableVerticalScrollbar: uiGridConstants.scrollbars.NEVER,
-				multiSelect: false
-			}
 
+			$scope.connectionFields = [];
 			var fields = Object.keys(connection.data.fields)
 			fields.forEach( function (field) {
 				$scope.connectionFields.push({attribute: field, value: connection.data.fields[field]})
 			})
-
-			$scope.$apply()
-			if (currentTimer) {
-				clearTimeout(currentTimer)
-				currentTimer = null
-			}
+			$scope.selectMode($scope.currentMode)
+	        loadColState($scope.connectionGrid);
 		}
 
 		// get info for a all logs
@@ -509,142 +1051,309 @@ var QDR = (function (QDR) {
 			$scope.log = node
 		}
 
-		var activated = function (event, node) {
+		var getExpandedList = function () {
+			if (!treeRoot)
+				return;
+			var list = [];
+			if (treeRoot.visit) {
+				treeRoot.visit(function(node){
+	                if (node.isExpanded()) {
+	                    list.push(node.data.parent)
+	                }
+	            });
+			}
+			return list;
+		}
+
+		var updateExpanded = function () {
+			if (!treeRoot)
+				return;
+			if (treeRoot.visit) {
+				treeRoot.visit(function(node){
+	                if (node.isExpanded() || node.isActive()) {
+	                    node.data.info(node)
+	                }
+	            });
+			}
+		}
+
+		// loads the tree node name that was last selected
+		var loadActivatedNode = function () {
+			return localStorage[OverviewActivatedKey] || 'Routers'
+		}
+		// saved the tree node name that is currently selected
+		var saveActivated = function (key) {
+			localStorage[OverviewActivatedKey] = key;
+			lastKey = key;
+		}
+		// loads list that was saved
+		var loadExpandedNodeList = function () {
+			return angular.fromJson(localStorage[OverviewExpandedKey]) || [];
+		}
+		// called when a node is expanded
+		// here we save the expanded node so it can be restored when the page reloads
+		var saveExpanded = function () {
+			var list = getExpandedList();
+			localStorage[OverviewExpandedKey] = JSON.stringify(list)
+		}
+
+		// activated is called each time a tree node is clicked
+		// based on which node is clicked, load the correct data grid template and start getting the data
+		var activated = function (node) {
 			//QDR.log.debug("node activated: " + node.data.title)
-			node = node.node;
 			var type = node.data.type;
+			saveExpanded()
+			saveActivated(node.data.key)
+
 			var template = $scope.templates.filter( function (tpl) {
 				return tpl.name == type;
 			})
 			$scope.template = template[0];
-			// Call the function associated with this type passing in the node that was selected
-			// In dynatree I could save the function to call in the node, but not in FancyTree
-			if (treeTypeMap[type])
-				treeTypeMap[type](node);
-
-			/*if (node.data.info)
-				node.data.info(node)
-			*/
+			// the nodes info function will fetch the grids data
+			if (node.data.info) {
+				$timeout(function () {node.data.info(node)})
+			}
+
+		}
+        $scope.template = {url: ''};
+
+		if (!QDRService.connected) {
+			QDRService.redirectWhenConnected("overview")
+			return;
+		}
+
+		// we are currently connected. setup a handler to get notified if we are ever disconnected
+		QDRService.addDisconnectAction( function () {
+			QDRService.redirectWhenConnected("overview")
 			$scope.$apply();
+		})
+
+	/* --------------------------------------------------
+	 *
+     * setup the tree on the left
+     *
+     * -------------------------------------------------
+     */
+		// utility function called by each top level tree node when it needs to populate its child nodes
+		var updateLeaves = function (leaves, parentKey, parentFolder, worker) {
+			var scrollTree = $('.qdr-overview.pane.left .pane-viewport')
+			var scrollTop = scrollTree.scrollTop();
+			var tree;
+			try {
+				tree = $("#overtree").dynatree("getTree")
+			} catch (e) {
+				// tree is not initialized yet
+				tree = {}
+			}
+			var parentNode = tree.count ? tree.getNodeByKey(parentKey) : null;
+			// if we were called after the tree is created, replace the existing nodes
+			var activeNode;
+			if (parentNode) {
+				activeNode = tree.getActiveNode();
+				parentNode.removeChildren();
+			}
+			leaves.forEach( function (leaf) {
+				var childFolder = worker(leaf)
+				if (parentNode)
+					parentNode.addChild(childFolder)
+				else
+					parentFolder.children.push(childFolder)
+			})
+			scrollTree.scrollTop(scrollTop)
+			if (activeNode) {
+				var newNode = tree.getNodeByKey(activeNode.data.key)
+				newNode.activateSilently()
+			}
 		}
-        $scope.template = $scope.templates[0];
 
+		// get saved tree state
+        var lastKey = loadActivatedNode();
+		var expandedNodeList = loadExpandedNodeList();
+
+		// create a routers tree branch
 		var routers = new Folder("Routers")
 		routers.type = "Routers"
-		//routers.info = allRouterInfo
-		routers.focus = true
-		routers.expanded = true
+		routers.info = allRouterInfo
+		routers.activate = lastKey === 'Routers'
+		routers.expand = (expandedNodeList.indexOf("Routers") > -1)
+		routers.clickFolderMode = 1
 		routers.key = "Routers"
-		$scope.overview.children.push(routers)
-		nodeIds.forEach( function (node) {
-			var name = QDRService.nameFromId(node)
-			var router = new Leaf(name)
-			router.type = "Router"
-			//router.info = routerInfo
-			router.nodeId = node
-			router.key = node
-			routers.children.push(router)
-		})
+		routers.parent = "Routers"
+	    routers.addClass = "routers"
+		topLevelChildren.push(routers)
+		// called when the list of routers changes
+		var updateRouterTree = function (nodes) {
+			var worker = function (node) {
+				var name = QDRService.nameFromId(node)
+				var router = new Folder(name)
+				router.type = "Router"
+				router.info = routerInfo
+				router.nodeId = node
+				router.key = node
+				router.addClass = "router"
+				router.parent = "Routers"
+				router.activate = lastKey === node
+				return router;
+			}
+			updateLeaves(nodes, "Routers", routers, worker)
+		}
+		updateRouterTree(QDRService.nodeIdList());
 
-		var expected = nodeIds.length;
+		// create an addresses tree branch
 		var addresses = new Folder("Addresses")
 		addresses.type = "Addresses"
-		//addresses.info = allAddressInfo
+		addresses.info = allAddressInfo
+		addresses.activate = lastKey === 'Addresses'
+		addresses.expand = (expandedNodeList.indexOf("Addresses") > -1)
+		addresses.clickFolderMode = 1
 		addresses.key = "Addresses"
-		$scope.overview.children.push(addresses)
-
-		var gotAddressFields = function (addressFields) {
-			addressFields.sort ( function (a,b) { return a.address < b.address ? -1 : a.address > b.address ? 1 : 0})
-			addressFields[0].title = addressFields[0].address
-			for (var i=1; i<addressFields.length; ++i) {
-				if (addressFields[i].address === addressFields[i-1].address) {
-					addressFields[i-1].title = addressFields[i-1].address + " (" + addressFields[i-1]['class'] + ")"
-					addressFields[i].title = addressFields[i].address + " (" + addressFields[i]['class'] + ")"
-				} else
-					addressFields[i].title = addressFields[i].address
-			}
-			addressFields.forEach( function (address) {
-				var a = new Leaf(address.title)
-				//a.info = addressInfo
+		addresses.parent = "Addresses"
+	    addresses.addClass = "addresses"
+		topLevelChildren.push(addresses)
+		var updateAddressTree = function (addressFields) {
+			var worker = function (address) {
+				var a = new Folder(address.title)
+				a.info = addressInfo
 				a.key = address.uid
 				a.fields = address
 				a.type = "Address"
-				addresses.children.push(a)
-			} )
+				a.addClass = "address"
+				a.activate = lastKey === address.uid
+				a.parent = "Addresses"
+				return a;
+			}
+			updateLeaves(addressFields, "Addresses", addresses, worker)
 		}
-		getAllAddressFields(gotAddressFields)
+		allAddressInfo();
 
+		$scope.setLinkFilter = function (cur) {
+			// filter out non-matching links from the tree and the grid
+			getAllLinkFields([updateLinkGrid, updateLinkTree])
+		}
+		$scope.filterClose = function () {
+			var filter = $('#linkFilter')
+			filter.hide();
+		}
+		$scope.currentLinkFilter = "endpoint"
+		var filterHtml = "<button type='button' class='btn btn-secondary btn-filter'><span class='filter-icon'><i class='icon-filter'> Filter</span></button>";
+		var links = new Folder("Links " + filterHtml)
+		links.type = "Links"
+		links.info = allLinkInfo
+		links.activate = lastKey === 'Links'
+		links.expand = (expandedNodeList.indexOf("Links") > -1)
+		links.clickFolderMode = 1
+		links.key = "Links"
+		links.parent = "Links"
+	    links.addClass = "links"
+		topLevelChildren.push(links)
+
+		// called both before the tree is created and whenever a background update is done
+		var updateLinkTree = function (linkFields) {
+			var worker = function (link) {
+				var l = new Folder(link.title)
+				l.info = linkInfo
+				l.key = link.uid
+				l.fields = link
+				l.type = "Link"
+				l.parent = "Links"
+				l.activate = lastKey === link.uid
+				l.addClass = "link"
+				return l;
+			}
+			linkFields.sort ( function (a,b) { return a.link < b.link ? -1 : a.link > b.link ? 1 : 0})
+			updateLeaves(linkFields, "Links", links, worker)
+		}
+		allLinkInfo();
 
-		var connreceived = 0;
-		var connectionsObj = {}
 		var connections = new Folder("Connections")
 		connections.type = "Connections"
-		//connections.info = allConnectionInfo
+		connections.info = allConnectionInfo
+		connections.activate = lastKey === 'Connections'
+		connections.expand = (expandedNodeList.indexOf("Connections") > -1)
+		connections.clickFolderMode = 1
 		connections.key = "Connections"
-		$scope.overview.children.push(connections)
-		nodeIds.forEach( function (nodeId) {
+		connections.parent = "Connections"
+	    connections.addClass = "connections"
+		topLevelChildren.push(connections)
 
-			QDRService.getNodeInfo(nodeId, ".connection", [], function (nodeName, entity, response) {
-				response.results.forEach( function (result) {
+		updateConnectionTree = function (nodes) {
+			var connectionsObj = {}
+			var expected = nodes.length;
+			var connreceived = 0;
+			nodes.forEach( function (nodeId) {
+				QDRService.getNodeInfo(nodeId, ".connection", [], function (nodeName, entity, response) {
+					response.results.forEach( function (result) {
 
-					var auth = "no_auth"
-					var sasl = QDRService.valFor(response.attributeNames, result, "sasl")
-					if (QDRService.valFor(response.attributeNames, result, "isAuthenticated")) {
-						auth = sasl
-						if (sasl === "ANONYMOUS")
-							auth = "anonymous-user"
-						else {
+						var auth = "no_auth"
+						var sasl = QDRService.valFor(response.attributeNames, result, "sasl")
+						if (QDRService.valFor(response.attributeNames, result, "isAuthenticated")) {
+							auth = sasl
+							if (sasl === "ANONYMOUS")
+								auth = "anonymous-user"
+							else {
+								if (sasl === "GSSAPI")
+									sasl = "Kerberos"
+								if (sasl === "EXTERNAL")
+									sasl = "x.509"
+								auth = QDRService.valFor(response.attributeNames, result, "user") + "(" +
+										QDRService.valFor(response.attributeNames, result, "sslCipher") + ")"
+								}
+						}
+
+						var sec = "no-security"
+						if (QDRService.valFor(response.attributeNames, result, "isEncrypted")) {
 							if (sasl === "GSSAPI")
-								sasl = "Kerberos"
-							if (sasl === "EXTERNAL")
-								sasl = "x.509"
-							auth = QDRService.valFor(response.attributeNames, result, "user") + "(" +
-									QDRService.valFor(response.attributeNames, result, "sslCipher") + ")"
-							}
-					}
+								sec = "Kerberos"
+							else
+								sec = QDRService.valFor(response.attributeNames, result, "sslProto") + "(" +
+										QDRService.valFor(response.attributeNames, result, "sslCipher") + ")"
+						}
 
-					var sec = "no-security"
-					if (QDRService.valFor(response.attributeNames, result, "isEncrypted")) {
-						if (sasl === "GSSAPI")
-							sec = "Kerberos"
-						else
-							sec = QDRService.valFor(response.attributeNames, result, "sslProto") + "(" +
-									QDRService.valFor(response.attributeNames, result, "sslCipher") + ")"
-					}
+						var host = QDRService.valFor(response.attributeNames, result, "host")
+						connectionsObj[host] = {}
+						response.attributeNames.forEach( function (attribute, i) {
+							connectionsObj[host][attribute] = result[i]
+						})
+						connectionsObj[host].security = sec
+						connectionsObj[host].authentication = auth
+						connectionsObj[host].routerId = nodeName
 
-					var host = QDRService.valFor(response.attributeNames, result, "host")
-					connectionsObj[host] = {}
-					response.attributeNames.forEach( function (attribute, i) {
-						connectionsObj[host][attribute] = result[i]
 					})
-					connectionsObj[host].security = sec
-					connectionsObj[host].authentication = auth
+					++connreceived;
+					if (connreceived == expected) {
+						var worker = function (connection) {
+							var c = new Folder(connection)
+							c.type = "Connection"
+							c.info = connectionInfo
+							c.key = connection
+							c.fields = connectionsObj[connection]
+							c.tooltip = connectionsObj[connection].role === "inter-router" ? "inter-router connection" : "external connection"
+							c.addClass = c.tooltip
+							c.parent = "Connections"
+							c.activate = lastKey === connection
+							return c
+						}
+						var allConnections = Object.keys(connectionsObj).sort()
+						updateLeaves(allConnections, "Connections", connections, worker)
+					}
 				})
-				++connreceived;
-				if (connreceived == expected) {
-					var allConnections = Object.keys(connectionsObj).sort()
-					allConnections.forEach(function (connection) {
-						var c = new Leaf(connection)
-						c.type = "Connection"
-						c.icon = "ui-icon "
-						c.icon += connectionsObj[connection].role === "inter-router" ? "ui-icon-refresh" : "ui-icon-transfer-e-w"
-						//c.info = connectionInfo
-						c.key = connection
-						c.fields = connectionsObj[connection]
-						c.tooltip = connectionsObj[connection].role === "inter-router" ? "inter-router connection" : "external connection"
-						connections.children.push(c)
-					})
-				}
 			})
-		})
+		}
+		updateConnectionTree(QDRService.nodeIdList())
 
 		var logsreceived = 0;
 		var logObj = {}
 		var logs = new Folder("Logs")
 		logs.type = "Logs"
-		//logs.info = allLogInfo
+		logs.info = allLogInfo
+		logs.activate = lastKey === 'Logs'
+		logs.expand = (expandedNodeList.indexOf("Logs") > -1)
+		logs.clickFolderMode = 1
 		logs.key = "Logs"
-		//$scope.overview.children.push(logs)
+		logs.parent = "Logs"
+		//topLevelChildren.push(logs)
+		var nodeIds = QDRService.nodeIdList()
+		var expected = nodeIds.length;
 		nodeIds.forEach( function (nodeId) {
 			QDRService.getNodeInfo(nodeId, ".log", ["name"], function (nodeName, entity, response) {
 				response.results.forEach( function (result) {
@@ -654,39 +1363,73 @@ var QDR = (function (QDR) {
 				if (logsreceived == expected) {
 					var allLogs = Object.keys(logObj).sort()
 					allLogs.forEach(function (log) {
-						var l = new Leaf(log)
+						var l = new Folder(log)
 						l.type = "Log"
-						//l.info = logInfo
+						l.info = logInfo
 						l.key = log
+						l.parent = "Logs"
+						l.activate = lastKey === log
 						logs.children.push(l)
 					})
-					//console.log("---------------")
-					//console.dump($scope.overview.children)
-					//console.log("---------------")
-					$("#overtree").fancytree({
-						activate: activated,
-						clickFolderMode: 1,
-						source: $scope.overview.children
-						})
-					allRouterInfo();
+					$('#overtree').dynatree({
+						onActivate: activated,
+						onExpand: saveExpanded,
+						onClick: function (n, e) {
+							if (e.target.className.indexOf('-filter') > -1) {
+								//QDR.log.debug("overtree on click called")
+								e.preventDefault();
+								e.stopPropagation()
+								var filter = $('#linkFilter')
+								var treeLink = $('span.links')
+								filter.css({
+                                              top: treeLink.offset().top + treeLink.height(),
+                                              left: treeLink.offset().left,
+                                              zIndex:5000
+                                            });
+								filter.toggle()
+								$("#overtree").dynatree("getTree").getNodeByKey('Links').activate()
+								return false;
+							}
+						},
+						selectMode: 1,
+						debugLevel: 0,
+						activeVisible: false,
+						children: topLevelChildren
+					})
+					treeRoot = $("#overtree").dynatree("getRoot");
+
+					// simulate a click on the previous active node
+					var active = $("#overtree").dynatree("getActiveNode");
+					if (!active) {
+						active = $("#overtree").dynatree("getTree").getNodeByKey("Routers")
+					}
+					activated(active);
+
+					// populate the data for each expanded node
+					updateExpanded();
+					QDRService.addUpdatedAction( "overview", function () {
+						$timeout(updateExpanded);
+					})
+					// update the node list
+					QDRService.startUpdating()
+
+			        loadColState($scope.allRouters);
+			        loadColState($scope.routerGrid);
+			        loadColState($scope.addressesGrid);
+			        loadColState($scope.addressGrid);
+			        loadColState($scope.linksGrid);
+			        loadColState($scope.linkGrid);
+			        loadColState($scope.allConnectionGrid);
+			        loadColState($scope.connectionGrid);
+
 				}
 			})
 		})
 
         $scope.$on("$destroy", function( event ) {
-			if (currentTimer) {
-				clearTimeout(currentTimer)
-				currentTimer = null;
-			}
+			QDRService.stopUpdating()
+			QDRService.delUpdatedAction("overview")
         });
-		var treeTypeMap = {
-			Routers:     allRouterInfo,
-			Router:      routerInfo,
-			Addresses:   allAddressInfo,
-			Address:     addressInfo,
-			Connections: allConnectionInfo,
-			Connection:  connectionInfo
-		}
 
     }]);
 

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/d98ae37e/console/stand-alone/plugin/js/qdrPlugin.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrPlugin.js b/console/stand-alone/plugin/js/qdrPlugin.js
deleted file mode 100644
index c287342..0000000
--- a/console/stand-alone/plugin/js/qdrPlugin.js
+++ /dev/null
@@ -1,197 +0,0 @@
-/*
-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
- * @main QDR
- *
- * The main entrypoint for the QDR module
- *
- */
-var QDR = (function(QDR) {
-
-  /**
-   * @property pluginName
-   * @type {string}
-   *
-   * The name of this plugin
-   */
-  QDR.pluginName = "QDR";
-
-  /**
-   * @property log
-   * @type {Logging.Logger}
-   *
-   * This plugin's logger instance
-   */
-  //HIO QDR.log = Logger.get(QDR.pluginName);
-  /**
-   * @property templatePath
-   * @type {string}
-   *
-   * The top level path to this plugin's partials
-   */
-  QDR.srcBase = "plugin/";
-  QDR.templatePath = QDR.srcBase + "html/";
-  QDR.cssPath = QDR.srcBase + "css/";
-
-  /**
-   * @property SETTINGS_KEY
-   * @type {string}
-   *
-   * The key used to fetch our settings from local storage
-   */
-  QDR.SETTINGS_KEY = 'QDRSettings';
-  QDR.LAST_LOCATION = "QDRLastLocation";
-
-  /**
-   * @property module
-   * @type {object}
-   *
-   * This plugin's angularjs module instance
-   */
-  QDR.module = angular.module(QDR.pluginName, ['ngAnimate', 'ngResource', 'ngRoute', 'ui.grid', 'ui.grid.selection',
-    'ui.grid.autoResize', 'jsonFormatter', 'ui.bootstrap', 'ui.slider'/*, 'minicolors' */]);
-
-  // set up the routing for this plugin
-  QDR.module.config(function($routeProvider) {
-    $routeProvider
-      .when('/', {
-        templateUrl: QDR.templatePath + 'qdrConnect.html'
-        })
-      .when('/overview', {
-          templateUrl: QDR.templatePath + 'qdrOverview.html'
-        })
-      .when('/topology', {
-          templateUrl: QDR.templatePath + 'qdrTopology.html'
-        })
-      .when('/list', {
-          templateUrl: QDR.templatePath + 'qdrList.html'
-        })
-      .when('/schema', {
-          templateUrl: QDR.templatePath + 'qdrSchema.html'
-        })
-      .when('/charts', {
-          templateUrl: QDR.templatePath + 'qdrCharts.html'
-        })
-      .when('/connect', {
-          templateUrl: QDR.templatePath + 'qdrConnect.html'
-        })
-  });
-
-  QDR.module.config(['$compileProvider', function ($compileProvider) {
-	var cur = $compileProvider.aHrefSanitizationWhitelist();
-    $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|blob):/);
-	cur = $compileProvider.aHrefSanitizationWhitelist();
-  }]);
-
-	QDR.module.config(function (JSONFormatterConfigProvider) {
-		// Enable the hover preview feature
-        JSONFormatterConfigProvider.hoverPreviewEnabled = true;
-	});
-
-	QDR.module.filter('to_trusted', ['$sce', function($sce){
-          return function(text) {
-              return $sce.trustAsHtml(text);
-          };
-    }]);
-
-	QDR.module.filter('humanify', function (QDRService) {
-		return function (input) {
-			return QDRService.humanify(input);
-		};
-	});
-
-	QDR.logger = function ($log) {
-		var log = $log;
-
-		this.debug = function (msg) { msg = "QDR: " + msg; log.debug(msg)};
-		this.error = function (msg) {msg = "QDR: " + msg; log.error(msg)}
-		this.info = function (msg) {msg = "QDR: " + msg; log.info(msg)}
-		this.warn = function (msg) {msg = "QDR: " + msg; log.warn(msg)}
-
-		return this;
-	}
-    // one-time initialization happens in the run function
-    // of our module
-	QDR.module.run( ["$rootScope", "$location", "$log", "QDRService", "QDRChartService",  function ($rootScope, $location, $log, QDRService, QDRChartService) {
-		QDR.log = new QDR.logger($log);
-		QDR.log.debug("QDR.module.run()")
-
-		QDRService.initProton();
-		var settings = angular.fromJson(localStorage[QDR.SETTINGS_KEY]);
-		var lastLocation = localStorage[QDR.LAST_LOCATION];
-		if (!angular.isDefined(lastLocation))
-			lastLocation = "/overview";
-
-		QDRService.addConnectAction(function() {
-			QDRChartService.init(); // initialize charting service after we are connected
-		});
-		if (settings && settings.autostart) {
-			QDRService.addConnectAction(function() {
-				$location.path(lastLocation);
-				$location.replace();
-				$rootScope.$apply();
-			});
-			QDRService.connect(settings);
-        } else {
-			setTimeout(function () {
-	            $location.url('/connect')
-				$location.replace();
-			}, 100)
-        }
-
-        $rootScope.$on('$routeChangeSuccess', function() {
-            localStorage[QDR.LAST_LOCATION] = $location.$$path;
-        });
-
-
-	}]);
-
-	QDR.module.controller ("QDR.MainController", ['$scope', '$location', function ($scope, $location) {
-		QDR.log.debug("started QDR.MainController with location.url: " + $location.url());
-		QDR.log.debug("started QDR.MainController with window.location.pathname : " + window.location.pathname);
-		$scope.topLevelTabs = [];
-		$scope.topLevelTabs.push({
-			id: "qdr",
-			content: "Qpid Dispatch Router Console",
-			title: "Dispatch Router Console",
-			isValid: function() { return true; },
-			href: function() { return "#connect"; },
-			isActive: function() { return true; }
-		});
-	}])
-
-	QDR.module.controller ("QDR.Core", function ($scope, $rootScope) {
-		$scope.alerts = [];
-		$scope.closeAlert = function(index) {
-            $scope.alerts.splice(index, 1);
-        };
-		$scope.$on('newAlert', function(event, data) {
-			$scope.alerts.push(data);
-			$scope.$apply();
-		});
-		$scope.$on("clearAlerts", function () {
-			$scope.alerts = [];
-			$scope.$apply();
-		})
-
-	})
-
-  return QDR;
-}(QDR || {}));

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/d98ae37e/console/stand-alone/plugin/js/qdrSchema.js
----------------------------------------------------------------------
diff --git a/console/stand-alone/plugin/js/qdrSchema.js b/console/stand-alone/plugin/js/qdrSchema.js
index 0e94351..656b159 100644
--- a/console/stand-alone/plugin/js/qdrSchema.js
+++ b/console/stand-alone/plugin/js/qdrSchema.js
@@ -21,9 +21,50 @@ under the License.
  */
 var QDR = (function (QDR) {
 
-    QDR.module.controller("QDR.SchemaController", ['$scope', 'QDRService', function($scope, QDRService) {
+    QDR.module.controller("QDR.SchemaController", ['$scope', '$location', 'QDRService', function($scope, $location, QDRService) {
+		if (!QDRService.connected) {
+			QDRService.redirectWhenConnected("schema")
+			return;
+		}
+		// we are currently connected. setup a handler to get notified if we are ever disconnected
+		QDRService.addDisconnectAction( function () {
+			QDRService.redirectWhenConnected("schema")
+			$scope.$apply();
+		})
+
+        var keys2kids = function (tree, obj) {
+
+			if (obj === Object(obj)) {
+				tree.children = []
+	            for (var key in obj) {
+	                var kid = {title: key}
+	                if (obj[key] === Object(obj[key])) {
+	                    kid.isFolder = true
+	                    keys2kids(kid, obj[key])
+	                } else {
+						kid.title += (': ' + JSON.stringify(obj[key],null,2))
+	                }
+	                tree.children.push(kid)
+	            }
+			}
+        }
+
+		var tree = []
+		for (var key in QDRService.schema) {
+			var kid = {title: key}
+			var val = QDRService.schema[key]
+			if (val === Object(val))
+				keys2kids(kid, val)
+			else
+				kid.title += (': ' + JSON.stringify(val,null,2))
+
+			tree.push(kid);
+		}
+        $('#schema').dynatree({
+			minExpandLevel: 2,
+            children: tree
+        })
 
-        $scope.schema = QDRService.schema;
 
     }]);
 

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/d98ae37e/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 758e518..55c6581 100644
--- a/console/stand-alone/plugin/js/qdrService.js
+++ b/console/stand-alone/plugin/js/qdrService.js
@@ -23,7 +23,7 @@ var QDR = (function(QDR) {
 
   // The QDR service handles the connection to
   // the server in the background
-  QDR.module.factory("QDRService", ['$rootScope', '$http', '$resource', '$location', function($rootScope, $http, $resource, $location) {
+  QDR.module.factory("QDRService", ['$rootScope', '$http', '$timeout', '$resource', '$location', function($rootScope, $http, $timeout, $resource, $location) {
     var self = {
 
 	  rhea: require("rhea"),
@@ -57,21 +57,50 @@ var QDR = (function(QDR) {
       executeConnectActions: function() {
         self.connectActions.forEach(function(action) {
           //QDR.log.debug("executing connect action " + action);
-          action.apply();
+			try {
+                action.apply();
+            } catch (e) {
+                // in case the page that registered the handler has been unloaded
+            }
         });
         self.connectActions = [];
+		if ($location.path().startsWith(QDR.pluginRoot)) {
+	        $timeout( function () {
+
+	            var searchObject = $location.search();
+	            var goto = "overview";
+	            if (searchObject.org && searchObject.org !== "connect") {
+	                goto = searchObject.org;
+	            }
+	            $location.search('org', null)
+	            $location.path(QDR.pluginRoot +"/" + goto);
+	        })
+		}
+
       },
       executeDisconnectActions: function() {
         self.disconnectActions.forEach(function(action) {
-          action.apply();
+			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) {
-            self.updatedActions[action].apply();
+			try {
+                self.updatedActions[action].apply();
+            } catch (e) {
+                delete self.updatedActions[action]
+            }
         }
       },
+	redirectWhenConnected: function (org) {
+		$location.path("/" + QDR.pluginName + "/connect")
+		$location.search('org', org);
+	},
 
       notifyTopologyDone: function() {
         //QDR.log.debug("got Toplogy done notice");
@@ -150,12 +179,11 @@ var QDR = (function(QDR) {
     
     onSubscription: function() {
         self.getSchema();
-        self.topology.get();
      },
 
     startUpdating: function () {
-        QDR.log.info("startUpdating called")
         self.stopUpdating();
+        QDR.log.info("startUpdating called")
         self.topology.get();
         self.stop = setInterval(function() {
             self.topology.get();
@@ -191,6 +219,8 @@ var QDR = (function(QDR) {
       },
 
       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(".", " ");
       },
@@ -255,6 +285,44 @@ var QDR = (function(QDR) {
 			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) {
+			// TODO: use connection properties when available
+			if (d.properties.console_identifier == 'Dispatch console')
+				return true;
+			// until connection properties can difinitively identify consoles:
+			var connid = d.connectionId;
+			if (connid && d.nodeType === 'normal') {
+				// find all the endpoint links for this router that have this connid
+				var linkInfo = self.topology.nodeInfo()[d.key]['.router.link']
+				var outs = 0, ins = 0;
+				var outaddr, inaddr;
+				linkInfo.results.forEach( function (link) {
+					if (self.valFor(linkInfo.attributeNames, link, 'connectionId') == connid &&
+						self.valFor(linkInfo.attributeNames, link, 'linkType') == 'endpoint') {
+						if (self.valFor(linkInfo.attributeNames, link, 'linkDir') == 'in') {
+							++ins;
+							inaddr = self.valFor(linkInfo.attributeNames, link, 'owningAddr')
+						}
+						if (self.valFor(linkInfo.attributeNames, link, 'linkDir') == 'out') {
+							++outs;
+							outaddr = self.valFor(linkInfo.attributeNames, link, 'owningAddr')
+						}
+						return true;
+					}
+				})
+				// consoles have 1 out link with an address that starts with Ltemp. and
+				// 1 in link with no address
+				if (outs == 1 && ins == 1 &&
+					inaddr == null && outaddr.startsWith('Ltemp.')) {
+					return true;
+				}
+			}
+			return false;
+		},
+
       /*
        * send the management messages that build up the topology
        *
@@ -329,9 +397,9 @@ var QDR = (function(QDR) {
         // results contains more nodes than actually respond within
         // the timeout. However, if the responses we get don't contain
         // the missing node, assume we are done.
-            QDR.log.debug("timed out waiting for management responses");
+            QDR.log.info("timed out waiting for management responses");
             // note: can't use 'this' in a timeout handler
-            self.topology.dump("state at timeout");
+            self.topology.miniDump("state at timeout");
             // check if _nodeInfo is consistent
             if (self.topology.isConsistent()) {
                 //TODO: notify controllers which node was dropped
@@ -402,60 +470,31 @@ var QDR = (function(QDR) {
             if (this._expected[id].indexOf(key) == -1)
                 this._expected[id].push(key);
         },
-/*
-The response looks like:
-{
-    ".router": {
-        "results": [
-            [4, "router/QDR.X", 1, "0", 3, 60, 60, 11, "QDR.X", 30, "interior", "org.apache.qpid.dispatch.router", 5, 12, "router/QDR.X"]
-        ],
-        "attributeNames": ["raIntervalFlux", "name", "helloInterval", "area", "helloMaxAge", "mobileAddrMaxAge", "remoteLsMaxAge", "addrCount", "routerId", "raInterval", "mode", "type", "nodeCount", "linkCount", "identity"]
-    },
-    ".connection": {
-        "results": [
-            ["QDR.B", "connection/0.0.0.0:20002", "operational", "0.0.0.0:20002", "inter-router", "connection/0.0.0.0:20002", "ANONYMOUS", "org.apache.qpid.dispatch.connection", "out"],
-            ["QDR.A", "connection/0.0.0.0:20001", "operational", "0.0.0.0:20001", "inter-router", "connection/0.0.0.0:20001", "ANONYMOUS", "org.apache.qpid.dispatch.connection", "out"],
-            ["b2de2f8c-ef4a-4415-9a23-000c2f86e85d", "connection/localhost:33669", "operational", "localhost:33669", "normal", "connection/localhost:33669", "ANONYMOUS", "org.apache.qpid.dispatch.connection", "in"]
-        ],
-        "attributeNames": ["container", "name", "state", "host", "role", "identity", "sasl", "type", "dir"]
-    },
-    ".router.node": {
-        "results": [
-            ["QDR.A", null],
-            ["QDR.B", null],
-            ["QDR.C", "QDR.A"],
-            ["QDR.D", "QDR.A"],
-            ["QDR.Y", "QDR.A"]
-        ],
-        "attributeNames": ["routerId", "nextHop"]
-    }
-}*/
         ondone: function () {
             clearTimeout(this.timerHandle);
             this._gettingTopo = false;
             //this.miniDump();
             //this.dump();
             self.notifyTopologyDone();
-
          },
          dump: function (prefix) {
             if (prefix)
-                QDR.log.debug(prefix);
-            QDR.log.debug("---");
+                QDR.log.info(prefix);
+            QDR.log.info("---");
             for (var key in this._nodeInfo) {
-                QDR.log.debug(key);
+                QDR.log.info(key);
                 console.dump(this._nodeInfo[key]);
-                QDR.log.debug("---");
+                QDR.log.info("---");
             }
             QDR.log.debug("was still expecting:");
             console.dump(this._expected);
         },
          miniDump: function (prefix) {
             if (prefix)
-                QDR.log.debug(prefix);
-            QDR.log.debug("---");
+                QDR.log.info(prefix);
+            QDR.log.info("---");
             console.dump(Object.keys(this._nodeInfo));
-            QDR.log.debug("---");
+            QDR.log.info("---");
         },
         onerror: function (err) {
             this._gettingTopo = false;
@@ -523,16 +562,10 @@ The response looks like:
 			//QDR.log.debug("got all results for  " + entity);
 			// aggregate the responses
 			var newResponse = {};
-			newResponse['aggregates'] = [];
 			var thisNode = responses[selectedNodeId];
-			if (!thisNode) {
-				newResponse['attributeNames'] = ['name'];
-				newResponse['results'] = [''];
-				callback(nodeNames, entity, newResponse);
-				return;
-			}
 			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 = [];
@@ -557,8 +590,8 @@ The response looks like:
 								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]})
 								}
+								aggregate[i].detail.push({node: self.nameFromId(id)+':', val: result[i]})
 							})
 							return true; // stop looping
 						}
@@ -569,7 +602,7 @@ The response looks like:
 						// because it was not in the selectedNodeId's results
 						var vals = [];
 						result.forEach( function (val) {
-							vals.push({sum: val, detail: []})
+							vals.push({sum: val, detail: [{node: self.nameFromId(id), val: val}]})
 						})
 						newResponse.aggregates.push(vals)
 					}
@@ -586,10 +619,24 @@ The response looks like:
             ret = self.sendMgmtQuery('GET-SCHEMA')
         ).then(ret.id, function(response) {
             //QDR.log.debug("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;
-            //self.schema = angular.copy(response);
-            //self.topology.cleanUp(response);
-            self.notifyTopologyDone();
+	        self.topology.get();
         }, ret.error);
       },
 
@@ -636,8 +683,13 @@ The response looks like:
 			var application_properties = {
 				operation:  operation
 			}
-			if (attrs.type)
-				application_properties.type = attrs.type;
+			var ent = self.schema.entityTypes[entity];
+			var fullyQualifiedType = ent.fullyQualifiedType;
+			if (fullyQualifiedType) {
+				application_properties.type = fullyQualifiedType
+			}
+			else
+				application_properties.type = entity;
 			if (attrs.name)
 				application_properties.name = attrs.name;
 			var msg = {
@@ -676,8 +728,11 @@ The response looks like:
             }
 		if (entity[0] === '.')
 			entity = entity.substr(1, entity.length-1)
-		//return self._send(body, fullAddr, operation, entity);
-		return self._send(body, fullAddr, operation, "org.apache.qpid.dispatch." + entity);
+		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) {
@@ -742,8 +797,8 @@ The response looks like:
 				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")
@@ -757,55 +812,65 @@ The response looks like:
 			}
 			var onDisconnect = function () {
 				//QDR.log.warn("Disconnected");
+				self.connectionError = true;
 				stop();
 				self.executeDisconnectActions();
 			}
 
 			QDR.log.debug("****** calling rhea.connect ********")
-            var connection = self.rhea.connect({
+			var connection;
+			try {
+                connection = self.rhea.connect({
                     connection_details:ws('ws://' + baseAddress, ["binary", "AMQWSB10"]),
                     reconnect:true,
                     properties: {console_identifier: 'Dispatch console'}
-            });
-			connection.on('connection_open', function (context) {
-				QDR.log.debug("connection_opened")
-				okay.connection = true;
-				okay.receiver = false;
-				okay.sender = false;
-			})
-			connection.on('disconnected', function (context) {
-				onDisconnect();
-				self.errorText = "Error: Connection failed"
-				self.executeDisconnectActions();
-				self.connectionError = true;
-			})
-			connection.on('connection_close', function (context) {
+	            });
+			}
+			catch (e) {
+				QDR.log.debug("exception caught on connect")
+				self.errorText = "Connection failed"
 				onDisconnect();
-				self.errorText = "Disconnected"
-			})
-
-			var sender = connection.open_sender();
-			sender.on('sender_open', function (context) {
-				QDR.log.debug("sender_opened")
-				okay.sender = true
-				maybeStart()
-			})
-			sender.on('sendable', function (context) {
-				//QDR.log.debug("sendable")
-				self.sendable = true;
-				maybeStart();
-			})
+			}
+			if (!self.connectionError) {
+				connection.on('connection_open', function (context) {
+					QDR.log.debug("connection_opened")
+					okay.connection = true;
+					okay.receiver = false;
+					okay.sender = false;
+				})
+				connection.on('disconnected', function (context) {
+					QDR.log.debug("connection disconnected")
+					self.errorText = "Unable to connect"
+					onDisconnect();
+				})
+				connection.on('connection_close', function (context) {
+					QDR.log.debug("connection closed")
+					self.errorText = "Disconnected"
+					onDisconnect();
+				})
 
-			var receiver = connection.open_receiver({source: {dynamic: true}});
-			receiver.on('receiver_open', function (context) {
-				QDR.log.debug("receiver_opened")
-				okay.receiver = true;
-				maybeStart()
-			})
-			receiver.on("message", function (context) {
-				self.correlator.resolve(context);
-			});
+				var sender = connection.open_sender();
+				sender.on('sender_open', function (context) {
+					QDR.log.debug("sender_opened")
+					okay.sender = true
+					maybeStart()
+				})
+				sender.on('sendable', function (context) {
+					//QDR.log.debug("sendable")
+					self.sendable = true;
+					maybeStart();
+				})
 
+				var receiver = connection.open_receiver({source: {dynamic: true}});
+				receiver.on('receiver_open', function (context) {
+					QDR.log.debug("receiver_opened")
+					okay.receiver = true;
+					maybeStart()
+				})
+				receiver.on("message", function (context) {
+					self.correlator.resolve(context);
+				});
+			}
 		}
       }
     }
@@ -818,8 +883,8 @@ The response looks like:
 (function() {
     console.dump = function(object) {
         if (window.JSON && window.JSON.stringify)
-            console.log(JSON.stringify(object,undefined,2));
+            QDR.log.info(JSON.stringify(object,undefined,2));
         else
             console.log(object);
     };
-})();
\ No newline at end of file
+})();


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


Mime
View raw message