Repository: qpid-dispatch
Updated Branches:
  refs/heads/master 8475aca8f -> 96ce4a6c8


DISPATCH-201 Updates for dispatch 0.6.0


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

Branch: refs/heads/master
Commit: 96ce4a6c869c7caa1d8d762c9227da23fb98ca04
Parents: 8475aca
Author: Ernest Allen <eal...@redhat.com>
Authored: Tue Mar 29 10:42:05 2016 -0400
Committer: Ernest Allen <eal...@redhat.com>
Committed: Tue Mar 29 10:42:05 2016 -0400

----------------------------------------------------------------------
 .../src/main/webapp/plugin/css/brokers.ttf      | Bin 0 -> 2272 bytes
 .../src/main/webapp/plugin/css/plugin.css       | 106 +++-
 .../src/main/webapp/plugin/css/qdrTopology.css  |  76 ++-
 .../src/main/webapp/plugin/html/qdrList.html    |  54 +-
 .../main/webapp/plugin/html/qdrOverview.html    |   2 +-
 .../main/webapp/plugin/html/qdrTopology.html    |  16 +-
 .../src/main/webapp/plugin/js/dispatchPlugin.js |  21 +-
 .../hawtio/src/main/webapp/plugin/js/navbar.js  |   4 +-
 .../hawtio/src/main/webapp/plugin/js/qdrList.js | 260 ++++++++--
 .../src/main/webapp/plugin/js/qdrOverview.js    |  54 +-
 .../src/main/webapp/plugin/js/qdrService.js     | 208 +++++---
 .../src/main/webapp/plugin/js/qdrTopology.js    | 492 ++++++++++++-------
 12 files changed, 979 insertions(+), 314 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/96ce4a6c/console/hawtio/src/main/webapp/plugin/css/brokers.ttf
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/css/brokers.ttf 
b/console/hawtio/src/main/webapp/plugin/css/brokers.ttf
new file mode 100644
index 0000000..ae83968
Binary files /dev/null and 
b/console/hawtio/src/main/webapp/plugin/css/brokers.ttf differ

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/96ce4a6c/console/hawtio/src/main/webapp/plugin/css/plugin.css
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/css/plugin.css 
b/console/hawtio/src/main/webapp/plugin/css/plugin.css
index c6f5cc1..889bf72 100644
--- a/console/hawtio/src/main/webapp/plugin/css/plugin.css
+++ b/console/hawtio/src/main/webapp/plugin/css/plugin.css
@@ -279,10 +279,13 @@ div.qdrList li.active, ul.qdrListNodes li.active {
 div.qdr-attributes span.dynatree-selected a {
     background-color: #e0e0ff;
 }
-div.qdr-attributes.pane {
+div.qdr-attributes.pane, div.qdr-topology.pane {
        position: absolute;
        margin-left: 10px;
 }
+div.qdr-topology.pane.left {
+       width: auto;
+}
 
 /* the selected row in the name table */
 div#main.qdr div.qdrList div.selected {
@@ -428,8 +431,8 @@ ul.qdrTopoModes {
        background:#e0e0ff;
 }
 
-.qdr-overview.pane.left, .qdr-attributes.pane.left {
-       top: 100px;
+.qdr-overview.pane.left, .qdr-attributes.pane.left, .qdr-topology.pane.left {
+       top: 104px;
 }
 .qdr-overview.pane.left {
        left: 10px;
@@ -723,3 +726,100 @@ span:not(.dynatree-has-children).allocator 
.dynatree-icon:before {
 .changed {
     color: #339933;
 }
+
+div.dispatch-router div.help {
+    width: auto;
+    padding: 1em;
+    background-color: lavender;
+    border-radius: 6px;
+    margin-top: 1em;
+    text-align: center;
+}
+
+div.operations tr:nth-child(even) {
+       background: #f3f3f3;
+}
+div.operations tr:nth-child(odd), div.operations tr:last-child {
+       background: #fff;
+}
+
+div.operations tr input {
+       margin: 0;
+       padding: 3px 6px;
+}
+div.operations table {
+    width: 100%;
+}
+div.operations th {
+    width: 50%;
+    border-bottom: 1px solid #cccccc;
+    text-align: left;
+}
+div.operations td:nth-child(odd), div.operations th:nth-child(odd) {
+       border-right: 1px solid #cccccc;
+}
+div.operations td:nth-child(odd) {
+       padding-left: 0;
+}
+div.operations td:nth-child(even), div.operations th:nth-child(even) {
+       padding-left: 5px;
+}
+div.operations th {
+       padding: 5px;
+}
+div.operations .tab-pane.active {
+    padding: 12px 12px 12px 0;
+}
+div.operations label {
+    padding-top: 4px;
+    margin-bottom: 4px;
+}
+.qdrListActions .ngGrid {
+       /*min-height: 40em;
+       height: 100%; */
+}
+div.qdrListActions .ngViewport {
+    height: initial !important;
+}
+
+div.operations .boolean {
+    padding-bottom: 0;
+}
+
+table.log-entry {
+    margin-bottom: 1em;
+    border-top: 1px solid black;
+}
+
+table.log-entry pre {
+    background-color: #f5f5f5;
+    color: inherit;
+    margin: 0;
+}
+
+circle.node.normal.console {
+    fill: lightcyan;
+}
+
+text.console, text.on-demand, text.normal {
+       font-family: FontAwesome;
+       font-weight: normal;
+       font-size: 16px;
+}
+
+@font-face {
+    font-family:"Brokers";
+    src: url("brokers.ttf") /* TTF file for CSS3 browsers */
+}
+
+text.artemis.on-demand {
+    font-family: Brokers;
+    font-size: 20px;
+    font-weight: bold;
+}
+
+text.qpid-cpp.on-demand {
+    font-family: Brokers;
+    font-size: 18px;
+    font-weight: bold;
+}

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/96ce4a6c/console/hawtio/src/main/webapp/plugin/css/qdrTopology.css
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/css/qdrTopology.css 
b/console/hawtio/src/main/webapp/plugin/css/qdrTopology.css
index 0eac80d..bc7ccfc 100644
--- a/console/hawtio/src/main/webapp/plugin/css/qdrTopology.css
+++ b/console/hawtio/src/main/webapp/plugin/css/qdrTopology.css
@@ -97,7 +97,11 @@ circle.node.normal {
     fill: #F0F000;
 }
 circle.node.on-demand {
-    fill: #00F000;
+    fill: #C0FFC0;
+}
+circle.node.on-demand.artemis {
+       fill: #FCC;
+       /*opacity: 0.2; */
 }
 
 circle.node.fixed {
@@ -148,6 +152,11 @@ text.id {
   font-weight: bold;
 }
 
+text.label {
+  text-anchor: start;
+  font-weight: bold;
+}
+
 .row-fluid.tertiary {
   position: relative;
   left: 20px;
@@ -159,7 +168,26 @@ text.id {
 
 .row-fluid.tertiary.panel {
   width: 410px;
-  height: 100%;
+  /*height: 100%; */
+}
+
+/*, div.qdrTopology div#multiple_details .ngViewport*/
+div#topologyForm .ngViewport, div#topologyForm .gridStyle {
+    height: inherit !important;
+       min-height: initial !important;
+       overflow: initial;
+}
+
+div#multiple_details {
+       height: 300px;
+       width: 500px;
+       display: none;
+       padding: 1em;
+    border: 1px solid;
+       position: absolute;
+       background-color: white;
+       max-height: 330px !important;
+    overflow: hidden;
 }
 
 .panel-adjacent {
@@ -170,10 +198,13 @@ text.id {
   border: 1px solid red;
 }
 #topologyForm {
-  border: 1px solid white;
-  padding: 2px;
-  position: relative;
-  top: -8px;
+    border: 1px solid white;
+    padding: 2px;
+    /* position: relative; */
+    /* top: -8px; */
+}
+div.qdr-topology.pane.left .ngViewport {
+    /* border: 1px solid lightgray; */
 }
 
 #topologyForm > div {
@@ -479,6 +510,13 @@ div.boolean {
   stroke-width: 3px;
 }
 
+circle.subcircle {
+    stroke-width: 1px;
+    /* stroke-dasharray: 2; */
+    fill-opacity: 0;
+    stroke: black;
+}
+
 .leaf circle {
   fill: #6fa8dc;
   fill-opacity: 0.95;
@@ -490,6 +528,28 @@ div.boolean {
 
 }
 
-.qdrListActions .ngGrid {
-       height: 100vh;
+#svg_legend {
+    position: absolute;
+    top: 110px;
+    right: 0;
+    border: 1px solid #ccc;
+    border-radius: 5px;
+    background-color: #fcfcfc;
+    margin-right: 1.3em;
+       padding: 1em;
+}
+
+#svg_legend svg {
+    height: 235px;
+    width: 180px;
+}
+
+#multiple_details div.gridStyle {
+/*     height: 50em; */
+       min-height: 70px !important;
+       height: auto !important;
+}
+
+#multiple_details .ngViewport {
+    height: auto !important;
 }
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/96ce4a6c/console/hawtio/src/main/webapp/plugin/html/qdrList.html
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/html/qdrList.html 
b/console/hawtio/src/main/webapp/plugin/html/qdrList.html
index adb249f..d07d3ca 100644
--- a/console/hawtio/src/main/webapp/plugin/html/qdrList.html
+++ b/console/hawtio/src/main/webapp/plugin/html/qdrList.html
@@ -26,12 +26,60 @@ under the License.
         </div>
     </hawtio-pane>
     <div class="row-fluid qdrListActions">
+        <ul class="nav nav-tabs">
+            <li ng-repeat="mode in modes" ng-show="isValid(mode)" 
ng-click="selectMode(mode)" ng-class="{active : isModeSelected(mode)}" 
title="{{mode.title}}" ng-bind-html-unsafe="mode.content"> </li>
+        </ul>
         <h4>{{selectedRecordName}}</h4>
         <div ng-show="currentMode.id === 'attributes'" class="selectedItems">
-            <div ng-grid="details"></div>
+            <div ng-show="selectedRecordName === selectedEntity" 
class="no-content">There are no {{selectedEntity}}s</div>
+            <div ng-hide="selectedRecordName === selectedEntity" 
ng-grid="details"></div>
         </div>
-        <div ng-show="currentMode.id === 'operations'">
-            Operations are not implemented yet.
+        <div class="operations" ng-show="currentMode.id === 'operations'">
+            <!-- <div ng-grid="detailsCREATE"></div> -->
+            <fieldset ng-show="operation != ''">
+                <table>
+                    <tr>
+                        <th>Attribute</th>
+                        <th>Value</th>
+                    </tr>
+                <tr title="{{attribute.title}}" ng-repeat="attribute in 
detailFields">
+                    <td><label for="{{attribute.name}}">{{attribute.name | 
humanify}}</label></td>
+                    <!-- we can't do <input type="{angular expression}"> 
because... jquery throws an exception because... -->
+                    <td>
+                    <div ng-if="attribute.input == 'input'">
+                        <!-- ng-pattern="testPattern(attribute)" -->
+                        <input ng-if="attribute.type == 'number'" 
type="number" name="{{attribute.name}}" id="{{attribute.name}}" 
ng-model="attribute.rawValue" ng-required="attribute.required" 
class="ui-widget-content ui-corner-all"/>
+                        <input ng-if="attribute.type == 'text'" type="text" 
name="{{attribute.name}}" id="{{attribute.name}}" 
ng-model="attribute.attributeValue" ng-required="attribute.required" 
class="ui-widget-content ui-corner-all"/>
+                        <span ng-if="attribute.type == 'disabled'" 
>{{getAttributeValue(attribute)}}</span>
+                    </div>
+                    <div ng-if="attribute.input == 'select'">
+                        <select id="{{attribute.name}}" 
ng-model="attribute.selected" ng-options="item for item in attribute.rawtype 
track by item"></select>
+                    </div>
+                    <div ng-if="attribute.input == 'boolean'" class="boolean">
+                        <label><input type="radio" 
ng-model="attribute.rawValue" ng-value="true"> True</label>
+                        <label><input type="radio" 
ng-model="attribute.rawValue" ng-value="false"> False</label>
+                    </div>
+                    </td>
+                </tr>
+                <tr><td></td><td><button class="btn btn-primary" type="button" 
ng-click="ok()">{{operation | Pascalcase}}</button></td></tr>
+                </table>
+            </fieldset>
+        </div>
+        <div ng-show="currentMode.id === 'log'">
+            <table class="log-entry" ng-repeat="entry in logResults track by 
$index">
+                <tr>
+                    <td align="left" colspan="2">{{entry.time}}</td>
+                </tr>
+                <tr>
+                    <td>Type</td><td>{{entry.type}}</td>
+                </tr>
+                <tr>
+                    <td>Source</td><td>{{entry.source}}:{{entry.line}}</td>
+                </tr>
+                <tr>
+                    <td valign="middle">Message</td><td 
valign="middle"><pre>{{entry.message}}</pre></td>
+                </tr>
+            </table>
         </div>
     </div>
 </div>

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/96ce4a6c/console/hawtio/src/main/webapp/plugin/html/qdrOverview.html
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/html/qdrOverview.html 
b/console/hawtio/src/main/webapp/plugin/html/qdrOverview.html
index 61d5143..2a66a94 100644
--- a/console/hawtio/src/main/webapp/plugin/html/qdrOverview.html
+++ b/console/hawtio/src/main/webapp/plugin/html/qdrOverview.html
@@ -47,7 +47,7 @@ under the License.
 
 <script type="text/ng-template" id="router.html">
     <div class="row-fluid">
-        <h3>Router {{router.data.title}}</h3>
+        <h3>Router {{router.data.title}} attributes</h3>
         <div class="gridStyle noHighlight" ng-grid="routerGrid"></div>
     </div>
 </script>

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/96ce4a6c/console/hawtio/src/main/webapp/plugin/html/qdrTopology.html
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/html/qdrTopology.html 
b/console/hawtio/src/main/webapp/plugin/html/qdrTopology.html
index 704c8e2..61ca2c1 100644
--- a/console/hawtio/src/main/webapp/plugin/html/qdrTopology.html
+++ b/console/hawtio/src/main/webapp/plugin/html/qdrTopology.html
@@ -17,19 +17,18 @@ specific language governing permissions and limitations
 under the License.
 -->
 <div class="qdrTopology row-fluid" ng-controller="QDR.TopologyController">
-    <div class="tertiary left panel">
+    <div class="qdr-topology pane left" 
ng-controller="QDR.TopologyFormController">
         <div id="topologyForm" ng-class="{selected : isSelected()}">
             <!-- <div ng-repeat="form in forms" ng-show="isVisible(form)" 
ng-class='{selected : isSelected(form)}'> -->
-
-            <div ng-show="isGeneral()">
+            <div ng-show="form == 'router'">
                 <h4>Router Info</h4>
                 <div class="gridStyle" ng-grid="topoGridOptions"></div>
             </div>
-            <div ng-show="isConnections()">
+            <div ng-show="form == 'connection'">
                 <h4>Connection Info</h4>
-                <div class="gridStyle" ng-grid="topoConnOptions"></div>
+                <div class="gridStyle" ng-grid="topoGridOptions"></div>
             </div>
-            <div id="addNodeForm" ng-show="isAddNode()">
+            <div id="addNodeForm" ng-show="form == 'add'">
                 <h4>Add a new router</h4>
                 <ul>
                     <li>Click on an existing router to create a connection to 
the new router</li>
@@ -71,7 +70,10 @@ under the License.
                 <li ng-click="removeLink()">Remove connection</li>
             </ul>
         </div>
-
+        <div id="svg_legend"></div>
+        <div id="multiple_details">
+            <div class="gridStyle" ng-grid="multiDetails"></div>
+        </div>
     </div>
 </div>
 <!--

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/96ce4a6c/console/hawtio/src/main/webapp/plugin/js/dispatchPlugin.js
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/js/dispatchPlugin.js 
b/console/hawtio/src/main/webapp/plugin/js/dispatchPlugin.js
index 59ea4d3..5061317 100644
--- a/console/hawtio/src/main/webapp/plugin/js/dispatchPlugin.js
+++ b/console/hawtio/src/main/webapp/plugin/js/dispatchPlugin.js
@@ -88,10 +88,12 @@ var QDR = (function(QDR) {
                        
$compileProvider.urlSanitizationWhitelist(/^\s*(https?|ftp|mailto|file|blob):/);
                        cur = $compileProvider.urlSanitizationWhitelist();
          })
+
          .config(function (JSONFormatterConfigProvider) {
                        // Enable the hover preview feature
                        JSONFormatterConfigProvider.hoverPreviewEnabled = true;
          })
+
          .filter('to_trusted', function($sce){
                        return function(text) {
                                return $sce.trustAsHtml(text);
@@ -107,7 +109,15 @@ var QDR = (function(QDR) {
                                var nameParts = name.split('/')
                                return nameParts.length > 1 ? 
nameParts[nameParts.length-1] : name;
                        };
-         });
+         })
+         .filter('Pascalcase', function () {
+               return function (str) {
+                               if (!str)
+                                       return "";
+                   return str.replace(/(\w)(\w*)/g,
+                        function(g0,g1,g2){return g1.toUpperCase() + 
g2.toLowerCase();});
+               }
+         })
 /*
        QDR.module.config(['$locationProvider', function($locationProvider) {
         $locationProvider.html5Mode(true);
@@ -134,6 +144,7 @@ var QDR = (function(QDR) {
                
Core.addCSS("https://cdn.rawgit.com/mohsen1/json-formatter/master/dist/json-formatter.min.css";);
                
Core.addCSS("https://cdnjs.cloudflare.com/ajax/libs/jquery.tipsy/1.0.2/jquery.tipsy.css";);
                
Core.addCSS("https://code.jquery.com/ui/1.8.24/themes/base/jquery-ui.css";);
+               
Core.addCSS("https://maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css";);
 
                // tell hawtio that we have our own custom layout for
                // our view
@@ -217,16 +228,14 @@ var QDR = (function(QDR) {
 
 })(QDR || {});
 
-
-// tell the hawtio plugin loader about our plugin so it can be
-// bootstrapped with the rest of angular
-hawtioPluginLoader.addModule(QDR.pluginName);
-
 
$.getScript('https://cdn.rawgit.com/angular-ui/ui-slider/master/src/slider.js', 
function() {
        hawtioPluginLoader.addModule('ui.slider');
 });
 
$.getScript('https://cdn.rawgit.com/mohsen1/json-formatter/master/dist/json-formatter.min.js',
 function() {
        hawtioPluginLoader.addModule('jsonFormatter');
+       // tell the hawtio plugin loader about our plugin so it can be
+       // bootstrapped with the rest of angular
+       hawtioPluginLoader.addModule(QDR.pluginName);
 });
 
 // force an more modern version of d3 to load

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/96ce4a6c/console/hawtio/src/main/webapp/plugin/js/navbar.js
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/js/navbar.js 
b/console/hawtio/src/main/webapp/plugin/js/navbar.js
index d305f38..8cfecdf 100644
--- a/console/hawtio/src/main/webapp/plugin/js/navbar.js
+++ b/console/hawtio/src/main/webapp/plugin/js/navbar.js
@@ -43,8 +43,8 @@ var QDR = (function (QDR) {
         href: "#/dispatch_plugin/overview"
       },
     {
-        content: '<i class="icon-list "></i> Details',
-        title: "View the attributes of the router nodes",
+        content: '<i class="icon-list "></i> Entities',
+        title: "View the attributes of the router entities",
         isValid: function (QDRService) { return QDRService.isConnected(); },
         href: "#/dispatch_plugin/list"
       },

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/96ce4a6c/console/hawtio/src/main/webapp/plugin/js/qdrList.js
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrList.js 
b/console/hawtio/src/main/webapp/plugin/js/qdrList.js
index 72fbc04..d816521 100644
--- a/console/hawtio/src/main/webapp/plugin/js/qdrList.js
+++ b/console/hawtio/src/main/webapp/plugin/js/qdrList.js
@@ -28,9 +28,10 @@ var QDR = (function(QDR) {
    *
    * Controller for the main interface
    */
-       QDR.module.controller("QDR.ListController", ['$scope', '$location', 
'$dialog', 'localStorage', 'QDRService', 'QDRChartService',
-               function ($scope, $location, $dialog, localStorage, QDRService, 
QDRChartService) {
+       QDR.module.controller("QDR.ListController", ['$scope', '$location', 
'$dialog', '$filter', 'localStorage', 'QDRService', 'QDRChartService',
+               function ($scope, $location, $dialog, $filter, localStorage, 
QDRService, QDRChartService) {
 
+               $scope.details = [];
                if (!QDRService.connected) {
                        // we are not connected. we probably got here from a 
bookmark or manual page reload
                        $location.path("/dispatch_plugin/connect")
@@ -43,6 +44,75 @@ var QDR = (function(QDR) {
                $scope.selectedNodeId = localStorage['QDRSelectedNodeId'];
                $scope.selectedRecordName = 
localStorage['QDRSelectedRecordName'];
 
+               $scope.modes = [{
+               content: '<a><i class="icon-list"></i> Attriutes</a>',
+                       id: 'attributes',
+                       op: 'READ',
+                       title: "View router attributes",
+               isValid: function () { return true; }
+           },
+           {
+               content: '<a><i class="icon-edit"></i> Update</a>',
+               id: 'operations',
+               op: 'UPDATE',
+               title: "Update this attribute",
+               isValid: function () { return 
$scope.operations.indexOf(this.op) > -1 }
+           },
+           {
+               content: '<a><i class="icon-plus"></i> Create</a>',
+               id: 'operations',
+               op: 'CREATE',
+               title: "Create a new attribute",
+               isValid: function () { return 
$scope.operations.indexOf(this.op) > -1 }
+           },
+           {
+               content: '<a><i class="icon-chart"></i> Chart</a>',
+               id: 'charts',
+               op: 'graph',
+               title: "Graph attributes",
+               isValid: function () { return false; return 
$scope.detailFields.some( function (field ) {
+                                                                               
        return field.graph;
+                                                                               
})}
+           },
+           {
+               content: '<a><i class="icon-eye-open"></i> Fetch</a>',
+               id: 'log',
+               op: 'GET-LOG',
+               title: "Fetch recent log entries",
+               isValid: function () { return ($scope.selectedEntity === 'log') 
}
+           }
+           ];
+               $scope.operations = []
+        $scope.currentMode = $scope.modes[0];
+               $scope.isModeSelected = function (mode) {
+                       return mode === $scope.currentMode;
+               }
+               $scope.selectMode = function (mode) {
+                       $scope.currentMode = mode;
+                       if (mode.id === 'log') {
+                               $scope.logResults = "getting recent log 
entries...";
+                               QDRService.sendMethod($scope.currentNode.id, 
$scope.selectedEntity, {}, $scope.currentMode.op, function (nodeName, entity, 
response, context) {
+                                       $scope.logResults = response.filter( 
function (entry) {
+                                               return entry[0] === 
$scope.detailsObject.module
+                                       }).sort( function (a, b) {
+                                               return b[5] - a[5]
+                                       }).map( function (entry) {
+                                               return {
+                                                       type: entry[1],
+                                                       message: entry[2],
+                                                       source: entry[3],
+                                                       line: entry[4],
+                                                       time: 
Date(entry[5]).toString()
+                                               }
+                                       })
+                                       $scope.$apply();
+                               })
+                       }
+               }
+               $scope.isValid = function (mode) {
+                       return mode.isValid()
+               }
+
                $scope.nodes = QDRService.nodeList().sort(function (a, b) { 
return a.name.toLowerCase() > b.name.toLowerCase()});
                if (!angular.isDefined($scope.selectedNode)) {
                        //QDR.log.debug("selectedNode was " + 
$scope.selectedNode);
@@ -69,33 +139,20 @@ var QDR = (function(QDR) {
                                return row.linkType.value;
                        }
                }
-               $scope.modes = [
-                   {
-                       content: '<a><i class="icon-list"></i> Attriutes</a>',
-                               id: 'attributes',
-                               title: "View router attributes",
-                       isValid: function () { return true; }
-                   },
-                   {
-                       content: '<a><i class="icon-leaf"></i> Operations</a>',
-                       id: 'operations',
-                       title: "Execute operations",
-                       isValid: function () { return true; }
-                   }
-        ];
-        $scope.currentMode = $scope.modes[0];
-               $scope.isModeSelected = function (mode) {
-                       return mode === $scope.currentMode;
-               }
-               $scope.selectMode = function (mode) {
-                       $scope.currentMode = mode;
+
+               var lookupOperations = function () {
+                       var ops = 
QDRService.schema.entityTypes[$scope.selectedEntity].operations.filter( 
function (op) { return op !== 'READ'});
+                       $scope.operation = ops.length ? ops[0] : "";
+                       return ops;
                }
 
         var entityTreeChildren = [];
-               for (var entity in QDRService.schema.entityTypes) {
+        var sortedEntities = Object.keys(QDRService.schema.entityTypes).sort();
+               sortedEntities.forEach( function (entity) {
                        if (excludedEntities.indexOf(entity) == -1) {
                                if (!angular.isDefined($scope.selectedEntity)) {
                                        $scope.selectedEntity = entity;
+                                       $scope.operations = lookupOperations()
                                }
                                var current = entity === $scope.selectedEntity;
                                var e = new Folder(entity)
@@ -106,7 +163,7 @@ var QDR = (function(QDR) {
                            e.children = [placeHolder]
                                entityTreeChildren.push(e)
                        }
-               }
+               })
                $scope.treeReady = function () {
                        $('#entityTree').dynatree({
                                onActivate: onTreeSelected,
@@ -123,10 +180,16 @@ var QDR = (function(QDR) {
                }
                // a tree node was selected
                var onTreeSelected = function (selectedNode) {
+                       if ($scope.currentMode.id === 'operations')
+                               $scope.currentMode = $scope.modes[0];
+                       else if ($scope.currentMode.id === 'log')
+                               $scope.selectMode($scope.currentMode)
                        if (selectedNode.data.typeName === "entity") {
                                $scope.selectedEntity = selectedNode.data.key;
+                               $scope.operations = lookupOperations()
                        } else if (selectedNode.data.typeName === 'attribute') {
                                $scope.selectedEntity = 
selectedNode.parent.data.key;
+                               $scope.operations = lookupOperations()
                                $scope.selectedRecordName = 
selectedNode.data.key;
                                updateDetails(selectedNode.data.details);
                                
$("#entityTree").dynatree("getRoot").visit(function(node){
@@ -136,6 +199,28 @@ var QDR = (function(QDR) {
                        }
                        $scope.$apply();
                }
+               // fill in an empty results recoord based on the entities schema
+               var fromSchema = function (entityName) {
+                       var row = {}
+                       var schemaEntity = 
QDRService.schema.entityTypes[entityName]
+                       for (attr in schemaEntity.attributes) {
+                               var entity = schemaEntity.attributes[attr]
+                               row[attr] = {
+                                       value: "",
+                                       type: entity.type,
+                                       graph: false,
+                                       title: entity.description,
+                                       aggregate: false,
+                                       aggregateTip: ''
+                               }
+                       }
+                       return row;
+               }
+               $scope.hasCreate = function () {
+                       var schemaEntity = 
QDRService.schema.entityTypes[$scope.selectedEntity]
+                       return (schemaEntity.operations.indexOf("CREATE") > -1)
+               }
+
                // the data for the selected entity is available, populate the 
tree
                var updateEntityChildren = function (tableRows, expand) {
                        var tree = $("#entityTree").dynatree("getTree");
@@ -148,8 +233,8 @@ var QDR = (function(QDR) {
                                typeName:   "none",
                                title:      "no data"
                            })
-                           $scope.selectedRecordName = "";
-                           updateDetails({});
+                           $scope.selectedRecordName = $scope.selectedEntity;
+                           updateDetails(fromSchema($scope.selectedEntity));
                        } else {
                                tableRows.forEach( function (row) {
                                        var addClass = $scope.selectedEntity;
@@ -184,24 +269,85 @@ var QDR = (function(QDR) {
                        }
                }
 
+               var schemaProps = function (entityName, key, currentNode) {
+                       var typeMap = {integer: 'number', string: 'text', path: 
'text', boolean: 'boolean'};
+
+                       var entity = QDRService.schema.entityTypes[entityName]
+                       var value = entity.attributes[key]
+                       // skip identity and depricated fields
+                       if (!value)
+                               return {input: 'input', type: 'disabled', 
required: false, selected: "", rawtype: 'string', disabled: true, 'default': ''}
+                       var description = value.description || ""
+                       var val = value['default'];
+                       var disabled = (key == 'identity' || 
description.startsWith('Deprecated'))
+                       // special cases
+                       if (entityName == 'log' && key == 'module') {
+                               return {input: 'input', type: 'disabled', 
required: false, selected: "", rawtype: 'string', disabled: true, 'default': ''}
+                       }
+                       if (entityName === 'linkRoutePattern' && key === 
'connector') {
+                               // turn input into a select. the values will be 
populated later
+                               value.type = []
+                               // find all the connector names and populate 
the select
+                               QDRService.getNodeInfo(currentNode.id, 
'.connector', ['name'], function (nodeName, dotentity, response) {
+                                       $scope.detailFields.some( function 
(field) {
+                                               if (field.name === 'connector') 
{
+                                                       field.rawtype = 
response.results.map (function (result) {return result[0]})
+                                                       return true;
+                                               }
+                                       })
+                               });
+                       }
+                       return {    name:       key,
+                                               humanName:  
QDRService.humanify(key),
+                        description:value.description,
+                        type:       disabled ? 'disabled' : 
typeMap[value.type],
+                        rawtype:    value.type,
+                        input:      typeof value.type == 'string' ? value.type 
== 'boolean' ? 'boolean' : 'input'
+                                                                  : 'select',
+                        selected:   val ? val : undefined,
+                        'default':  value['default'],
+                        value:      val,
+                        required:   value.required,
+                        unique:     value.unique,
+                        disabled:   disabled
+            };
+               }
+               $scope.getAttributeValue = function (attribute) {
+                       var value = attribute.attributeValue;
+                       if ($scope.currentMode.op === "CREATE" && 
attribute.name === 'identity')
+                               value = "<assigned by system>"
+                       return value;
+               }
                var updateDetails = function (row) {
                        var details = [];
-                       for (var attr in row) {
+                       $scope.detailsObject = {};
+                       var attrs = Object.keys(row).sort();
+                       attrs.forEach( function (attr) {
                                var changed = 
$scope.detailFields.filter(function (old) {
                                        return (old.name === attr) ? old.graph 
&& old.rawValue != row[attr].value : false;
                                })
+                               var schemaEntity = 
schemaProps($scope.selectedEntity, attr, $scope.currentNode)
                                details.push( {
                                        attributeName:  
QDRService.humanify(attr),
-                                       attributeValue: 
QDRService.pretty(row[attr].value),
+                                       attributeValue: attr === 'port' ? 
row[attr].value : QDRService.pretty(row[attr].value),
                                        name:           attr,
                                        changed:        changed.length,
                                        rawValue:       row[attr].value,
                                        graph:          row[attr].graph,
                                        title:          row[attr].title,
                                        aggregateValue: 
QDRService.pretty(row[attr].aggregate),
-                                       aggregateTip:   row[attr].aggregateTip
+                                       aggregateTip:   row[attr].aggregateTip,
+
+                                       input:          schemaEntity.input,
+                                       type:           schemaEntity.type,
+                                       required:       schemaEntity.required,
+                                       selected:       schemaEntity.selected,
+                                       rawtype:        schemaEntity.rawtype,
+                                       disabled:       schemaEntity.disabled,
+                                       'default':      schemaEntity['default']
                                })
-                       }
+                               $scope.detailsObject[attr] = row[attr].value;
+                       })
                        $scope.detailFields = details;
                        aggregateColumn();
                        $scope.$apply();
@@ -224,6 +370,7 @@ var QDR = (function(QDR) {
                        if (newValue !== oldValue) {
                                localStorage['QDRSelectedEntity'] = 
$scope.selectedEntity;
                                restartUpdate();
+                               $scope.operations = lookupOperations()
                        }
                })
                $scope.$watch('selectedNode', function(newValue, oldValue) {
@@ -241,14 +388,19 @@ var QDR = (function(QDR) {
                $scope.tableRows = [];
                var selectedRowIndex = 0;
                var updateTableData = function (entity, expand) {
+                       // don't update the data when on the operations tab
+                       if ($scope.currentMode.id === 'operations') {
+                               return;
+                       }
+
                        var gotNodeInfo = function (nodeName, dotentity, 
response) {
                                //QDR.log.debug("got results for  " + nodeName);
                                //console.dump(response);
-
                                var records = response.results;
                                var aggregates = response.aggregates;
                                var attributeNames = response.attributeNames;
                                var nameIndex = attributeNames.indexOf("name");
+                               var identityIndex = 
attributeNames.indexOf("identity");
                                var ent = QDRService.schema.entityTypes[entity];
                                var tableRows = [];
                                for (var i=0; i<records.length; ++i) {
@@ -258,7 +410,11 @@ var QDR = (function(QDR) {
                                        var rowName;
                                        if (nameIndex > -1) {
                                                rowName = record[nameIndex];
-                                       } else {
+                                               if (!rowName && identityIndex > 
-1) {
+                                                       rowName = 
record[nameIndex] = (dotentity + '/' + record[identityIndex])
+                                               }
+                                       }
+                                       if (!rowName) {
                                                QDR.log.error("response 
attributeNames did not contain a name field");
                                                
console.dump(response.attributeNames);
                                                return;
@@ -290,15 +446,15 @@ var QDR = (function(QDR) {
                                        }
                                        tableRows.push(row);
                                }
+                               tableRows.sort( function (a, b) { return 
a.name.value.localeCompare(b.name.value) })
                                setTimeout(selectRow, 0, {rows: tableRows, 
expand: expand});
                        }
-
                        // if this entity should show an aggregate column, send 
the request to get the info for this entity from all the nedes
                        if (aggregateEntities.indexOf(entity) > -1) {
                                var nodeInfo = QDRService.topology.nodeInfo();
                                
QDRService.getMultipleNodeInfo(Object.keys(nodeInfo), entity, [], gotNodeInfo, 
$scope.selectedNodeId);
                        } else {
-                               QDRService.getNodeInfo($scope.selectedNodeId, 
'.' + entity, [], gotNodeInfo);
+                               QDRService.getNodeInfo($scope.selectedNodeId, 
entity, [], gotNodeInfo);
                        }
                };
 
@@ -420,7 +576,6 @@ var QDR = (function(QDR) {
                                  return false;
                        }
                };
-
                updateTableData($scope.selectedEntity, true);
                stop = setInterval(updateTableData, 5000, 
$scope.selectedEntity);
 
@@ -432,6 +587,41 @@ var QDR = (function(QDR) {
                        };
                });
 
+               function gotMethodResponse (nodeName, entity, response, 
context) {
+                       var statusCode = 
context.message.application_properties.statusCode;
+                       if (statusCode < 200 || statusCode >= 300) {
+                               Core.notification('error', 
context.message.application_properties.statusDescription);
+                               
QDR.log.debug(context.message.application_properties.statusDescription)
+                       } else {
+                               var note = entity + " " + 
$filter('Pascalcase')($scope.currentMode.op) + "d"
+                               QDR.log.info(note)
+                               Core.notification('success', note);
+                               $scope.selectMode($scope.modes[0]);
+                               $scope.$apply();
+                       }
+               }
+               $scope.ok = function () {
+                       var attributes = {}
+                       $scope.detailFields.forEach( function (field) {
+                               var value = field.rawValue;
+                               if (field.input === 'input') {
+                                       if (field.type === 'text' || field.type 
=== 'disabled')
+                                               value = field.attributeValue;
+                               }
+                               if (field.input === 'select')
+                                       value = field.selected;
+
+                               if (value != field['default'] || field.required 
|| (field.name === 'role')) {
+                                       if (field.name !== 'identity')
+                                               attributes[field.name] = value
+                               }
+                               if (!attributes.type)
+                                       attributes.type = $scope.selectedEntity;
+
+                       })
+                       QDRService.sendMethod($scope.currentNode.id, 
$scope.selectedEntity, attributes, $scope.currentMode.op, gotMethodResponse)
+               }
+
                function doDialog(template, chart) {
                    var d = $dialog.dialog({
                      backdrop: true,

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/96ce4a6c/console/hawtio/src/main/webapp/plugin/js/qdrOverview.js
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrOverview.js 
b/console/hawtio/src/main/webapp/plugin/js/qdrOverview.js
index cc52868..bf75ffd 100644
--- a/console/hawtio/src/main/webapp/plugin/js/qdrOverview.js
+++ b/console/hawtio/src/main/webapp/plugin/js/qdrOverview.js
@@ -44,7 +44,31 @@ var QDR = (function (QDR) {
                        $location.path("/dispatch_plugin/connect")
                        return;
                }
-
+               // 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 "#/dispatch-plugin/attributes"; 
},
+                   index: 0
+               },
+               {
+                   content: '<i class="icon-leaf"></i> Operations',
+                   title: "Execute operations on your selection",
+                   isValid: function (workspace) { return true; },
+                   href: function () { return "#/dispatch-plugin/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;
+               }
                var nodeIds = QDRService.nodeIdList();
                var currentTimer;
                var refreshInterval = 5000
@@ -141,7 +165,28 @@ 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) {
+                                       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) {
+                                                                       
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);
+/*
+
                                                var results = 
response.aggregates
                                                results.forEach ( function 
(result) {
 
@@ -162,10 +207,11 @@ var QDR = (function (QDR) {
                                                        
clearTimeout(currentTimer)
                                                }
                                                currentTimer = 
setTimeout(allRouterInfo, refreshInterval);
-                                       }, nodeIds[0])
+*/
+                                       }, nodeIds[0], false)
                                }
                        }
-                       nodeIds.forEach ( function (nodeId) {
+                       nodeIds.forEach ( function (nodeId, i) {
                                QDRService.getNodeInfo(nodeId, ".connection", 
["role"], gotNodeInfo)
                        })
 

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/96ce4a6c/console/hawtio/src/main/webapp/plugin/js/qdrService.js
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrService.js 
b/console/hawtio/src/main/webapp/plugin/js/qdrService.js
index 06bf113..aebb71b 100644
--- a/console/hawtio/src/main/webapp/plugin/js/qdrService.js
+++ b/console/hawtio/src/main/webapp/plugin/js/qdrService.js
@@ -143,7 +143,7 @@ var QDR = (function(QDR) {
         // called by receiver's on('message') handler when a response arrives
         resolve: function(context) {
                        var correlationID = 
context.message.properties.correlation_id;
-            this._objects[correlationID].resolver(context.message.body);
+            this._objects[correlationID].resolver(context.message.body, 
context);
             delete this._objects[correlationID];
         }
     },
@@ -247,6 +247,14 @@ var QDR = (function(QDR) {
           return null;
       },
 
+               isArtemis: function (d) {
+                       return d.nodeType ==='on-demand' && 
!d.properties.product;
+               },
+
+               isQpid: function (d) {
+                       return d.nodeType ==='on-demand' && (d.properties && 
d.properties.product === 'qpid-cpp');
+               },
+
       /*
        * send the management messages that build up the topology
        *
@@ -482,74 +490,81 @@ The response looks like:
         }, ret.error);
       },
 
-               getMultipleNodeInfo: function (nodeNames, entity, attrs, 
callback, selectedNodeId) {
+               getMultipleNodeInfo: function (nodeNames, entity, attrs, 
callback, selectedNodeId, aggregate) {
+                       if (!angular.isDefined(aggregate))
+                               aggregate = true;
                        var responses = {};
                        var gotNodesResult = function (nodeName, dotentity, 
response) {
                                responses[nodeName] = response;
                                if (Object.keys(responses).length == 
nodeNames.length) {
-                                       aggregateNodeInfo(nodeNames, entity, 
responses, callback);
+                                       if (aggregate)
+                                               
self.aggregateNodeInfo(nodeNames, entity, selectedNodeId, responses, callback);
+                                       else {
+                                               callback(nodeNames, entity, 
responses)
+                                       }
                                }
                        }
 
-                       var aggregateNodeInfo = function (nodeNames, entity, 
responses, callback) {
-                               //QDR.log.debug("got all results for  " + 
entity);
-                               // aggregate the responses
-                               var newResponse = {};
-                               var thisNode = responses[selectedNodeId];
-                               newResponse['attributeNames'] = 
thisNode.attributeNames;
-                               newResponse['results'] = thisNode.results;
-                               newResponse['aggregates'] = [];
-                               for (var i=0; i<thisNode.results.length; ++i) {
-                                       var result = thisNode.results[i];
-                                       var vals = [];
-                                       result.forEach( function (val) {
-                                               vals.push({sum: val, detail: 
[]})
-                                       })
-                                       newResponse.aggregates.push(vals);
-                               }
-                               var nameIndex = 
thisNode.attributeNames.indexOf("name");
-                               var ent = self.schema.entityTypes[entity];
-                               var ids = Object.keys(responses);
-                               ids.sort();
-                               ids.forEach( function (id) {
-                                       var response = responses[id];
-                                       var results = response.results;
-                                       results.forEach( function (result) {
-                                               // find the matching result in 
the aggregates
-                                               var found = 
newResponse.aggregates.some( function (aggregate, j) {
-                                                       if 
(aggregate[nameIndex].sum === result[nameIndex]) {
-                                                               // result and 
aggregate are now the same record, add the graphable values
-                                                               
newResponse.attributeNames.forEach( function (key, i) {
-                                                                       if 
(ent.attributes[key] && ent.attributes[key].graph) {
-                                                                               
if (id != selectedNodeId)
-                                                                               
        aggregate[i].sum += result[i];
-                                                                               
aggregate[i].detail.push({node: self.nameFromId(id)+':', val: result[i]})
-                                                                       }
-                                                               })
-                                                               return true; // 
stop looping
-                                                       }
-                                                       return false; // 
continute looking for the aggregate record
-                                               })
-                                               if (!found) {
-                                                       // this attribute was 
not found in the aggregates yet
-                                                       // because it was not 
in the selectedNodeId's results
-                                                       var vals = [];
-                                                       result.forEach( 
function (val) {
-                                                               vals.push({sum: 
val, detail: []})
-                                                       })
-                                                       
newResponse.aggregates.push(vals)
-                                               }
-                                       })
-                               })
-                               callback(nodeNames, entity, newResponse);
-                       }
-
                        nodeNames.forEach( function (id) {
                    self.getNodeInfo(id, '.'+entity, attrs, gotNodesResult);
                })
                        //TODO: implement a timeout in case not all requests 
complete
                },
 
+               aggregateNodeInfo: function (nodeNames, entity, selectedNodeId, 
responses, callback) {
+                       //QDR.log.debug("got all results for  " + entity);
+                       // aggregate the responses
+                       var newResponse = {};
+                       var thisNode = responses[selectedNodeId];
+                       newResponse['attributeNames'] = thisNode.attributeNames;
+                       newResponse['results'] = thisNode.results;
+                       newResponse['aggregates'] = [];
+                       for (var i=0; i<thisNode.results.length; ++i) {
+                               var result = thisNode.results[i];
+                               var vals = [];
+                               result.forEach( function (val) {
+                                       vals.push({sum: val, detail: []})
+                               })
+                               newResponse.aggregates.push(vals);
+                       }
+                       var nameIndex = thisNode.attributeNames.indexOf("name");
+                       var ent = self.schema.entityTypes[entity];
+                       var ids = Object.keys(responses);
+                       ids.sort();
+                       ids.forEach( function (id) {
+                               var response = responses[id];
+                               var results = response.results;
+                               results.forEach( function (result) {
+                                       // find the matching result in the 
aggregates
+                                       var found = 
newResponse.aggregates.some( function (aggregate, j) {
+                                               if (aggregate[nameIndex].sum 
=== result[nameIndex]) {
+                                                       // result and aggregate 
are now the same record, add the graphable values
+                                                       
newResponse.attributeNames.forEach( function (key, i) {
+                                                               if 
(ent.attributes[key] && ent.attributes[key].graph) {
+                                                                       if (id 
!= selectedNodeId)
+                                                                               
aggregate[i].sum += result[i];
+                                                                       
aggregate[i].detail.push({node: self.nameFromId(id)+':', val: result[i]})
+                                                               }
+                                                       })
+                                                       return true; // stop 
looping
+                                               }
+                                               return false; // continute 
looking for the aggregate record
+                                       })
+                                       if (!found) {
+                                               // this attribute was not found 
in the aggregates yet
+                                               // because it was not in the 
selectedNodeId's results
+                                               var vals = [];
+                                               result.forEach( function (val) {
+                                                       vals.push({sum: val, 
detail: []})
+                                               })
+                                               
newResponse.aggregates.push(vals)
+                                       }
+                               })
+                       })
+                       callback(nodeNames, entity, newResponse);
+               },
+
+
       getSchema: function () {
         //QDR.log.debug("getting schema");
         var ret;
@@ -564,13 +579,74 @@ The response looks like:
         }, ret.error);
       },
 
-    sendQuery: function(toAddr, entity, attrs) {
+      getNodeInfo: function (nodeName, entity, attrs, callback) {
+        //QDR.log.debug("getNodeInfo called with nodeName: " + nodeName + " 
and entity " + entity);
+        var ret;
+        self.correlator.request(
+            ret = self.sendQuery(nodeName, entity, attrs)
+        ).then(ret.id, function(response) {
+            callback(nodeName, entity, response);
+            //self.topology.addNodeInfo(nodeName, entity, response);
+            //self.topology.cleanUp(response);
+        }, ret.error);
+      },
+
+       sendMethod: function (nodeId, entity, attrs, operation, callback) {
+               var ret;
+               self.correlator.request(
+                       ret = self._sendMethod(nodeId, entity, attrs, operation)
+               ).then(ret.id, function (response, context) {
+                               callback(nodeId, entity, response, context);
+               }, ret.error);
+       },
+
+       _fullAddr: function (toAddr) {
         var toAddrParts = toAddr.split('/');
         if (toAddrParts.shift() != "amqp:") {
             self.topology.error(Error("unexpected format for router address: " 
+ toAddr));
             return;
         }
-        var fullAddr =  self.toAddress + "/" + toAddrParts.join('/');
+        //var fullAddr =  self.toAddress + "/" + toAddrParts.join('/');
+        var fullAddr =  toAddrParts.join('/');
+               return fullAddr;
+       },
+
+       _sendMethod: function (toAddr, entity, attrs, operation) {
+               var fullAddr = self._fullAddr(toAddr);
+               var ret = {id: self.correlator.corr()};
+               if (!self.sender || !self.sendable) {
+                       ret.error = "no sender"
+                       return ret;
+               }
+               try {
+                       var application_properties = {
+                               operation:  operation
+                       }
+                       if (attrs.type)
+                               application_properties.type = attrs.type;
+                       if (attrs.name)
+                               application_properties.name = attrs.name;
+               self.sender.send({
+                       body: attrs,
+                       properties: {
+                           to:                     fullAddr,
+                        reply_to:               
self.receiver.remote.attach.source.address,
+                           correlation_id:         ret.id
+                       },
+                       application_properties: application_properties
+            })
+               }
+               catch (e) {
+                       error = "error sending: " + e;
+                       QDR.log.error(error)
+                       ret.error = error;
+               }
+               return ret;
+       },
+
+    sendQuery: function(toAddr, entity, attrs, operation) {
+        operation = operation || "QUERY"
+               var fullAddr = self._fullAddr(toAddr);
 
                var body;
         if (attrs)
@@ -581,13 +657,14 @@ The response looks like:
             body = {
                 "attributeNames": [],
             }
-
-               return self._send(body, fullAddr, "QUERY", 
"org.apache.qpid.dispatch" + entity);
+               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);
     },
 
     sendMgmtQuery: function (operation) {
-               // TODO: file bug against dispatch - We should be able to just 
pass body: [], but that generates an 'invalid body'
-               return self._send([' '], self.toAddress + "/$management", 
operation);
+               return self._send([], "/$management", operation);
     },
 
        _send: function (body, to, operation, entityType) {
@@ -649,7 +726,6 @@ The response looks like:
                                self.receiver = null;
                                self.sendable = false;
                        }
-
                        var maybeStart = function () {
                                if (okay.connection && okay.sender && 
okay.receiver && self.sendable && !self.connected) {
                                        QDR.log.info("okay to start")
@@ -662,7 +738,7 @@ The response looks like:
                                }
                        }
                        var onDisconnect = function () {
-                               QDR.log.warn("Disconnected");
+                               //QDR.log.warn("Disconnected");
                                stop();
                                self.executeDisconnectActions();
                        }
@@ -690,7 +766,7 @@ The response looks like:
                                self.errorText = "Disconnected"
                        })
 
-                       var sender = connection.open_sender("/$management");
+                       var sender = connection.open_sender();
                        sender.on('sender_open', function (context) {
                                QDR.log.debug("sender_opened")
                                okay.sender = true
@@ -724,7 +800,7 @@ The response looks like:
 (function() {
     console.dump = function(object) {
         if (window.JSON && window.JSON.stringify)
-            console.log(JSON.stringify(object));
+            console.log(JSON.stringify(object,undefined,2));
         else
             console.log(object);
     };

http://git-wip-us.apache.org/repos/asf/qpid-dispatch/blob/96ce4a6c/console/hawtio/src/main/webapp/plugin/js/qdrTopology.js
----------------------------------------------------------------------
diff --git a/console/hawtio/src/main/webapp/plugin/js/qdrTopology.js 
b/console/hawtio/src/main/webapp/plugin/js/qdrTopology.js
index ff5b696..a59c8d3 100644
--- a/console/hawtio/src/main/webapp/plugin/js/qdrTopology.js
+++ b/console/hawtio/src/main/webapp/plugin/js/qdrTopology.js
@@ -21,6 +21,44 @@ under the License.
  */
 var QDR = (function (QDR) {
 
+
+       QDR.module.controller('QDR.TopologyFormController', function ($scope, 
QDRService) {
+
+               $scope.attributes = []
+        var generalCellTemplate = '<div class="ngCellText"><span 
title="{{row.entity.description}}">{{row.entity.attributeName}}</span></div>';
+        $scope.topoGridOptions = {
+            data: 'attributes',
+                       enableColumnResize: true,
+                       multiSelect: false,
+            columnDefs: [
+            {
+                field: 'attributeName',
+                displayName: 'Attribute',
+                cellTemplate: generalCellTemplate
+            },
+            {
+                field: 'attributeValue',
+                displayName: 'Value'
+            }
+            ]
+        };
+               $scope.form = ''
+               $scope.$on('showEntityForm', function (event, args) {
+                       var attributes = args.attributes;
+                       var entityTypes = 
QDRService.schema.entityTypes[args.entity].attributes;
+                       Object.keys(attributes).forEach( function (attr) {
+                               if (entityTypes[attr])
+                                       attributes[attr].description = 
entityTypes[attr]
+                       })
+                       $scope.attributes = attributes;
+                       $scope.form = args.entity;
+               })
+               $scope.$on('showAddForm', function (event) {
+                       $scope.form = 'add';
+               })
+       })
+
+
   /**
    * @method TopologyController
    *
@@ -38,37 +76,11 @@ var QDR = (function (QDR) {
                QDR.log.debug("started QDR.TopologyController with urlPrefix: " 
+ $location.absUrl());
                var urlPrefix = $location.absUrl();
 
-               $scope.attributes = [];
-        $scope.connAttributes = [];
-        $scope.topoForm = "general";
-        $scope.topoFormSelected = "";
                $scope.addingNode = {
                        step: 0,
                        hasLink: false,
                        trigger: ''
-               }; // shared object about the node that is be       
$scope.topoForm = "general";
-
-        var generalCellTemplate = '<div class="ngCellText"><span 
title="{{row.entity.description}}">{{row.entity.attributeName}}</span></div>';
-
-               $scope.isGeneral = function () {
-           //QDR.log.debug("$scope.topoForm=" + $scope.topoForm)
-           return $scope.topoForm === 'general';
-               };
-               $scope.isConnections = function () {
-           //QDR.log.debug("$scope.topoForm=" + $scope.topoForm)
-           return $scope.topoForm === 'connections';
                };
-               $scope.isAddNode = function () {
-           //QDR.log.debug("$scope.topoForm=" + $scope.topoForm)
-                       return $scope.topoForm === 'addNode';
-               }
-
-               $scope.getTableHeight = function (rows) {
-               return {height: (rows.length * 30) + "px"};
-               }
-        $scope.isSelected = function () {
-            return ($scope.topoFormSelected != "");
-        }
 
         $scope.cancel = function () {
             $scope.addingNode.step = 0;
@@ -77,24 +89,6 @@ var QDR = (function (QDR) {
                        $scope.addingNode.trigger = 'editNode';
                }
 
-        $scope.topoGridOptions = {
-            data: 'attributes',
-                       enableColumnResize: true,
-                       multiSelect: false,
-            columnDefs: [
-            {
-                field: 'attributeName',
-                displayName: 'Attribute',
-                cellTemplate: generalCellTemplate
-            },
-            {
-                field: 'attributeValue',
-                displayName: 'Value'
-            }
-            ]
-        };
-        $scope.topoConnOptions = angular.copy($scope.topoGridOptions);
-        $scope.topoConnOptions.data = 'connAttributes';
                var NewRouterName = "__NEW__";
            // mouse event vars
            var selected_node = null,
@@ -122,16 +116,12 @@ var QDR = (function (QDR) {
                        if (name == "Add Router") {
                                name = 'Diagram';
                                if ($scope.addingNode.step > 0) {
-                                       $scope.topoForm = 'general'
-                                       $scope.topoFormSelected = '';
                                        $scope.addingNode.step = 0;
                                } else {
                                        // start adding node mode
                                        $scope.addingNode.step = 1;
                                }
                        } else {
-                               $scope.topoForm = 'general'
-                               $scope.topoFormSelected = '';
                                $scope.addingNode.step = 0;
                        }
 
@@ -160,12 +150,11 @@ var QDR = (function (QDR) {
                                        }
                                        return true;
                                })
-                               $scope.topoForm = 'general'
-                               $scope.topoFormSelected = '';
+                               
updateForm(Object.keys(QDRService.topology.nodeInfo())[0], 'router', 0);
+
                        } else if (newValue > 0) {
                                // we are starting the add mode
-                               $scope.topoForm = 'addNode';
-                $scope.topoFormSelected = 'addNode';
+                               $scope.$broadcast('showAddForm')
 
                                resetMouseVars();
                 selected_node = null;
@@ -190,6 +179,29 @@ var QDR = (function (QDR) {
                        return mode.right;
                }
 
+               // for ng-grid that shows details for multiple consoles/clients
+               $scope.multiData = [{name: ''}, {name: ''}, {name: ''}]
+        $scope.multiDetails = {
+            data: 'multiData',
+            columnDefs: [
+            {
+                field: 'host',
+                displayName: 'Host'
+            },
+            {
+                field: 'user',
+                displayName: 'User'
+            },
+                       {
+                               field: 'properties',
+                               displayName: 'Properties'
+                       },
+                       {
+                               field: 'isEncrypted',
+                               displayName: 'Encrypted'
+                       }
+            ]
+        };
 
                // generate unique name for router and containerName
                var genNewName = function () {
@@ -283,9 +295,9 @@ var QDR = (function (QDR) {
            var radius = 25;
            var radiusNormal = 15;
            width = tpdiv.width() - gap;
-           height = $(document).height() - gap;
+           height = $('#main').height() - $('#topology').position().top - gap;
 
-           var svg;
+           var svg, lsvg;
                var force;
                var animate = false; // should the force graph organize itself 
when it is displayed
                var path, circle;
@@ -299,7 +311,8 @@ var QDR = (function (QDR) {
                var nodes = [];
                var links = [];
 
-               var aNode = function (id, name, nodeType, nodeInfo, nodeIndex, 
x, y, resultIndex, fixed) {
+               var aNode = function (id, name, nodeType, nodeInfo, nodeIndex, 
x, y, resultIndex, fixed, properties) {
+                       properties = properties || {};
                        var containerName;
                        if (nodeInfo) {
                                var node = nodeInfo[id];
@@ -310,6 +323,7 @@ var QDR = (function (QDR) {
                        return {   key: id,
                                name: name,
                                nodeType: nodeType,
+                               properties: properties,
                                containerName: containerName,
                                x: x,
                                y: y,
@@ -340,8 +354,6 @@ var QDR = (function (QDR) {
             }
         }
 
-               //var drag;
-               // create an bare svg element and
                // initialize the nodes and links array from the 
QDRService.topology._nodeInfo object
                var initForceGraph = function () {
             //QDR.log.debug("initForceGraph called");
@@ -370,6 +382,20 @@ var QDR = (function (QDR) {
                     removeCrosssection()
                 });
 
+                $(document).keyup(function(e) {
+                  if (e.keyCode === 27) {
+                    removeCrosssection()
+                  }
+                });
+
+                       // the legend
+                       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
                        selected_node = null;
                        selected_link = null;
@@ -388,11 +414,11 @@ var QDR = (function (QDR) {
                                if (!angular.isDefined(position)) {
                                    animate = true;
                                    position = {x: width / 4 + ((width / 
2)/nodeCount) * nodes.length,
-                                               y: height / 2 + yInit,
+                                               y: 200 + yInit,
                                                fixed: false};
                                }
                                if (position.y > height)
-                                       position.y = height / 2 - yInit;
+                                       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);
@@ -405,9 +431,13 @@ var QDR = (function (QDR) {
                                var onode = nodeInfo[id];
                                var conns = onode['.connection'].results;
                                var attrs = onode['.connection'].attributeNames;
+                               var parent = 
getNodeIndex(QDRService.nameFromId(id));
+                               //QDR.log.debug("external client parent is " + 
parent);
+                               var normalsParent = {console: undefined, 
client: undefined}; // 1st normal node for this parent
 
                                for (var j = 0; j < conns.length; j++) {
                     var role = QDRService.valFor(attrs, conns[j], "role");
+                    var properties = QDRService.valFor(attrs, conns[j], 
"properties") || {};
                     var dir = QDRService.valFor(attrs, conns[j], "dir");
                                        if (role == "inter-router") {
                                                var connId = 
QDRService.valFor(attrs, conns[j], "container");
@@ -419,8 +449,6 @@ var QDR = (function (QDR) {
                                                //QDR.log.debug("found an 
external client for " + id);
                                                var name = 
QDRService.nameFromId(id) + "." + client;
                                                //QDR.log.debug("external 
client name is  " + name + " and the role is " + role);
-                                               var parent = 
getNodeIndex(QDRService.nameFromId(id));
-                                               //QDR.log.debug("external 
client parent is " + parent);
 
                         // if we have any new clients, animate the force graph 
to position them
                         var position = angular.fromJson(localStorage[name]);
@@ -432,26 +460,36 @@ var QDR = (function (QDR) {
                         }
                                                if (position.y > height)
                                                        position.y = 
nodes[parent].y + 40 + Math.cos(Math.PI/2 * client)
-                                               //QDR.log.debug("adding node " 
+ nodeIndex);
-                                               nodes.push(     aNode(id, name, 
role, nodeInfo, nodes.length, position.x, position.y, j, position.fixed) );
-                                               // now add a link
-                                               getLink(parent, nodes.length-1, 
dir);
-                                               client++;
+                                               var node = aNode(id, name, 
role, nodeInfo, nodes.length, position.x, position.y, j, position.fixed, 
properties)
+                                               var nodeType = role === 
'normal' ? (properties.console_identifier == 'Dispatch console' ? 'console' : 
'client') : 'broker';
+                                               if (role === 'normal') {
+                                                       node.user = 
QDRService.valFor(attrs, conns[j], "user")
+                                                       node.isEncrypted = 
QDRService.valFor(attrs, conns[j], "isEncrypted")
+                                                       node.host = 
QDRService.valFor(attrs, conns[j], "host")
+
+                                                       if 
(!normalsParent[nodeType]) {
+                                                               
normalsParent[nodeType] = node;
+                                                               nodes.push(     
node );
+                                                               node.normals = 
[node];
+                                                               // now add a 
link
+                                                               getLink(parent, 
nodes.length-1, dir);
+                                                               client++;
+                                                       } else {
+
+                                                               
normalsParent[nodeType].normals.push(node)
+                                                       }
+                                               } else {
+                                                       nodes.push( node)
+                                                       // now add a link
+                                                       getLink(parent, 
nodes.length-1, dir);
+                                                       client++;
+                                               }
                                        }
                                }
                                source++;
                        }
 
             $scope.schema = QDRService.schema;
-                       // add a row for each attribute in .router 
attributeNames array
-                       for (var id in nodeInfo) {
-                               var onode = nodeInfo[id];
-
-                initForm(onode['.connection'].attributeNames, 
onode['.connection'].results[0], QDRService.schema.entityTypes.connection, 
$scope.connAttributes);
-                initForm(onode['.router'].attributeNames, 
onode['.router'].results[0], QDRService.schema.entityTypes.router, 
$scope.attributes);
-                
-                               break;
-                       }
                        // init D3 force layout
                        force = d3.layout.force()
                                .nodes(nodes)
@@ -506,69 +544,54 @@ var QDR = (function (QDR) {
                        // app starts here
                        restart(false);
            force.start();
-               }
-/*
-               function dragstart(d) {
-                 d3.select(this).classed("fixed", d.fixed = true);
-               }
+                       setTimeout(function () {
+                   updateForm(Object.keys(QDRService.topology.nodeInfo())[0], 
'router', 0);
+                       }, 10)
 
-               function dblclick(d) {
-                 d3.select(this).classed("fixed", d.fixed = false);
                }
-*/
-        // called when we mouseover a node
-        // we need to update the table
-               function updateNodeForm (d) {
-                       //QDR.log.debug("update form info for ");
-                       //console.dump(d);
+
+               function updateForm (key, entity, resultIndex) {
                        var nodeInfo = QDRService.topology.nodeInfo();
-                       var onode = nodeInfo[d.key];
+                       var onode = nodeInfo[key]
                        if (onode) {
-                               var nodeResults = onode['.router'].results[0];
-                               var nodeAttributes = 
onode['.router'].attributeNames;
-
-                for (var i=0; i<$scope.attributes.length; ++i) {
-                    var idx = 
nodeAttributes.indexOf($scope.attributes[i].attributeName);
-                    if (idx > -1) {
-                        if ($scope.attributes[i].attributeValue != 
nodeResults[idx]) {
-                            // highlight the changed data
-                            $scope.attributes[i].attributeValue = 
nodeResults[idx];
-
-                        }
-                    }
-                }
-                       }
-            $scope.topoForm = "general";
-            $scope.$apply();
-               }
+                               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) })
 
-               function updateConnForm (d, resultIndex) {
-                       var nodeInfo = QDRService.topology.nodeInfo();
-                       var onode = nodeInfo[d.key];
-                       if (onode && onode['.connection']) {
-                               var nodeResults = 
onode['.connection'].results[resultIndex];
-                               var nodeAttributes = 
onode['.connection'].attributeNames;
-
-                for (var i=0; i<$scope.connAttributes.length; ++i) {
-                    var idx = 
nodeAttributes.indexOf($scope.connAttributes[i].attributeName);
-                    if (idx > -1) {
-                       try {
-                        if ($scope.connAttributes[i].attributeValue != 
nodeResults[idx]) {
-                            // highlight the changed data
-                            $scope.connAttributes[i].attributeValue = 
nodeResults[idx];
+                               // 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';
+                                       })
+                                       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});
+                               }
 
-                        }
-                        } catch (err) {
-                                                       QDR.log.error("error 
updating form" + err)
-                        }
-                    }
-                }
+                               $scope.$broadcast('showEntityForm', {entity: 
entity, attributes: attributes})
                        }
-            $scope.topoForm = "connections";
-            $scope.$apply();
+                       $scope.$apply();
                }
 
-
         function getContainerIndex(_id) {
             var nodeIndex = 0;
             var nodeInfo = QDRService.topology.nodeInfo();
@@ -738,6 +761,12 @@ var QDR = (function (QDR) {
                     d3.select("#crosssection svg").remove();
                     d3.select("#crosssection").style("display","none");
                 });
+            d3.select("#multiple_details").transition()
+                .duration(500)
+                .style("opacity", 0)
+                .each("end", function (d) {
+                    d3.select("#multiple_details").style("display", "none")
+                })
                }
 
            // takes the nodes and links array of objects and adds svg elements 
for everything that hasn't already
@@ -793,7 +822,7 @@ var QDR = (function (QDR) {
                             var conn = 
onode['.connection'].results[resultIndex];
                             /// find the connection whose container is the 
right's name
                             var name = 
QDRService.valFor(onode['.connection'].attributeNames, conn, "container");
-                            if (name == right.name) {
+                            if (name == right.containerName) {
                                 break;
                             }
                         }
@@ -803,7 +832,7 @@ var QDR = (function (QDR) {
                             left = d.target;
                             resultIndex = left.resultIndex;
                         }
-                        updateConnForm(left, resultIndex);
+                        updateForm(left.key, 'connection', resultIndex);
                     }
 
                                        // select link
@@ -944,33 +973,30 @@ var QDR = (function (QDR) {
                circle.selectAll('circle')
                    .classed('selected', function (d) { return (d === 
selected_node) })
                    .classed('fixed', function (d) { return (d.fixed & 0b1) })
+                           //.classed('multiple', function(d) { return 
(d.normals && d.normals.length > 1)  } )
 
                        // add new circle nodes. if nodes[] is longer than the 
existing paths, add a new path for each new element
-               var g = circle.enter().append('svg:g');
-
-                       // add new circles and set their attr/class/behavior
-               g.append('svg:circle')
-                   .attr('class', 'node')
-                   .attr('r', function (d) {
-                       return radii[d.nodeType];
-                   })
-                   .classed('fixed', function (d) {return d.fixed})
-                           .classed('temp', function(d) { return 
QDRService.nameFromId(d.key) == '__internal__'; } )
-                           .classed('normal', function(d) { return d.nodeType 
== 'normal' } )
-                           .classed('inter-router', function(d) { return 
d.nodeType == 'inter-router' } )
-                           .classed('on-demand', function(d) { return 
d.nodeType == 'on-demand' } )
-
-/*
-                   .style('fill', function (d) {
-                       var sColor = colors[d.nodeType];
-                       return (d === selected_node) ? 
d3.rgb(sColor).brighter().toString() : d3.rgb(sColor);
-                   })
-                   .style('stroke', function (d) {
-                       var sColor = colors[d.nodeType];
-                       return d3.rgb(sColor).darker().toString();
-                   })
-*/
-                   .on('mouseover', function (d) {
+               var g = circle.enter().append('svg:g')
+                           .classed('multiple', function(d) { return 
(d.normals && d.normals.length > 1)  } )
+
+                       var appendCircle = function (g) {
+                               // add new circles and set their 
attr/class/behavior
+                       return g.append('svg:circle')
+                           .attr('class', 'node')
+                           .attr('r', function (d) {
+                               return radii[d.nodeType];
+                           })
+                           .classed('fixed', function (d) {return d.fixed})
+                       .classed('temp', function(d) { return 
QDRService.nameFromId(d.key) == '__internal__'; } )
+                       .classed('normal', function(d) { return d.nodeType == 
'normal' } )
+                       .classed('inter-router', function(d) { return 
d.nodeType == 'inter-router' } )
+                       .classed('on-demand', function(d) { return d.nodeType 
== 'on-demand' } )
+                       .classed('console', function(d) { return 
d.properties.console_identifier == 'Dispatch console' } )
+                       .classed('artemis', function(d) { return 
QDRService.isArtemis(d) } )
+                       .classed('qpid-cpp', function(d) { return 
QDRService.isQpid(d) } )
+                       .classed('client', function(d) { return d.nodeType === 
'normal' && !d.properties.console_identifier } )
+                       }
+                       appendCircle(g).on('mouseover', function (d) {
                        if ($scope.addingNode.step > 0) {
                                d3.select(this).attr('transform', 'scale(1.1)');
                                                return;
@@ -978,10 +1004,10 @@ var QDR = (function (QDR) {
                                        if (!selected_node) {
                         if (d.nodeType === 'inter-router') {
                             //QDR.log.debug("showing general form");
-                            updateNodeForm(d);
+                            updateForm(d.key, 'router', 0);
                         } else if (d.nodeType === 'normal' || d.nodeType === 
'on-demand') {
                             //QDR.log.debug("showing connections form");
-                            updateConnForm(d, d.resultIndex);
+                            updateForm(d.key, 'connection', d.resultIndex);
                         }
                                        }
 
@@ -1057,19 +1083,11 @@ var QDR = (function (QDR) {
                                        // if this node was selected, unselect 
it
                     if (mousedown_node === selected_node) {
                         selected_node = null;
-                        $scope.topoFormSelected = "";
                     }
                     else {
-                        selected_node = mousedown_node;
-                        if (d.nodeType === 'inter-router') {
-                            //QDR.log.debug("showing general form");
-                            updateNodeForm(d);
-                            $scope.topoFormSelected = "general";
-                        } else if (d.nodeType === 'normal' || d.nodeType === 
'on-demand') {
-                            //QDR.log.debug("showing connections form");
-                            updateConnForm(d, d.resultIndex);
-                            $scope.topoFormSelected = "connections";
-                        }
+                                               // don't select nodes that 
represent multiple clients/consoles
+                        if (!d.normals || d.normals.length < 2)
+                            selected_node = mousedown_node;
                     }
                     for (var i=0; i<links.length; ++i) {
                         links[i]['highlighted'] = false;
@@ -1099,21 +1117,136 @@ var QDR = (function (QDR) {
                       .style('top', (mouseY + $(document).scrollTop()) + "px")
                       .style('display', 'block');
 
-                });
+                })
+                .on("click", function (d) {
+                                       if (!d.normals || d.normals.length < 2) 
{
+                                   if ( QDRService.isArtemis(d) && 
Core.ConnectionName === 'Artemis' ) {
+                                                       
$location.path('/jmx/attributes?tab=artemis&con=Artemis')
+                                               }
+                                               return;
+                                       }
+                    clickPos = d3.mouse(this);
+                    d3.event.stopPropagation();
+                    $scope.multiData = []
+                    d.normals.forEach( function (n) {
+                        $scope.multiData.push(n)
+                    })
+                    $scope.$apply();
+                    d3.select('#multiple_details')
+                        .style({
+                            display: 'block',
+                            opacity: 1,
+                            height: (d.normals.length + 1) * 30 + "px",
+                            'overflow-y': d.normals.length > 10 ? 'scroll' : 
'hidden',
+                                   left: (mouseX + $(document).scrollLeft()) + 
"px",
+                            top:  (mouseY + $(document).scrollTop()) + "px"})
+                               })
 
-               // show node IDs
-               g.append('svg:text')
-                   .attr('x', 0)
-                   .attr('y', 4)
-                   .attr('class', 'id')
-                   .text(function (d) {
-                       return (d.nodeType === 'normal' || d.nodeType == 
'on-demand') ? d.name.slice(-1) :
-                           d.name.length>7 ? d.name.substr(0,6)+'...' : d.name;
-               });
+                       var appendContent = function (g) {
+                       // show node IDs
+                       g.append('svg:text')
+                           .attr('x', 0)
+                           .attr('y', function (d) {
+                               var y = 6;
+                               if (QDRService.isArtemis(d))
+                                   y = 8;
+                               else if (QDRService.isQpid(d))
+                                   y = 9;
+                               else if (d.nodeType === 'inter-router')
+                                   y = 4;
+                               return y;})
+                           .attr('class', 'id')
+                       .classed('console', function(d) { return 
d.properties.console_identifier == 'Dispatch console' } )
+                       .classed('normal', function(d) { return d.nodeType === 
'normal' } )
+                       .classed('on-demand', function(d) { return d.nodeType 
=== 'on-demand' } )
+                       .classed('artemis', function(d) { return 
QDRService.isArtemis(d) } )
+                       .classed('qpid-cpp', function(d) { return 
QDRService.isQpid(d) } )
+                           .text(function (d) {
+                               if (d.properties.console_identifier == 
'Dispatch console') {
+                                   return '\uf108'; // icon-desktop for this 
console
+                               }
+                                               if (QDRService.isArtemis(d)) {
+                                                       return '\ue900'
+                                               }
+                               if (QDRService.isQpid(d)) {
+                                   return '\ue901';
+                               }
+                                               if (d.nodeType === 'normal')
+                                                       return '\uf109'; // 
icon-laptop for clients
+                               return d.name.length>7 ? 
d.name.substr(0,6)+'...' : d.name;
+                       });
+                       }
+                       appendContent(g)
+
+                       var appendTitle = function (g) {
+                       g.append("svg:title").text(function (d) {
+                       var x = '';
+                       if (d.normals && d.normals.length > 1)
+                           x = " x " + d.normals.length;
+                           if (d.properties.console_identifier == 'Dispatch 
console') {
+                           return 'Dispatch console' + x
+                       }
+                           if (d.properties.product == 'qpid-cpp') {
+                           return 'Broker - qpid-cpp' + x
+                       }
+                           if ( QDRService.isArtemis(d) ) {
+                           return 'Broker - Artemis' + x
+                       }
+                           return d.nodeType == 'normal' ? 'client' + x : 
(d.nodeType == 'on-demand' ? 'broker' : 'Router ' + d.name)
+                       })
+                       }
+                       appendTitle(g);
 
                // remove old nodes
                circle.exit().remove();
 
+                       // add subcircles
+                       svg.selectAll('.subcircle').remove();
+
+                       svg.selectAll('.multiple')
+                               .insert('svg:circle', '.normal')
+                                       .attr('class', 'subcircle')
+                                       .attr('r', 18)
+
+
+                       // dynamically create the legend based on which node 
types are present
+                       var legendNodes = [];
+                       legendNodes.push(aNode("Router", "", "inter-router", 
undefined, 0, 0, 0, 0, false, {}))
+
+                       if (!svg.selectAll('circle.console').empty()) {
+                               legendNodes.push(aNode("Dispatch console", "", 
"normal", undefined, 1, 0, 0, 0, false, {console_identifier: 'Dispatch 
console'}))
+                       }
+                       if (!svg.selectAll('circle.client').empty()) {
+                               legendNodes.push(aNode("Client", "", "normal", 
undefined, 2, 0, 0, 0, false, {}))
+                       }
+                       if (!svg.selectAll('circle.qpid-cpp').empty()) {
+                               legendNodes.push(aNode("Qpid cpp broker", "", 
"on-demand", undefined, 3, 0, 0, 0, false, {product: 'qpid-cpp'}))
+                       }
+                       if (!svg.selectAll('circle.artemis').empty()) {
+                               legendNodes.push(aNode("Artemis broker", "", 
"on-demand", undefined, 4, 0, 0, 0, false, {}))
+                       }
+                   lsvg = lsvg.data(legendNodes, function (d) {
+                   return d.id;
+            });
+               var lg = lsvg.enter().append('svg:g')
+                               .attr('transform', function (d, i) {
+                                       // 45px between lines and add 10px 
space after 1st line
+                                       return "translate(0, 
"+(45*i+(i>0?10:0))+")"
+                               })
+                       appendCircle(lg)
+                       appendContent(lg)
+                       appendTitle(lg)
+                       lg.append('svg:text')
+                               .attr('x', 35)
+                               .attr('y', 6)
+                               .attr('class', "label")
+                               .text(function (d) {return d.key })
+                       lsvg.exit().remove();
+                       var svgEl = document.getElementById("svglegend"),
+                               bb = svgEl.getBBox();
+                       svgEl.style.height = bb.y + bb.height;
+                       svgEl.style.width = bb.x + bb.width;
+
                if (!mousedown_node || !selected_node)
                    return;
 
@@ -1161,6 +1294,7 @@ var QDR = (function (QDR) {
                 saveChanged();
                 // TODO: update graph nodes instead of rebuilding entire graph
                 d3.select("#SVG_ID").remove();
+                d3.select("#svg_legend svg").remove();
                 animate = true;
                 initForceGraph();
                 //if ($location.path().startsWith("/topology"))


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

Reply via email to