blob: 4e75ca378f4b903c9cf3dea5d557fc9b38b318d0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.
*/
/* global define, module, require, exports */
/**
* Views state for a given component.
*/
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery',
'Slick',
'nf.ClusterSummary',
'nf.ErrorHandler',
'nf.Dialog',
'nf.Common'],
function ($, Slick, nfClusterSummary, nfErrorHandler, nfDialog, nfCommon) {
return (nf.ComponentState = factory($, Slick, nfClusterSummary, nfErrorHandler, nfDialog, nfCommon));
});
} else if (typeof exports === 'object' && typeof module === 'object') {
module.exports = (nf.ComponentState =
factory(require('jquery'),
require('Slick'),
require('nf.ClusterSummary'),
require('nf.ErrorHandler'),
require('nf.Dialog'),
require('nf.Common')));
} else {
nf.ComponentState = factory(root.$,
root.Slick,
root.nf.ClusterSummary,
root.nf.ErrorHandler,
root.nf.Dialog,
root.nf.Common);
}
}(this, function ($, Slick, nfClusterSummary, nfErrorHandler, nfDialog, nfCommon) {
'use strict';
/**
* Get the component state column model.
*/
var getComponentStateColumnModel = function () {
// initialize the queue listing table
var componentStateColumns = [
{
id: 'key',
field: 'key',
name: 'Key',
sortable: true,
resizable: true,
formatter: nfCommon.genericValueFormatter
},
{
id: 'value',
field: 'value',
name: 'Value',
sortable: true,
resizable: true,
formatter: nfCommon.genericValueFormatter
}
];
// conditionally show the cluster node identifier
if (nfClusterSummary.isConnectedToCluster()) {
componentStateColumns.push({
id: 'scope',
field: 'scope',
name: 'Scope',
sortable: true,
resizable: true,
formatter: nfCommon.genericValueFormatter
});
}
return componentStateColumns;
};
/**
* Filters the component state table.
*/
var applyFilter = function () {
// get the dataview
var componentStateTable = $('#component-state-table').data('gridInstance');
// ensure the grid has been initialized
if (nfCommon.isDefinedAndNotNull(componentStateTable)) {
var componentStateData = componentStateTable.getData();
// update the search criteria
componentStateData.setFilterArgs({
searchString: getFilterText()
});
componentStateData.refresh();
}
};
/**
* Determines if the item matches the filter.
*
* @param {object} item The item to filter
* @param {object} args The filter criteria
* @returns {boolean} Whether the item matches the filter
*/
var filter = function (item, args) {
if (args.searchString === '') {
return true;
}
try {
// perform the row filtering
var filterExp = new RegExp(args.searchString, 'i');
} catch (e) {
// invalid regex
return false;
}
// determine if the item matches the filter
var matchesKey = item['key'].search(filterExp) >= 0;
var matchesValue = item['value'].search(filterExp) >= 0;
// conditionally consider the scope
var matchesScope = false;
if (nfCommon.isDefinedAndNotNull(item['scope'])) {
matchesScope = item['scope'].search(filterExp) >= 0;
}
return matchesKey || matchesValue || matchesScope;
};
/**
* Sorts the specified data using the specified sort details.
*
* @param {object} sortDetails
* @param {object} data
*/
var sort = function (sortDetails, data) {
// defines a function for sorting
var comparer = function (a, b) {
var aString = nfCommon.isDefinedAndNotNull(a[sortDetails.columnId]) ? a[sortDetails.columnId] : '';
var bString = nfCommon.isDefinedAndNotNull(b[sortDetails.columnId]) ? b[sortDetails.columnId] : '';
return aString === bString ? 0 : aString > bString ? 1 : -1;
};
// perform the sort
data.sort(comparer, sortDetails.sortAsc);
};
/**
* Get the text out of the filter field. If the filter field doesn't
* have any text it will contain the text 'filter list' so this method
* accounts for that.
*/
var getFilterText = function () {
return $('#component-state-filter').val();
};
/**
* Clears the component state table.
*/
var clearTable = function () {
var componentStateGrid = $('#component-state-table').data('gridInstance');
var componentStateData = componentStateGrid.getData();
componentStateData.setItems([]);
// hide the partial results details message
$('#component-state-partial-results-container').hide();
// clear the total number entries
$('#displayed-component-state-entries').text('0');
$('#total-component-state-entries').text('0');
};
/**
* Loads the table with the component state.
*
* @param {object} componentState
*/
var loadComponentState = function (localState, clusterState) {
var count = 0;
var totalEntries = 0;
var showPartialDetails = false;
var componentStateGrid = $('#component-state-table').data('gridInstance');
componentStateGrid.setColumns(getComponentStateColumnModel());
var componentStateData = componentStateGrid.getData();
// begin the update
componentStateData.beginUpdate();
// local state
if (nfCommon.isDefinedAndNotNull(localState)) {
$.each(localState.state, function (i, stateEntry) {
componentStateData.addItem($.extend({
id: count++,
scope: stateEntry.clusterNodeAddress
}, stateEntry));
});
totalEntries += localState.totalEntryCount;
if (nfCommon.isDefinedAndNotNull(localState.state) && localState.totalEntryCount !== localState.state.length) {
showPartialDetails = true;
}
}
if (nfCommon.isDefinedAndNotNull(clusterState)) {
$.each(clusterState.state, function (i, stateEntry) {
componentStateData.addItem($.extend({
id: count++,
scope: 'Cluster'
}, stateEntry));
});
totalEntries += clusterState.totalEntryCount;
if (nfCommon.isDefinedAndNotNull(clusterState.state) && clusterState.totalEntryCount !== clusterState.state.length) {
showPartialDetails = true;
}
}
// complete the update
componentStateData.endUpdate();
componentStateData.reSort();
if (showPartialDetails) {
$('#component-state-partial-results-container').show();
}
// update the total number of state entries
$('#total-component-state-entries').text(nfCommon.formatInteger(totalEntries));
};
/**
* Reset the dialog.
*/
var resetDialog = function () {
// clear the fields
$('#component-state-name').text('');
$('#component-state-description').text('');
// clear any filter strings
$('#component-state-filter').val('');
// reset clear link
$('#clear-link').removeClass('disabled').attr('title', '');
// clear the table
clearTable();
// clear the component
$('#component-state-table').removeData('component');
};
return {
init: function () {
// intialize the component state filter
$('#component-state-filter').on('keyup', function () {
applyFilter();
});
// initialize the component state dialog
$('#component-state-dialog').modal({
scrollableContentStyle: 'scrollable',
headerText: 'Component State',
buttons: [{
buttonText: 'Close',
color: {
base: '#728E9B',
hover: '#004849',
text: '#ffffff'
},
handler: {
click: function () {
$(this).modal('hide');
}
}
}],
handler: {
close: function () {
resetDialog();
},
resize: function () {
$('#component-state-description')
.width($('#component-state-dialog').find('.dialog-content').innerWidth() - 1)
.text($('#component-state-description').attr('title'))
.ellipsis();
}
}
});
// clear state link
$('#clear-link').on('click', function () {
if ($(this).hasClass('disabled') === false) {
var componentStateTable = $('#component-state-table');
// ensure there is state to clear
var componentStateGrid = componentStateTable.data('gridInstance');
var stateEntryCount = componentStateGrid.getDataLength();
if (stateEntryCount > 0) {
var componentEntity = componentStateTable.data('component');
$.ajax({
type: 'POST',
url: componentEntity.uri + '/state/clear-requests',
dataType: 'json'
}).done(function (response) {
// clear the table
clearTable();
// reload the table with no state
loadComponentState()
}).fail(nfErrorHandler.handleAjaxError);
} else {
nfDialog.showOkDialog({
headerText: 'Component State',
dialogContent: 'This component has no state to clear.'
});
}
}
});
var componentStateOptions = {
autosizeColsMode: Slick.GridAutosizeColsMode.LegacyForceFit,
enableTextSelectionOnCells: true,
enableCellNavigation: false,
enableColumnReorder: false,
autoEdit: false,
rowHeight: 24
};
// initialize the dataview
var componentStateData = new Slick.Data.DataView({
inlineFilters: false
});
componentStateData.setItems([]);
componentStateData.setFilterArgs({
searchString: '',
property: 'key'
});
componentStateData.setFilter(filter);
// initialize the sort
sort({
columnId: 'key',
sortAsc: true
}, componentStateData);
// initialize the grid
var componentStateGrid = new Slick.Grid('#component-state-table', componentStateData, getComponentStateColumnModel(), componentStateOptions);
componentStateGrid.setSelectionModel(new Slick.RowSelectionModel());
componentStateGrid.registerPlugin(new Slick.AutoTooltips());
componentStateGrid.setSortColumn('key', true);
componentStateGrid.onSort.subscribe(function (e, args) {
sort({
columnId: args.sortCol.field,
sortAsc: args.sortAsc
}, componentStateData);
});
// wire up the dataview to the grid
componentStateData.onRowCountChanged.subscribe(function (e, args) {
componentStateGrid.updateRowCount();
componentStateGrid.render();
// update the total number of displayed items
$('#displayed-component-state-entries').text(args.current);
});
componentStateData.onRowsChanged.subscribe(function (e, args) {
componentStateGrid.invalidateRows(args.rows);
componentStateGrid.render();
});
// hold onto an instance of the grid
$('#component-state-table').data('gridInstance', componentStateGrid);
// initialize the number of display items
$('#displayed-component-state-entries').text('0');
$('#total-component-state-entries').text('0');
},
/**
* Shows the state for a given component.
*
* @param {object} componentEntity
* @param {boolean} canClear
*/
showState: function (componentEntity, canClear) {
return $.ajax({
type: 'GET',
url: componentEntity.uri + '/state',
dataType: 'json'
}).done(function (response) {
var componentState = response.componentState;
var componentStateTable = $('#component-state-table');
// load the table
loadComponentState(componentState.localState, componentState.clusterState);
// show the dialog
$('#component-state-dialog').modal('show');
// populate the name/description
$('#component-state-name').text(componentEntity.component.name);
$('#component-state-description')
.width($('#component-state-dialog').find('.dialog-content').innerWidth() - 1)
.text(componentState.stateDescription)
.ellipsis();
// store the component
componentStateTable.data('component', componentEntity);
// only activate the link when appropriate
if (canClear === false) {
$('#clear-link').addClass('disabled').attr('title', 'Component state can only be cleared when the component is not actively running');
}
// reset the grid size
var componentStateGrid = componentStateTable.data('gridInstance');
componentStateGrid.resizeCanvas();
}).fail(nfErrorHandler.handleAjaxError);
}
};
}));