Code Review

The following sections walk you through the sections of code from the Alerts Notification gadget Alerts Notification gadget and the Alerts Notification gadget KPI Subscriber gadget.

Alerts Notification gadget code review

The Alerts Notification gadget is coded for a server that is secured. The user is prompted for a username and password at the time of their first REST service call. If security is not enabled, the user must set the following cookie on all XmlHttpRequest objects after xhr.open(...) and before xhr.send(...): xhr.setRequestHeader("Cookie", "com.ibm.wbimonitor.UserName="+username+";").

Google gadgets are built with a union of Javascript and XML files. These gadgets can consume business data from IBM® WebSphere® Business Monitor V7.0 through the REST services interface. The business data is emitted in JSON format and can be obtained through the Javascript XmlHttpRequest object. A basic request is in the following form:

//Create the object
var xhr = new XMLHttpRequest();
//Create the url which is to be called
var url = "";
/**
* Initialize the xhr object.
* The first parameter is the HTTP Protocol method
* The second parameter is the url to be called
* The third parameter is whether or not to perform the call asynchronously
* An optional fourth parameter is the username
* An optional fifth parameter is the user's password
**/
xhr.open("GET", url, true);
//Set the callback function to be called once the request is completed
xhr.onreadystatechange = function() {
if (xhr.readyState==4 && xhr.status==200) {
//WORK WITH xhr.responseText HERE
}
}
//Perform the actual request
xhr_.send();

The WebSphere Business Monitor REST services provide read and write capabilities for Business Alert data. With the addition of the REST services, we can now read and write business alerts, as well as read and write business alert subscriptions. These examples cover what is necessary to interact with business alert data, for the full extent of the REST services see the Business Monitor REST API reference.
To read business alerts data and output the data into the JSON format, perform a XmlHttpRequest similar to the following example:

var xhr = new XMLHttpRequest();
var url = "http://localhost:9080/rest/bpm/monitor/alerts/dashboardalerts";
xhr.open("GET", url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState==4 && xhr.status==200) {
//An arbitrary method to process the returned JSON data
processAlertData(xhr.responseText);
}
}
xhr_.send();

The business data is now in the following JSON string format:

{
   "Dashboard Alert Array": [
                               {
                                   "ID":"ABC123",
                                   "Subject":"Order received",
                                   "Acknowledged":true,
                                   "Model ID":"Model1",
                                   "Context ID":"ABC123",
                                   "Instance ID":"123456",
                                   "Creation Timestamp":"2007-02-02T15:15:10"
                                   "Creation Timestamp Localized":"Feb. 2, 2007 3:15:10PM",
                                }
                             ],
   "Record Count":50,
   "Page Size":1,
   "Page Number":1
}

Next you must parse the JSON data. The JSON library you use is platform and language dependent. In this example, the following conventions are used:
  • json.parse to convert from a JSON string to a JSON object
  • json.stringify to convert from a JSON object to a JSON string
After using json.parse(xhr.responseText), you have a JSON object that contains our business alert data. The object can be referenced using the following code:

var dataObject = json.parse(xhr.responseText);
var recordCount = dataObject["Record Count"];
var alertArray = dataObject["Dashboard Alert Array"];
for(each in alertArray){
if(!alertArray[each]["Acknowledged"]){
alert("This alert is unread!  ID:"+alertArray[each]["ID"]);
}
}

The modal dialog that the alert() function presents is not the most effective or user friendly way to display information, but it works in this example. In a real application, you would want to mark up the display or append unread alerts to a list. The business alerts data returned in the previous REST services call is a digest-form of the alerts data, the complete business alert data object is not returned. Returning only the digest-form of the data improved the performance since all of the data is not needed. To retrieve the complete business alert data object, another REST service call must be made. The following is a sample call for the entire set of data:

var xhr = new XMLHttpRequest();
//The same URL that was previously called, however we need to append the Alert instance ID
var url = "http://localhost:9080/rest/bpm/monitor/alerts/dashboardalerts/ABC123";
xhr.open("GET", url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState==4 && xhr.status==200) {
processAlertInstanceData(xhr.responseText);  
/*
* An arbitrary method to process the returned JSON data
* that now is in the form of
* {
* "ID":"ABC123",
* "Subject":"Order greater than 5000 received",
* "Acknowledged":false,
* "Model ID":"Model1",
* "Context ID":"Context1",
* "Instance ID":"ABC123",
* "Context Def":"ABC123",
* "Creation Timestamp":"2007-02-02T15:15:10",
* "Creation Timestamp Localized":"Feb. 2, 2007 3:15:10PM",
* "Body":"An order was received for 50000",
* "Event":"TheEvent",
* "CBE":"TheCBE"
* }
*/
}
}
xhr_.send();

To write the data back to the Business Monitor server, use the POST HTTP method. This is similar to the REST services call use to read the data with the exception of changing the request type, providing a method override, and providing the actual data to post. The actual data that can be modified depends on if the alert has been read or acknowledged and if the alert was deleted by a user. The data that is written to the server, so any subsequent calls to the business alert REST services receives the updates that were made with the most recent service call. Therefore, any updates made by a user in the business space dashboards are reflected in our REST service calls, and any changes made in our REST service calls are reflected in the business space dashboard. This applies to both alert instance data and alert subscriptions. The following code shows how to update the alert with and ID of ABC123.

var data = [{"ID":"ABC123","Acknowledged":true},{"ID":"DEF456","Acknowledged":false}];
var xhr = new XMLHttpRequest();
var url = "http://localhost:9080/rest/bpm/monitor/alerts/dashboardalerts";
xhr.open("POST", url, true);
//The following line tells the REST service to use the PUT method instead of the POST method.  This is
//an architectural implementation due to the limits of clients supporting GET and POST methods, but
//not necessarily PUT and DELETE methods.
xhr.setRequestHeader("X-Method-Override","PUT");

xhr.onreadystatechange = function() {
if (xhr.readyState==4 && xhr.status==200) {
//check for an error message contained in the REST response.  
//If xhr.responseText is blank, update was successful.
}
}
xhr.send(json.stringify(data));

The following code shows how to delete the alert with an ID of DEF456.

var data = [{"ID":"DEF456"}];
var xhr = new XMLHttpRequest();
var url = "http://localhost:9080/rest/bpm/monitor/alerts/dashboardalerts";
xhr.open("POST", url, true);
//The following line tells the REST service to use the DELETE method instead of the POST method.  This is
//an architectural implementation due to the limits of clients supporting GET and POST methods, but
//not necessarily PUT and DELETE methods.
xhr.setRequestHeader("X-Method-Override","DELETE");

xhr.onreadystatechange = function() {
if (xhr.readyState==4 && xhr.status==200) {
//check for an error message contained in the REST response.  
//If xhr.responseText is blank, update was successful.
}
}
xhr.send (json.stringify(data));

WebSphere Business Monitor enables all users to edit the alert subscriptions. Before WebSphere Business Monitor V6.1, alert subscriptions had to be modified in the WebSphere Application Server administrative console, which is not accessible by all users. However, the alert subscription interface implemented in WebSphere Business Monitor V6.1, V6.2 and V7.0 enables users to edit alert subscriptions in not only in WebSphere Business Monitor dashboards, but any custom user-application as well. There is a REST service call that reads alert subscriptions and one REST service call that writes alert subscriptions. The following code sample shows how to read subscriptions.

var xhr = new XMLHttpRequest();
var url = "http://localhost:9080/rest/bpm/monitor/alerts/subscriptions";
xhr.open("GET", url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState==4 && xhr.status==200) {
processAlertSubscriptions(xhr.responseText);  
/*
* An arbitrary method to process the returned JSON data
* that now is in the form of
* {
* "Alert Subscription Array":
*   [
             * {
             *        "Binding ID":"ABC123",
             *        "Name":"Test Situation Binding",
             *        "Description":"Binding for testing",
             *        "Category":"Test",
             *        "Creation Timestamp":"2007-02-02T15:15:10",
             *        "Creation Timestamp Localized":"Feb. 2, 2007 3:15:10PM",
             *        "Dashboard": true,
             *        "Cell": false,
             *        "E-mail": false,
             *        "Pager": false
             *       }
             *     ],
*  "Record Count":1,
*  "Page Size":10,
*  "Page Number":1
* }
*/
}
}
xhr.send();

To update alert subscriptions, post back an array of objects containing the binding ID and a boolean for each of the four types of alerts. Use the POST method with a PUT method override the XmlHttpRequest. The following switches the previous binding from using dashboard alerts to email alerts.


var data = [{"Binding ID":"ABC123", "Dashboard": false, "Cell": false, "E-mail": true, "Pager": false}];
var xhr = new XMLHttpRequest();
var url = "http://localhost:9080/rest/bpm/monitor/alerts/subscriptions";
xhr.open("POST", url, true);
//The following line tells the REST service to use the PUT method instead of the POST method.  This is
//an architectural implementation due to the limits of clients supporting GET and POST methods, but
//not necessarily PUT and DELETE methods.
xhr.setRequestHeader("X-Method-Override","PUT");

xhr.onreadystatechange = function() {
if (xhr.readyState==4 && xhr.status==200) {
//check for an error message contained in the REST response.  
//If xhr.responseText is blank, update was successful.
}
}
xhr.send(json.stringify(data));

The following pseudocode shows the general flow of an alerts viewing application.

Dashboard alerts:
- on open, issue a GET call to /alerts/dashboardalerts
- on a successful response, parse the JSON object and iterate over the alert list and add alerts to a UI list
- on double-click of an alert instance in the UI list, issue a GET call to /alerts/dashboardalerts/ALERT_ID to get alert details
- on double-click of an alert instance in the UI list, issue a POST/PUT call to /alerts/dashboardalerts to mark the alert as read
- on a successful response, parse the JSON object and display an alert instance details dialog
- on forward button click, gather the desired target username and issue a POST call to /alerts/dashboardalerts to forward the alerts to the desired user
- on delete button click, gather the selected alerts and issue a POST/DELETE call to /alerts/dashboardalerts to delete the selected alerts

References:

KPI Subscriber gadget code review

The KPI Subscriber gadget is coded for a server that is secured. The user is prompted for a username and password at the time of their first REST service call. If security is not enabled, all KPIs will be returned to the user, regardless of the owner. It would then be up to the client application to perform any desired user filtering.

The Alerts Notification gadget code review covered how to make calls to the WebSphere Business Monitor REST services for Alerts® data. The following examples show how to use KPI data using the same REST services. For a more detailed explanation of the Javascript capabilities, see the Alerts Notification gadget code review.

Because KPIs are defined inside of a model and version context, a list of all the models and their versions must be obtained before a KPI query can be made The following conventions are used to convert to and from JSON strings and objects.
  • json.parse is used to convert from a JSON string to a JSON object.
  • json.stringify to convert from a JSON object to a JSON string.
Data from the monitor models can be retrieved with the following sample code:

var xhr = new XMLHttpRequest();
var url = "http://localhost:9080/rest/bpm/monitor/models";
xhr.open("GET", url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState==4 && xhr.status==200) {
/**
*  Download was successful.  The data returned is in the format
* [
* {
* "Model ID":"model_id1",
* "Versions":[
* {"Display Name":"display name1","Version":"20070101000000"},
* {"Display Name":"display name2","Version":"20080101000000"},
* ]
* }
* ]
**/
}
}
xhr_.send();

How the client application displays the models and versions is application dependent, but you generally only need to display KPIs from the most recent version of the model that has been deployed. The following examples use a model id of "model_id1" and a version of "20080101000000" for all KPI queries and actions. After you have the model and version information, you can query the KPI data that returns a list of digest information for all KPIs defined in the model version. You can then make a subsequent call once you have obtained the KPI ID. The following example shows the query necessary to retrieve a list of all KPIs in a monitor model version.

var xhr_ = new XMLHttpRequest();
var url = "http://localhost:9080/rest/bpm/monitor/models/model_id1/versions/20080101000000/kpis"
xhr_.open("GET", url, true);
xhr_.onreadystatechange = function() {
if (xhr.readyState==4 && xhr.status==200) {
/**
* Download was successful.  The data returned is in the format:
* [
* {
* "KPI ID":  "kpi_id1",
* "Model ID": "model_id1",
* "Version": "20080101000000",
* "KPI Display Name": "kpi name",
* "KPI Origin": "modeled",
* "User ID": "user 1",
* "KPI Context ID": "context id1",
* "KPI Description": "sample KPI Data",
* "KPI Data Type": "duration",
* "Target": 60000,
* "Target Localized": "1 m 0 s",
* "KPI Calc Method": "aggregated",
* "Aggregated Metric ID": "metric ID",
* "Aggregated Metric Name": "metric name 1",
* "Aggregated Function": "avg",
* "Version Aggregation": "allVersions",
* "View Access": "public",
* }
* ]
**/
}
}
xhr_.send();

This service call returns all the metadata for the KPI that is required to make any further requests. This metadata contains the KPI ID for making REST service calls, the place where the KPI was created (either in the WebSphere Business Monitor Toolkit or the WebSphere Business Monitor Dashboards), the owner of the KPI (if one is assigned), a description of the KPI that makes it easier to determine what the KPI represents, the type of KPI data (whether it is representing a decimal or duration), and the desired value of the KPI is available in the Target field. This information is all that is needed to display a list of KPIs that you can select from to view the actual KPI values. To retrieve KPI data with the current KPI value, make the following REST call providing a model ID, version number, and KPI ID.

var xhr = new XMLHttpRequest();
var url = "http://localhost:9080/rest/bpm/monitor/models/model_id1/versions/20080101000000/kpis/value/kpi_id1?locale=en_US";
xhr.open("GET", url, true);
xhr.onreadystatechange = function() {
if (xhr.readyState==4 && xhr.status==200) {
/**
* Download was successful.  The data returned is in the format:
{
"KPI ID":"kpi_id1",
"Model ID":"model_id1",
"Version":20080101000000,
"Locale": "en_US",
"Model Display Name":"sample model1",
"KPI Context ID":"context id1",
"KPI Context Name":"context name1",
"KPI Display Name":"kpi name1",
"KPI Description":"sample kpi data",
"KPI Origin":"modeled", //Possible values are either: modeled or runtime
"KPI Data Type":"duration", //Possible values are either: decimal or duration
"Target":200801010000008, // if duration KPI, in milliseconds
"Target Localized":"xxxxxxx", // "xxxxxxx" is localized Target
"KPI Range Type":"actualValue", // Possible values are either: actualValue or percentage
"KPI Calc Method":"aggregated", // Possible values are either: aggregated or calculated
"Aggregated Metric ID":"metric id1",
"Aggregated Metric Name":"matric name1",
"Aggregated Metric Type":"STRING", // "STRING", "BOOLEAN", "DECIMAL", "INTEGER", "DATE", "TIME", "DATETIME", "DURATION", "COUNTER", "STOPWATCH-A", "STOPWATCH-NA"
"Aggregated Metric MC ID":"metric mc id1",
"Aggregated Metric MC Name":"metric mc name1",
"Aggregated Function":"avg", // Possible values include: avg, sum, min, max, or count
"Version Aggregation":"allVersions", // Possible values are either: singleVersion or allVersions
"Time Period Metric ID":"time period metric id1",
"Time Period Metric Name":"time period metric name1",
"Time Period Method":"rollingPeriod", //Possible values include: repeatingPeriod, rollingPeriod, or fixedPeriod
"Repeating Period Duration":"", // Possible values include: yearly, quarterly, monthly, weekly, daily, hourly, or minutely
"Repeating Period Basis":"", // Possible values include: previousPeriod or periodInProgress
"Repeating Period Timezone":"", // java/ICU timezone identifier
"Rolling Period Duration":"years", // Possible values include: years, months, days, hours, or minutes
"Rolling Period Quantity":123,
"Fixed Period Start":"", // Valid formats are '2007-01-01' or '2007-01-01T00:00:00'
"Fixed Period End":"", // Valid formats are '2007-01-01' or '2007-01-01T00:00:00'
"Fixed Period Timezone":"", // java timezone identifier
"Calculated KPI Expression":"expression1",
"User ID":"user 1",
"View Access":"public", // Possible values are either: public or personal
"Format Decimal Precision":2,
"Format Currency":"USD", // ISO4217.Currency identifier
"Format Percentage":true, // Possible values are either: false or true
"KPI Value":"90",
"KPI Value Localized":"xxx", // "xxx" is localized KPI value
"KPI Metric Filter Array":[
{
"KPI Metric Filter ID":"filter id1"
"Filter Metric ID":"filter metric id1"
"Filter Metric Name":""
"Filter Metric Type":"STRING"
"Filter Operator":"lessThan" // Possible values include: equals, lessThan, lessThanOrEquals, greaterThan, greaterThanOrEquals, notEquals, in, notIn, isNull, isNotNull, like, notLike
"Filter Operator Case Sensitive":false // Possible values are either: false or true
"Filter Value":"filter value1"
}
],
"KPI Range Array":[
{
"KPI Range ID":"range1"  
"KPI Range Display Name":"range name1"
"KPI Range Start Value":75  // if duration KPI, and if rangeType is actualValue, in milliseconds
"KPI Range Start Value Localized":"xxxxxx" // "xxxxxx" is localized range start
"KPI Range End Value":200 // if duration KPI, and if rangeType is actualValue, in milliseconds
"KPI Range End Value Localized":"xxxxxx" // "xxxxxx" is localized range start
"KPI Range Color":""  // #000000 - #FFFFFF (omit the # sign)
"KPI Range Icon":"images/kpi/monitorIcons/IBM_down_red.gif"
}
]                          
}
**/
}
}
xhr_.send();

From this result set, you can extract the business data from the KPIs. The JSON object now contains the current KPI value, any defined time filters, any defined metric data filters, and any defined KPI ranges. Now we will use the locale query parameter. This parameter is in the form of xx_YY, where xx is the language code and YY is the country code. Supplying this locale parameter formats the KPI values, range values, targets, and so on with a localized value in the supplied locale. When working with business data in mathematical calculations, you can use the KPI Value field. However, when displaying values to users, you can use the KPI Value Localized field.

There is a lot of data that can be used in this response. Using the data for the value, target, and range data, you construct a gauge-type view that is implemented in the Business Space and WebSphere Portal dashboards of WebSphere Business Monitor. If the ranges array contains at least one range, you can iterate over the array to determine the minimum and maximum range values. This determines the span of the gauge. If there are no defined ranges, you can generate a range with some heuristics, using educated guesses at minimum and maximum range values. For the purpose of our examples, a range is generated using the following parameters:
  • For the lesser value, the greater value between 1.25 times the value of the minimum and 0.
  • For the greater value, the lesser value between 1.25 times the value of the maximum and 0.

Next, if there is a non-null KPI value, determine the current status of the KPI. To determine the status, iterate over the ranges array and compare the KPI Value to each range's KPI Start Value and KPI End Value. If the KPI value falls within this range, you can display the KPI Range Display Name as the current status.

If the KPI Range Color is not null, you can mark up the interface with the current range's color.

If the KPI Range Icon is not null, you can display the range icon which the you previously selected. The value of the KPI range icon is a URL relative to the REST service. In the preceding example, the value of images/kpi/monitorIcons/IBM_down_red.gif is relative to the value of http://localhost:9080/rest/. The complete URL of the status icon is then http://localhost:9080/rest/images/kpi/monitorIcons/IBM_down_red.gif. These icons can either be downloaded during development and packaged with your client applications, or if your platform/language supports dynamic remote image loading (similar to a browser loading a remote image), you can simply create the URL on the fly and point to the source of the image. The REST service located at http://localhost:9080/rest/bpm/monitor/icons/kpi returns a list of all available status icons to the user.

The general flow of a KPI viewing application is described in the following pseudocode:

KPI Selection displayed in a gauge format:
- on open, issue a GET call to /monitor/models and update UI with model and versions
- on model and version selection, issue a GET call to /monitor/models/MODELID/version/VERSION/kpis and update UI with available KPIs
- on KPI selection, issue a GET call to /monitor/models/MODELID/version/VERSION/kpis/value/KPIID
- on successful KPI response, parse JSON data object and step through the necessary UI updates
- if ranges are defined, sort the range list and determine the minimum and maximum values
- if ranges are not defined, determine an auto-generated minimum and maximum based on available kpi value or target
- display range widgets as pie pieces in gauge
- if value is defined, determine rotation of value arrow based on comparison of value to minimum and maximum of ranges
- if target is defined, determine rotation of target arrow based on comparison of value to minimum and maximum of ranges

Although it is outside the scope of the previous examples, it is worth noting that the KPI services also enable writing KPI data, much the same way the alerts data is writable. A POST/DELETE service call to the URL of http://localhost:9080/rest/bpm/monitor/models/MODELID/versions/VERSION/kpis/config/KPIID deletes the KPI if the KPI was created in the dashboards and the user has delete authority. A new KPI can also be created or an existing KPI can be updated. Calling the URL http://localhost:9080/rest/bpm/monitor/models/MODELID/versions/VERSION/kpis/config with a POST method creates a new KPI. Calling the same URL with a POST/PUT method call updates an existing KPI. To create a new KPI or edit an existing KPI, a JSON object containing the desired data is required to be posted. The exact format of this JSON object is defined in the Business Monitor REST API reference.

References: