Commit b0b82b2c by joshiste Committed by Johannes Stelzer

Added JMX-Section for interaction with MBeans.

Reading, writing attribute and operation execution supported.
parent 5bb5fd09
......@@ -22,9 +22,11 @@
<jquery.version>1.11.0</jquery.version>
<angularjs.version>1.2.12</angularjs.version>
<angular-ui-router.version>0.2.10-1</angular-ui-router.version>
<angular-ui-bootstrap.version>0.8.0</angular-ui-bootstrap.version>
<angularjs-nvd3-directives.version>0.0.7-1</angularjs-nvd3-directives.version>
<d3js.version>3.4.11</d3js.version>
<nvd3.version>1.1.15-beta</nvd3.version>
<jolokia-js.version>1.2.2</jolokia-js.version>
<main.basedir>${basedir}</main.basedir>
<passphrase>${gpg.passphrase}</passphrase>
</properties>
......@@ -208,6 +210,11 @@
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>angular-ui-bootstrap</artifactId>
<version>${angular-ui-bootstrap.version}</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>angularjs-nvd3-directives</artifactId>
<version>${angularjs-nvd3-directives.version}</version>
</dependency>
......@@ -221,6 +228,11 @@
<artifactId>nvd3</artifactId>
<version>${nvd3.version}</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jolokia.js</artifactId>
<version>${jolokia-js.version}</version>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
......
......@@ -53,6 +53,14 @@
<groupId>org.webjars</groupId>
<artifactId>nvd3</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jolokia.js</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>angular-ui-bootstrap</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>junit</groupId>
......
......@@ -61,9 +61,12 @@
<script src="/webjars/angularjs/${angularjs.version}/angular-resource.min.js"></script>
<script src="/webjars/angularjs/${angularjs.version}/angular-route.min.js"></script>
<script src="/webjars/angular-ui-router/0.2.10/angular-ui-router.min.js"></script>
<script src="/webjars/angular-ui-bootstrap/${angular-ui-bootstrap.version}/ui-bootstrap.min.js"></script>
<script src="/webjars/angular-ui-bootstrap/${angular-ui-bootstrap.version}/ui-bootstrap-tpls.min.js"></script>
<script src="/webjars/d3js/${d3js.version}/d3.min.js"></script>
<script src="/webjars/nvd3/${nvd3.version}/nv.d3.min.js"></script>
<script src="/webjars/angularjs-nvd3-directives/0.0.7/angularjs-nvd3-directives.js"></script>
<script src="/webjars/jolokia.js/${jolokia-js.version}/jolokia.js"></script>
<script src="/scripts/app.js"></script>
<script src="/scripts/controllers/controllers.js"></script>
<script src="/scripts/services/services.js"></script>
......
......@@ -19,6 +19,7 @@ angular.module('springBootAdmin', [
'ngResource',
'ngRoute',
'ui.router',
'ui.bootstrap',
'springBootAdmin.services',
'nvd3ChartDirectives'
])
......@@ -87,6 +88,11 @@ angular.module('springBootAdmin', [
url: '/write',
templateUrl: 'views/apps/logging/write.html',
controller: 'loggingWriteCtrl'
})
.state('apps.jmx', {
url: '/jmx/:id',
templateUrl: 'views/apps/jmx.html',
controller: 'jmxCtrl'
});
})
.run(function ($rootScope, $state, $stateParams, $log) {
......
......@@ -36,6 +36,10 @@ angular.module('springBootAdmin')
$scope.showLogging = function(id) {
$location.path('/apps/logging/' + id + '/read');
};
// callback for ng-click 'showJmx':
$scope.showJmx = function(id) {
$location.path('/apps/jmx/' + id);
};
// callback for ng-click 'refresh':
$scope.refresh = function(id) {
$scope.application = Application.query({id: id}, function(application) {
......@@ -202,4 +206,99 @@ angular.module('springBootAdmin')
$scope.writeLoglevel = function(application) {
ApplicationLogging.setLoglevel(application);
};
})
.controller('jmxCtrl', function ($scope, $stateParams, $modal, Application, ApplicationJMX) {
$scope.application = Application.query({id: $stateParams.id}, function(application) {
$scope.error = null;
$scope.domains = [];
ApplicationJMX.list(application).then(function(domains){
$scope.domains = domains;
}).catch(function(response) {
$scope.error = response.error;
console.log(response.stacktrace);
})
});
$scope.readAttr = function(bean) {
bean.error = null;
ApplicationJMX.readAttr($scope.application, bean).then(
function(response) {
for (var name in response.value) {
bean.attributes[name].error = null;
bean.attributes[name].value = response.value[name];
bean.attributes[name].jsonValue = JSON.stringify(response.value[name], null, " ");
}
}).catch(function(response) {
bean.error = response.error;
console.log(response.stacktrace);
});
}
$scope.writeAttr = function(bean, name, attr) {
attr.error = null;
ApplicationJMX.writeAttr($scope.application, bean, name, attr.value).catch(
function(response) {
attr.error = response.error;
console.log(response.stacktrace);
});
}
$scope.invoke = function() {
$scope.invocation.state = 'executing';
ApplicationJMX.invoke($scope.application, $scope.invocation.bean, $scope.invocation.opname, $scope.invocation.args).then(
function(response) {
$scope.invocation.state = 'success';
$scope.invocation.result = response.value;
}).catch(function(response){
$scope.invocation.state = 'error';
$scope.invocation.error = response.error;
$scope.invocation.stacktrace = response.stacktrace;
});
$modal.open({
templateUrl: 'invocationResultDialog.html',
scope: $scope
}).result.then(function() {
$scope.invocation = null;
});
}
$scope.prepareInvoke = function(bean, name, op) {
$scope.invocation = { bean: bean, opname: name, opdesc: op, args: [], state: 'prepare' };
if (op instanceof Array) {
$modal.open({
templateUrl: 'invocationVariantDialog.html',
scope: $scope
}).result.then(function(chosenOp) {
$scope.prepareInvoke(bean, name, chosenOp);
}).catch(function() {
$scope.invocation = null;
});
} else {
if (op.args.length === 0) {
$scope.invoke();
} else {
var signature = "(";
for (var i in op.args) {
if (i > 0) signature += ',';
signature += op.args[i].type;
$scope.invocation.args[i] = null;
}
signature += ")";
$scope.invocation.opname = name + signature;
$modal.open({
templateUrl: 'invocationPrepareDialog.html',
scope: $scope
}).result.then(function() {
$scope.invoke();
}).catch(function() {
$scope.invocation = null;
});
}
}
}
});
......@@ -130,6 +130,78 @@ angular.module('springBootAdmin.services', ['ngResource'])
});
}
}])
.service('ApplicationJMX', ['$rootScope', 'Abbreviator', 'Jolokia', function($rootScope, Abbreviator, jolokia) {
this.list = function(app) {
return jolokia.list(app.url + '/jolokia/').then(function(response) {
var domains = [];
for (var rDomainName in response.value) {
var rDomain = response.value[rDomainName];
var domain = {name : rDomainName, beans: [] };
for (var rBeanName in rDomain ) {
var rBean = rDomain[rBeanName];
var bean = { id : domain.name + ':' + rBeanName,
name : '',
nameProps: {},
description : rBean.desc,
operations : rBean.op,
attributes : rBean.attr
};
var name = '';
var type = '';
var parts = rBeanName.split(',');
for (var i in parts ) {
var tokens = parts[i].split('=');
if (tokens[0].toLowerCase() === 'name') {
name = tokens[1];
} else{
bean.nameProps[tokens[0]] = tokens[1];
if ((tokens[0].toLowerCase() === 'type' || tokens[0].toLowerCase() == 'j2eetype') && type.length ==0 ) {
type = tokens[1];
}
}
}
if (name.length !== 0) {
bean.name = name;
}
if ( type.length !== 0) {
if (bean.name !== 0) {
bean.name += ' ';
}
bean.name += '[' + Abbreviator.abbreviate(type, '.', 25, 1, 1) + ']';
}
if (bean.name.length === 0) {
bean.name = rBeanName;
}
domain.beans.push(bean);
}
domains.push(domain);
}
return domains;
}, function(response) {
return response;
});
}
this.readAttr = function(app, bean) {
return jolokia.read(app.url + '/jolokia', bean.id)
}
this.writeAttr = function(app, bean, attr, val) {
return jolokia.write(app.url + '/jolokia', bean.id, attr, val);
}
this.invoke = function(app, bean, opname, args) {
return jolokia.exec(app.url + '/jolokia', bean.id, opname, args);
}
}])
.service('Abbreviator', [function() {
function _computeDotIndexes(fqName, delimiter, preserveLast) {
var dotArray = [];
......@@ -198,4 +270,41 @@ angular.module('springBootAdmin.services', ['ngResource'])
return result;
}
}])
.service('Jolokia', [ '$q' , '$rootScope', function($q){
var j4p = new Jolokia();
function call(url, request) {
var deferred = $q.defer();
deferred.notify(request);
j4p.request( request,
{ url: url,
method: 'post',
success: function (response) {
deferred.resolve(response);
},
error: function (response) {
deferred.reject(response);
}
});
return deferred.promise;
}
this.exec = function(url, mbean, op, args) {
return call(url, { type: 'exec', mbean: mbean, operation: op, arguments: args });
}
this.read = function(url, mbean) {
return call(url, { type: 'read', mbean: mbean });
}
this.write = function(url, mbean, attr, val) {
return call(url, { type: 'write', mbean: mbean, attribute: attr, value: val });
}
this.list = function(url) {
return call(url, { type: 'list' });
}
}]);
......@@ -6,11 +6,17 @@
@import "application.css";
@import "highlight.css";
.accordion-heading ,
.table > thead > tr > th {
background-color: #34302D; /* lighten 3% */
color: #f1f1f1;
}
.tabs-left > ul {
max-width: 30%;
overflow: hidden;
}
.table tr.highlight > td {
background-color: #a5b2b9 !important;
font-weight: bold;
......
<div class="row-fluid">
<div class="span12">
<h1 class="index-page--title">{{ application.id }}</h1>
<p class="index-page--subtitle">JMX Beans</p>
</div>
</div>
<div class="alert alert-error" ng-if="error">
<b>Error:</b> {{ error }}
</div>
<div class="container">
<accordion close-others="true">
<accordion-group ng-repeat="domain in domains track by domain.name">
<accordion-heading>
<small class="muted">Domain</small> {{domain.name}}
</accordion-heading>
<tabset class="tabs-left" ng-init="visible = []">
<tab ng-repeat="bean in domain.beans track by bean.id" select="visible[bean.id] = true" deselect="visible[bean.id] = false">
<tab-heading>
<small class="muted">MBean</small> {{bean.name }}
</tab-heading>
<div ng-if="visible[bean.id]">
<h2>
{{bean.name}}<br/><small>{{bean.description}}</small>
</h2>
<dl>
<dt>Id</dt>
<dd style="word-break: break-all;">{{ bean.id }}</dd>
<dt ng-repeat-start="(name, value) in bean.nameProps">{{ name }}</dt>
<dd ng-repeat-end>{{ value }}</dd>
</dl>
<form class="form-horizontal" ng-if="bean.attributes !== undefinded">
<legend>
Attributes
<button class="btn" type="button" ng-click="readAttr(bean)">read</button>
</legend>
<div ng-show="bean.error" class="alert alert-error">
<b>Error:</b> {{ bean.error }}
</div>
<div class="control-group" ng-repeat="(name, attr) in bean.attributes track by name" ng-class="{error: attr.error}">
<label class="control-label" for="{{name}}" style="word-break: break-all;">
{{name}}<br/>
<small class="muted">{{attr.type}}</small>
</label>
<div class="controls">
<div class="input-prepend" ng-switch="attr.type.toLowerCase()">
<button class="btn" type="button" ng-click="writeAttr(bean, name, attr)" ng-disabled="!attr.rw">write</button>
<input ng-switch-when="java.lang.string" class="span5" type="text" ng-model="attr.value" ng-disabled="!attr.rw" />
<input ng-switch-when="long" class="span5" type="number" ng-model="attr.value" ng-disabled="!attr.rw" />
<input ng-switch-when="int" class="span5" type="number" ng-model="attr.value" ng-disabled="!attr.rw" />
<input ng-switch-when="double" class="span5" type="number" ng-model="attr.value" ng-disabled="!attr.rw" />
<select ng-switch-when="boolean" class="span5" ng-model="attr.value" ng-disabled="!attr.rw" ng-options="val for val in [true, false]" />
<textarea ng-switch-default style="word-break: break-all;" class="span5" ng-model="attr.jsonValue" ng-disabled="!attr.rw" />
</div>
<span class="help-block">{{attr.desc}}</span>
<span class="help-inline">{{attr.error}}</span>
</div>
</div>
</form>
<form class="form" ng-if="bean.operations !== undefinded">
<legend>Operations</legend>
<div class="control-group" ng-repeat="(name, op) in bean.operations track by name">
<button class="btn span6" style="text-align: left; padding-left: 5px;" ng-click="prepareInvoke(bean, name, op)">
{{name}}<br/>
<small class="muted" style="word-break: break-all;">{{op.ret}}</small>
<span class="help-block">{{op.desc}}</span>
</button>
</div>
</form>
</div>
</tab>
</tabset>
</accordion-group>
</accordion>
</div>
<script type="text/ng-template" id="invocationPrepareDialog.html">
<div class="modal-header">
<h3>Arguments for {{ invocation.opname }}</h3>
</div>
<div class="modal-body">
<p>Please input the arguments</p>
<form class="form">
<div class="control-group" ng-repeat="arg in invocation.opdesc.args">
<label class="control-label" for="{{arg.name}}" style="word-break: break-all;">
{{arg.name}} <small class="muted" style="word-break: break-all;">{{arg.type}}</small>
</label>
<div class="controls" ng-switch="arg.type.toLowerCase()" >
<input ng-switch-when="java.lang.string" class="span6" type="text" ng-model="invocation.args[$index]"/>
<input ng-switch-when="long" class="span6" type="number" ng-model="invocation.args[$index]" />
<input ng-switch-when="int" class="span6" type="number" ng-model="invocation.args[$index]" />
<input ng-switch-when="double" class="span6" type="number" ng-model="invocation.args[$index]" />
<select ng-switch-when="boolean" class="span6" ng-model="invocation.args[$index]" ng-options="val for val in [true, false]" />
<textarea ng-switch-default style="word-break: break-all;" class="span6" ng-model="invocation.args[$index]" />
<span class="help-block">{{arg.desc}}</span>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn" ng-click="$dismiss()" >Abort</button>
<button class="btn btn-inverse" ng-click="$close()">Execute</button>
</div>
</script>
<script type="text/ng-template" id="invocationVariantDialog.html">
<div class="modal-header">
<h3>Variant for {{ invocation.opname }}</h3>
</div>
<div class="modal-body">
<p>The method is overloaded. Please choose a variant.</p>
<form>
<div class="control-group" >
<button class="btn btn-block" style="text-align: left; padding-left: 5px;" ng-repeat="op in invocation.opdesc" ng-click="$close(op)">
<b>{{invocation.opname}}</b> (
<span ng-repeat-start="arg in op.args" data-toggle="tooltip" title="{{arg.desc}}">{{arg.type}} {{arg.name}}</span>
<span ng-repeat-end ng-if="!$last">, </span>
)<br/><small class="muted">{{op.ret}}</small>
<span class="help-block">{{op.desc}}</span>
</button>
</div>
</form>
</div>
<div class="modal-footer">
<button class="btn" ng-click="$dismiss()">Abort</button>
</div>
</script>
<script type="text/ng-template" id="invocationResultDialog.html">
<div class="modal-header">
<h3>Executing {{ invocation.opname }}</h3>
</div>
<div class="modal-body" ng-switch="invocation.state">
<div ng-switch-when="executing" class="progress progress-striped active">
<div class="bar">executing ... </div>
</div>
<div ng-switch-when="success" >
<div class="alert alert-success">
<b>Success</b>
</div>
<h4>Result <small class="muted"> {{invocation.opdesc.ret}}</small></h4>
<pre>{{ invocation.result | json }}</pre>
</div>
<div ng-switch-when="error">
<div class="alert alert-error">
<b>Error:</b> {{ invocation.error }}
</div>
<h4>Stacktrace:</h4>
<pre>{{ invocation.stacktrace }}</pre>
</div>
</div>
<div class="modal-footer">
<button class="btn" ng-click="$close()">Close</button>
</div>
</script>
......@@ -32,6 +32,7 @@
<a ng-show="application.providesLogfile" target="_self" ng-href="{{application.urlLogfile}}"><button class="btn btn-success">Logfile</button></a>
<button ng-click="showDetails(application.id)" class="btn btn-success">Details</button>
<button ng-click="showLogging(application.id)" class="btn btn-success">Logging</button>
<button ng-click="showJmx(application.id)" class="btn btn-success">JMX</button>
<!-- <button ng-click="refresh(application.id)" class="btn btn-success">Refresh</button> -->
</span>
</td>
......
......@@ -36,15 +36,19 @@ public class SimpleCORSFilter implements Filter {
@Value("${http.filter.cors.origin:*}")
private String origin;
@Value("${http.filter.cors.headers:'Origin, X-Requested-With, Content-Type, Accept'}")
private String headers;
/**
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse,
* javax.servlet.FilterChain)
* @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest,
* javax.servlet.ServletResponse, javax.servlet.FilterChain)
*/
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException,
ServletException {
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletResponse res = (HttpServletResponse) response;
res.setHeader("Access-Control-Allow-Origin", origin);
res.setHeader("Access-Control-Allow-Headers", headers);
chain.doFilter(request, res);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment