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.
