In this tutorial we explain how to write your own dashboard gadget for WSO2 Business Activity Monitor. This will be covering the important aspects of creating a dashboard gadget. For you to understand this tutorial you will need some prior knowledge in Google Gadget API. We use some existing resources (JavaScript) in our example gadget, hence we assume that you have a working build of WSO2 Business Activity Monitor, before proceed.
We will write an example gadget which shows the number of operations and the list of operations of a given service. The gadget user must be able to select the desired server from the monitored server list and then a specific service to get the operations. As we move forward, we will discuss some important steps of creating the example gadget. You can find the complete source pre of the gadget XML at the end of this article.
Let's have a look at the first few lines of our gadget XML.
<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="Operations Count for Service" description="Latest Operations Count For Service" author="Your Name" author_email="you@yourdomain.org" author_location="Colombo, Sri Lanka"> <Require feature="setprefs"/> <Require feature="tabs"/> </ModulePrefs>
The first line declares that the document contains XML data. The <Module> tag, which is declared in the gadget specification is used to indicate that it is a Gadget XML. <ModulePrefs> contains meta data such as gadget title, description and author information. We use <Require> tag to declare some feature dependencies of the gadget which in this case are the user preferences and tabs.
<UserPref name="serverID" display_name="Server to monitor" default_value="" datatype="string"></UserPref>
<UserPref name="serviceID" display_name="Service to monitor" default_value="" datatype="string"></UserPref>
Two user preferences, serverID and serviceID, are used to store the gadget configurations. This allows us to load the gadget data automatically when the gadget is reloaded.
<Content type="html"> <![CDATA[ .......... ]]> <Content>
We define the content type as HTML and all the logic (JavaScript) and the content will be defined inside a CDATA.
<script type="text/javascript" src="js/retrieve-data.js"></script>
"retrieve-data.js" is the main JavaScript that we use to retrieve data from the server. It is bundled with WSO2 Business Activity Monitor. This file contains functions to retrieve server and mediation data from the backend.
In this example we add two drop downs as we need to allow the users to select a server from the monitored server list and then a service to get the operations. If you have a closer look at the "retrieve-data.js" you will find the predefined methods to carry out this task. Function "getServerList()" gives you a list of currently monitored servers in the following format.
"server1_id, server1_name | server2_id, server2_name | server3_id, server3_name"
Similarly, function "getServiceList()" gives you a list of available services for a selected server, formatted as follows.
"service1_id, service1_name | service2_id, service2_name | service3_id, service3_name"
These functions allow you to fetch raw data which can be then presented in a proper way within the gadget. However, in this tutorial we use the function "loadServerListWithServices()" to populate two drop downs for us. This function has to be declared as the "registerOnLoadHandler" of the gadget and also mentioned at the latter part of the tutorial.
<div id="server_select_box"> <select id="serverIDs" onchange="loadServices();"> </select> </div>
In order to create a drop down with all the monitored servers, we should define a <select> tag inside a div which id is defined as the "server_select_box".
<div id="service_select_box"> <select id="services" onchange="refreshData();"> </select> </div>
A similar step can be taken to create a drop down to select the services of a given server, except the div's id should be defined as "service_select_box".
The gadget's logic can be defined using JavaScript inside a <script> tag. In our example, first we have to load all the user preferences of the gadget.
// Loading Gadget configuration var prefs = new _IG_Prefs(__MODULE_ID__); var serverID = prefs.getString("serverID"); var serverURL = prefs.getString("serverURL"); var serviceID = prefs.getString("serviceID"); var serviceName = prefs.getString("serviceName");
Our "Operations Count for Service" gadget contains two tabs. "Display" tab is to display the list of operations and the configuration tab is to display the drop downs that we mentioned earlier. Gadget specification defined a feature called "tabs" that we included in our gadget using the <Require> tag inside the <ModulePrefs>. Following pre will convert the two div tags with ids "main_disp" and "disp_config", into two tabs.
// Adding Tabs var tabs = new gadgets.TabSet(__MODULE_ID__, "Display"); tabs.addTab("Display", { contentContainer: document.getElementById("main_disp"), callback: drawDiagram }); tabs.addTab("Configure", "disp_config");
As mentioned earlier, "retrieve-data.js" contains several important methods that are needed to retrieve data from the server. This includes server data and as well as mediation data. For example, you can use "getOperationsOfService(serverID, serviceID)" method to retrieve the operations for a given service. These methods return XML data in clear text and you will have to parse the results using a simple JavaScript parser.
Second option is to use the Gadget API to retrieve the necessary data from a component called "flashdata-ajaxprocessor.jsp". This component returns data in XML format. In our example we use this option.
var dataURL = getAdminConsoleUrl() + "/carbon/gauges/gadgets/flash/flashdata-ajaxprocessor.jsp?funcName=getOperationsOfService&serverID=" + serverID + "&serviceID=" + serviceID;
When using the Gadget API to retrieve remote content an absolute (full) URL must be provided. Therefore, we use the method "getAdminConsoleUrl()" from "retrieve-data.js" to get the host address of our client instance. You should pass the desired function name as the parameter "funcName" and here we are using "getOperationsOfService".
function makeDOMRequest(dataURL) { var params = {}; params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.DOM; gadgets.io.makeRequest(dataURL, response, params); }
Pass the generated URL into "gadgets.io.makeRequest(...)". It will generate a DOM object using the XML data fetched from "flashdata-ajaxprocessor.jsp". This DOM object can be used to access all the data nodes with an ease.
Finally, the gadget initialization is carried out by setting the Javascript function: "loadServerListWithServices" as the registerOnLoadHandler.
// Registering onload handler gadgets.util.registerOnLoadHandler(loadServerListWithServices);
You can host your gadget in a public location where gadget XML is accessible via HTTP. Click on the "Add Gadget" link and provide the HTTP URL of the gadget XML.
The process of creating a dashboard gadget for WSO2 Business Activity Monitor is pretty much similar to the process of creating any other dashboard gadget. All the necessary functions to retrieve server and mediation data are exposed through the "retrieve-data.js".
This tutorial only covers important aspects of creating a BAM dashboard gadget. For a complete guide on gadgets please refer this article.
<?xml version="1.0" encoding="UTF-8" ?> <Module> <ModulePrefs title="Operations Count for Service" height="240" description="Latest Operations Count For Service" author="Your Name" author_email="carbon-dev@wso2.org" author_location="Colombo, Sri Lanka"> <Require feature="setprefs"/> <Require feature="tabs"/> </ModulePrefs> <UserPref name="serverID" display_name="Server to monitor" default_value="" datatype="string"></UserPref> <UserPref name="serviceID" display_name="Service to monitor" default_value="" datatype="string"></UserPref> <Content type="html"> <![CDATA[ <script type="text/javascript" src="js/retrieve-data.js"></script> <div id="disp_config" style="display:none;height:300px;"> <table class="main-table"> <tr> <td><div id="select_boxes"> <table> <tr> <td><div style="text-align: right; font-size:12px; font-family: "Lucida Grande","Lucida Sans","Microsoft Sans Serif", "Lucida Sans Unipre","Verdana","Sans-serif","trebuchet ms" !important;"> Server :</div></td> <td><div id="server_select_box"> <select id="serverIDs" onchange="loadServices();"> </select> </div></td> </tr> <tr> <td><div style="text-align: right;font-size:12px; font-family: "Lucida Grande","Lucida Sans","Microsoft Sans Serif", "Lucida Sans Unipre","Verdana","Sans-serif","trebuchet ms" !important;">Service :</div></td> <td><div id="service_select_box"> <select id="services" onchange="refreshData();"> </select> </div></td> </tr> </table> </div></td> </tr> </table> </div> <div id="main_disp" style="display:none;height:300px;"> <div id="disp_title" class="titleText"></div> <div id="content" >Text</div> </div> <script type="text/javascript"> // Loading Gadget configuration var prefs = new _IG_Prefs(__MODULE_ID__); var serverID = prefs.getString("serverID"); var serverURL = prefs.getString("serverURL"); var serviceID = prefs.getString("serviceID"); var serviceName = prefs.getString("serviceName"); // Adding Tabs var tabs = new gadgets.TabSet(__MODULE_ID__, "Display"); tabs.addTab("Display", { contentContainer: ocument.getElementById("main_disp"), callback: drawDiagram }); tabs.addTab("Configure", "disp_config"); function drawDiagram() { serverID = prefs.getString("serverID"); serverURL = prefs.getString("serverURL"); serviceID = prefs.getString("serviceID"); serviceName = prefs.getString("serviceName"); // Clear existing content var contentEl = document.getElementById("content"); contentEl.innerHTML = ""; var titleEl=document.getElementById("disp_title"); titleEl.innerHTML = ""; // Check whether we have a server configured if(serverID == "") { contentEl.innerHTML = "Please use the 'Configure' tab to select a Server to monitor."; } else { // Set display title titleEl.innerHTML = "Server : <strong>" + serverURL + "</strong> Service : <strong>" + serviceName + "</strong>"; // Initialize display var dataURL = getAdminConsoleUrl() + "/carbon/gauges/gadgets/flash/flashdata-ajaxprocessor.jsp?funcName=getOperationsOfService&serverID=" + serverID + "&serviceID=" + serviceID; makeDOMRequest(dataURL); } } function makeDOMRequest(dataURL) { var params = {}; params[gadgets.io.RequestParameters.CONTENT_TYPE] = gadgets.io.ContentType.DOM; gadgets.io.makeRequest(dataURL, response, params); } function response(dataObj) { var domData = dataObj.data; var operationsList = domData.getElementsByTagName("level2"); var contentEl = document.getElementById("content"); var contentHtml = "<div>Number of Operations: <strong>" + operationsList.length + "</strong> </div>"; // Loop through all <level2> nodes and find the names of the Operations for (var i = 0; i < operationsList.length ; i++) { var opName = operationsList.item(i).attributes.item(0).nodeValue; contentHtml += "<div>" + opName + "</div>"; } contentEl.innerHTML = contentHtml; } // Registering onload handler gadgets.util.registerOnLoadHandler(loadServerListWithServices); </script> ]]> </Content> </Module>