olokia-client-javascript.1.2.1.source-code.jolokia-cubism.js Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jolokia-client-javascript
Show all versions of jolokia-client-javascript
Jolokia :: Client :: JavaScript
/*
* Copyright 2012 Roland Huss
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Jolokia integration into cubism (http://square.github.com/cubism/)
*
* This integration requires the following
*/
(function () {
var builder = function (cubism,Jolokia) {
var VERSION = "1.2.1";
var ctx_jolokia = function (url, opts) {
var source = {},
context = this,
j4p = createAgent(url, opts),
step = 5e3; // 5 seconds by default
try
{
// Connect to start and stop events
context.on("start",function() {
j4p.start();
});
context.on("stop",function() {
j4p.stop();
});
}
catch(err)
{
// Still waiting for pull request https://github.com/square/cubism/pull/19 for supporting
// "start" and "stop" events
}
/**
* Factory method for create a metric objects which has various variants.
*
* If the first argument is a Jolokia request object (i.e. not a function), this request
* is used for sending requests periodically.
*
* If the first argument is a function, this function is used for calculating the numeric value
* to be plotted. The rest of the arguments can be one or more request objects, which are registered and their
* responses are put as arguments to the given callback function.
*
* The last argument, if an object but not a Jolokia request (i.e. there is no type
key), is
* taken as an option object with the following possible keys:
*
*
* - name: name used in charts
* - delta: delta value in milliseconds for creating delta (velocity) charts. This is done by
* taking the value measured that many milliseconds ago and subtract them from each other.
* - keepDelay: how many seconds back the fetched values should be kept.
*
*
* Finally, if the last argument is a pure string, then this string is used as name for the chart.
*
* @return the metric objects which can be used in Cubism for creating charts.
*/
source.metric = function () {
var values = [];
// If the first argument is a function, this callback function is used for calculating the
// value of a data point. The remaining arguments should be one or more Jolokia requests objects
// and the callback function given will be called with as many response objects after each server query.
// The callback function needs to return a single calculated numerical value
var name;
var argsLen = arguments.length;
var options = {};
// Create metric upfront so that it can be used in extraction functions. The name defaults to the mbean name
// but can be given as first argument
var lastIdx = arguments.length - 1;
var lastArg = arguments[lastIdx];
if (typeof lastArg == "string") {
name = lastArg;
argsLen = lastIdx;
}
// Options can be given as an object (but not a request with a 'type' property)
if (typeof lastArg == "object" && !lastArg.type) {
options = lastArg;
name = options.name;
argsLen = lastIdx;
}
if (!name && typeof arguments[0] != "function") {
name = arguments[0].mbean;
}
// Metric which maps our previously locally stored values to the ones requested by cubism
var metric = context.metric(mapValuesFunc(values, options.keepDelay, context.width), name);
if (options.delta) {
// Use cubism metric chaining for calculating the difference value and keep care that the
// metric keeps old values up to the delta value
var prevMetric = metric.shift(-options.delta);
metric = metric.subtract(prevMetric);
if (name) {
metric.toString = function () {
return name
};
}
}
// If an extraction function is given, this can be used for fine grained manipulations of
// the answer
if (typeof arguments[0] == "function") {
var func = arguments[0];
var respFunc = function (resp) {
var isError = false;
for (var i = 0; i < arguments.length; i++) {
if (j4p.isError(arguments[i])) {
isError = true;
break;
}
}
values.unshift(
{ time:Date.now(), value:isError ? NaN : func.apply(metric, arguments) }
);
};
var args = [ respFunc ];
for (var i = 1; i < argsLen; i++) {
args.push(arguments[i]);
}
j4p.register.apply(j4p, args);
} else {
// Register the argument given directly as a Jolokia request. The request must return a single
// numerical value
var request = arguments[0];
j4p.register(function (resp) {
values.unshift({
time: Date.now(),
value:j4p.isError(resp) ? NaN : Number(resp.value)
});
}, request);
}
return metric;
};
// Start up fetching of values in the background
source.start = function (newStep) {
newStep = newStep || step;
j4p.start(newStep);
};
// Stop fetching of values in the background
source.stop = function() { j4p.stop() };
// Check whether the scheduler is running
source.isRunning = function() { return j4p.isRunning() };
// Startup poller which will call the agent periodically
return source;
// =======================================================================================
// Private helper method
// Create a new Jolokia agent or reuse a given one
function createAgent(url, opts) {
if (url instanceof Jolokia) {
return url;
} else {
var args;
if (typeof url == "string") {
args = {url:url};
if (opts) {
for (var key in opts) {
if (opts.hasOwnProperty(key)) {
args[key] = opts[key];
}
}
}
} else {
args = url;
}
return new Jolokia(args);
}
}
// Generate function which picks the requested values from the values
// stored periodically by the Jolokia poller.
function mapValuesFunc(values, keepDelay, width) {
return function (cStart, cStop, cStep, callback) {
cStart = +cStart;
cStop = +cStop;
var retVals = [],
cTime = cStop,
vLen = values.length,
vIdx = 0,
vStart = vLen > 0 ? values[vLen - 1].time : undefined;
if (!vLen || cStop < vStart) {
// Nothing fetched yet or seeked interval doesn't overlap with stored values --> return only NaNs
for (var t = cStart; t <= cStop; t += cStep) {
retVals.push(NaN);
}
return callback(null, retVals);
}
// Fill up wit NaN until we reach the first stored val
while (cTime > values[0].time + cStep) {
retVals.unshift(NaN);
cTime -= cStep;
}
while (cTime >= cStart && cTime >= vStart) {
// Count down stored values until we find the next best 'fit'
// (equals or closest before the step-calculated ime)
while (values[vIdx].time > cTime) {
vIdx++;
}
retVals.unshift(values[vIdx].value);
cTime -= cStep;
}
// Finally prepend with 'NaN' for data not yet fetched
while (cTime >= cStart) {
retVals.unshift(NaN);
cTime -= cStep;
}
// Remove older values
if (vLen > width) {
if (!keepDelay) {
values.length = width;
} else {
var keepUntil = values[width].time - keepDelay,
i = width;
while (i < vLen && values[i].time > keepUntil) {
i++;
}
values.length = i;
}
}
callback(null, retVals);
}
}
};
ctx_jolokia.VERSION = VERSION;
cubism.context.prototype.jolokia = ctx_jolokia;
return ctx_jolokia;
};
// =====================================================================================================
// Register either at the global Jolokia object global or as an AMD module
(function (root) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as a named module
define(["cubism","jolokia"],function (cubism,Jolokia) {
return builder(cubism,Jolokia);
});
} else {
if (root.Jolokia && root.cubism) {
builder(root.cubism,root.Jolokia);
} else {
console.error("No " + (root.cubism ? "Cubism" : "Jolokia") + " definition found. " +
"Please include jolokia.js and cubism.js before jolokia-cubism.js");
}
}
})(this);
})();