Visar inlägg med etikett tablesorter. Visa alla inlägg
Visar inlägg med etikett tablesorter. Visa alla inlägg

torsdag 4 oktober 2012

JMX over HTTP with Jolokia and javascript

Using a combination of jolokia and jquery (with jstree) it is quite easy to create a jconsole like M-Bean handler application in javascript.

The basic idea is to search all M-Beans using jolokia and to create a navigation tree using jstree. When selecting a leaf node the corresponding M-Bean attributes and operations are presented.

Implementation

The AgenServlet from jolokia was added to a servletcontainer (tomcat) in web.xml

<servlet>
      <servlet-name>jolokia</servlet-name>
      <servlet-class>org.jolokia.http.AgentServlet</servlet-class>
      <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
      <servlet-name> jolokia </servlet-name>
      <url-pattern>/jolokia/*</url-pattern>
</servlet-mapping>

By using the javascipt API for jolokia to search for M-Beans it is possible to present a jconsole like tree structure using jstree.

NOTE: the keyorder:"constructionTime" is a patch to jolokia making it possible to get the M-Bean keys in construction order instead of cannonical. This feature makes the tree look like jconsole but is currently not available in jolokia (just omit it).

Disclaimer: I'm obviously not a javascript developer, sorry for the coding style :)

<div id="nav" class="jstree"></div>
<script type="text/javascript">
function extractDomainMap(mbeans) {
   var domains = {};
   for (var i = 0; i < mbeans.length; i++) {
      var mbean = mbeans[i];
      var domainEnd = mbean.indexOf(":");
      var domainName = mbean.substring(0, domainEnd);
      var domain = domains[domainName];
      if (domain == null) {
         domain = {"name": domainName, "entries": {}};
         domains[domainName] = domain;
      }
      var container = domain.entries;
      var tokens = mbean.substring(domainEnd + 1).split(",");
      for (var ii = 0; ii < tokens.length; ii++) {
         var token = tokens[ii];
         var typeEnd = token.indexOf("=");
         var name = token.substring(typeEnd + 1);
         var item = container[name];
         if (item == null) {
            item = {"name": name, "entries": []};
            container[name] = item;
         }
         if (ii == (tokens.length - 1)) {
            item.mbean = mbean;
         }
         container = item.entries;
      }
   }
   return domains;
}

function createJsTreeDataRecursive(entries, children) {
   var i = 0;
   for (var itemName in entries) {
      if (entries.hasOwnProperty(itemName)) {
         var item = entries[itemName];
         var node = {"data": item.name, "children": []};
         if (item.mbean != null) {
            node.metadata = {"mbean": item.mbean};
         }
         children[i] = node;
         createJsTreeDataRecursive(item.entries, node.children);
         i++;
      }
   }
}

function createJsTreeData(domains) {
   var data = {"data" : []};
   var i = 0;
   for (var domainName in domains) {
      if (domains.hasOwnProperty(domainName)) {
         var domain = domains[domainName];
         var node = {"data": domain.name, "children": []};
         data.data[i] = node;
         createJsTreeDataRecursive(domain.entries, node.children);
         i++;
      }
   }
   return data;
}

function searchAll() {
   var response = new Jolokia({"url": "jolokia"}).request(
     {type: "search", mbean:"*:*", keyorder:"constructionTime"}, 
     {method: "post"});
   response.value.sort();
   return createJsTreeData(extractDomainMap(response.value));
}

var data = searchAll();
$("#nav")
   .jstree ({
      "json_data" : data,
      "themes" : {
         "theme" : "classic",
         "dots" : false
      },
      "core" : {
         "animation" : 50
      },
         "plugins" : ["themes", "classic", "json_data","ui"]
   })
   .bind("select_node.jstree", function (event, data) {
      var mbean = data.rslt.obj.data("mbean");
      if (mbean != null) {
         listMbean(mbean);
      }
   });
</script>

The listMBean function is invoked when an leaf node is selected. It is quite easy to create a jquery tablesorter of all the attributes, operations and notifications using for example the cool javascript template framework handlebars. Here is the code that creates a table of the M-Bean attributes

<div id="attributes"></div>
<script id="attributes-template" type="text/x-handlebars-template">
<table id="attributes-table" class="tablesorter">
   <thead>
      <th>Name</th>
      <th>Value</th>
   </thead>
   <tbody>
   {{#attributes}}
      <tr>
         <td>{{name}}</td>
         <td id='{{name}}'>
         {{#if value.rw}}
            <input class="attr-input" id='attr-input-{{name}}' name='{{name}}' type='text' value=''>
            <input class="attr-submit" id='attr-submit-{{name}}' name='Save' type='submit' value='Save' onclick="updateAttribute('{{name}}')">
         {{/if}}
         </td>
      </tr>
   {{/attributes}}
   </tbody>
</table>
</script>

<script type="text/javascript">
var currentMbean;
function listMbean(mbean) {
   var path = mbean.replace(new RegExp("/", 'g'), "!/");
   path = path.replace(":", "/");
   var meta = new Jolokia({"url": "jolokia"}).request(
     {"type": "list", "path": path}, 
     {method: "post"});
   currentMbean = mbean;
   currentMeta = meta;
   $("#mbeanInfo").html("<h2 id='mbeanName'>" + mbean + "</h2><p>" + meta.value.desc + "</p>");
   var attributes = { 'attributes' : [] };
   for (var attr in meta.value.attr) {
      if (meta.value.attr.hasOwnProperty(attr)) {
         attributes['attributes'].push({
            'name' : attr,
            'value' : meta.value.attr[attr]
         });
      }
   }
   var source = $("#attributes-template").html();
   var template = Handlebars.compile(source);
   $("#attributes").html(template(attributes));
   $("#attributes-table").tablesorter({widgets: ['zebra']});
   // inject the values
   var values = new Jolokia({"url": "jolokia"}).request(
     {"type": "read", "mbean": mbean}, 
     {method: "post"});
   for (var attr in values.value) {
      if (values.value.hasOwnProperty(attr)) {
         var value = values.value[attr];
         if (meta.value.attr[attr].rw) {
            $("#attr-input-" + attr).val(value);
         } else {
            $("#" + attr).html(value);
         }
      }
   }
}


function updateAttribute(name) {
   var mbean = currentMbean;
   var value = $("#attr-input-" + name).val();
    new Jolokia({"url": "jolokia"}).request(
     {"type": "write", "mbean": mbean, "attribute": name, "value": value}, 
     {method: "post"});
   listMbean(mbean);
}
</script>

Similar tables can be created for the M-Bean operations and notifications.