/*
* RHQ Management Platform
* Copyright 2011, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
package org.rhq.coregui.client.inventory.groups.detail.monitoring.table;
import static org.rhq.coregui.client.inventory.resource.ResourceDataSourceField.CATEGORY;
import static org.rhq.coregui.client.inventory.resource.ResourceDataSourceField.INVENTORY_STATUS;
import static org.rhq.coregui.client.inventory.resource.ResourceDataSourceField.NAME;
import static org.rhq.coregui.client.inventory.resource.ResourceDataSourceField.TYPE;
import static org.rhq.coregui.client.inventory.resource.ResourceDataSourceField.TYPE_ID;
import java.util.ArrayList;
import java.util.Date;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.data.DSRequest;
import com.smartgwt.client.data.DSResponse;
import com.smartgwt.client.data.Record;
import com.smartgwt.client.data.SortSpecifier;
import com.smartgwt.client.types.GroupStartOpen;
import com.smartgwt.client.types.SortDirection;
import com.smartgwt.client.widgets.grid.CellFormatter;
import com.smartgwt.client.widgets.grid.GroupValueFunction;
import com.smartgwt.client.widgets.grid.HoverCustomizer;
import com.smartgwt.client.widgets.grid.ListGrid;
import com.smartgwt.client.widgets.grid.ListGridField;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import com.smartgwt.client.widgets.grid.events.RecordClickEvent;
import com.smartgwt.client.widgets.grid.events.RecordClickHandler;
import org.rhq.core.domain.common.EntityContext;
import org.rhq.core.domain.criteria.Criteria;
import org.rhq.core.domain.measurement.DataType;
import org.rhq.core.domain.measurement.DisplayType;
import org.rhq.core.domain.measurement.MeasurementDefinition;
import org.rhq.core.domain.measurement.ui.MetricDisplaySummary;
import org.rhq.core.domain.measurement.ui.MetricDisplayValue;
import org.rhq.core.domain.resource.InventoryStatus;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.core.domain.resource.group.composite.ResourceGroupComposite;
import org.rhq.core.domain.util.ResourceTypeUtility;
import org.rhq.coregui.client.CoreGUI;
import org.rhq.coregui.client.ImageManager;
import org.rhq.coregui.client.LinkManager;
import org.rhq.coregui.client.UserSessionManager;
import org.rhq.coregui.client.components.table.IconField;
import org.rhq.coregui.client.components.table.Table;
import org.rhq.coregui.client.gwt.GWTServiceLookup;
import org.rhq.coregui.client.inventory.common.detail.summary.AbstractActivityView.ChartViewWindow;
import org.rhq.coregui.client.inventory.common.graph.ButtonBarDateTimeRangeEditor;
import org.rhq.coregui.client.inventory.common.graph.CustomDateRangeState;
import org.rhq.coregui.client.inventory.common.graph.Refreshable;
import org.rhq.coregui.client.inventory.groups.detail.monitoring.table.GroupMembersComparisonView.GroupMembersComparisonDataSource;
import org.rhq.coregui.client.inventory.resource.AncestryUtil;
import org.rhq.coregui.client.inventory.resource.type.ResourceTypeRepository;
import org.rhq.coregui.client.inventory.resource.type.ResourceTypeRepository.TypesLoadedCallback;
import org.rhq.coregui.client.util.MeasurementUtility;
import org.rhq.coregui.client.util.RPCDataSource;
import org.rhq.coregui.client.util.StringUtility;
import org.rhq.coregui.client.util.preferences.MeasurementUserPreferences;
/**
* @author Jay Shaughnessy
*/
public class GroupMembersComparisonView extends Table<GroupMembersComparisonDataSource> implements Refreshable { //, AutoRefresh {
private static SortSpecifier DEFAULT_SORT_SPECIFIER_1 = new SortSpecifier(
GroupMembersComparisonDataSource.FIELD_METRIC_NAME, SortDirection.ASCENDING);
private static SortSpecifier DEFAULT_SORT_SPECIFIER_2 = new SortSpecifier(NAME.propertyName(),
SortDirection.ASCENDING);
protected ButtonBarDateTimeRangeEditor buttonBarDateTimeRangeEditor;
private final ResourceGroupComposite groupComposite;
private final int[] resourceIds;
private final GroupMembersComparisonDataSource dataSource;
public GroupMembersComparisonView(ResourceGroupComposite groupComposite, int[] resourceIds) {
super(null, new SortSpecifier[] { DEFAULT_SORT_SPECIFIER_1, DEFAULT_SORT_SPECIFIER_2 });
this.groupComposite = groupComposite;
this.resourceIds = resourceIds;
dataSource = new GroupMembersComparisonDataSource();
setDataSource(dataSource);
//disable full-screen fields, just use auto refresh
setShowFooterRefresh(false);
buttonBarDateTimeRangeEditor = new ButtonBarDateTimeRangeEditor(this);
}
@Override
public void refreshData() {
if (isVisible()) {
refreshDateTimeRangeEditor();
refresh();
}
}
private void refreshDateTimeRangeEditor() {
Date now = new Date();
long timeRange = CustomDateRangeState.getInstance().getTimeRange();
Date newStartDate = new Date(now.getTime() - timeRange);
buttonBarDateTimeRangeEditor.showUserFriendlyTimeRange(newStartDate.getTime(), now.getTime());
}
@Override
public GroupMembersComparisonDataSource getDataSource() {
return this.dataSource;
}
@Override
protected void configureTableFilters() {
// currently no table filters
}
@Override
protected ListGrid createListGrid() {
return new GroupMembersComparisonListGrid();
}
@Override
protected void configureTable() {
ArrayList<ListGridField> dataSourceFields = getDataSource().getListGridFields();
getListGrid().setFields(dataSourceFields.toArray(new ListGridField[dataSourceFields.size()]));
addTopWidget(buttonBarDateTimeRangeEditor);
}
/**
* The list grid with metric per row and embedded component for the selected resources
*/
private class GroupMembersComparisonListGrid extends ListGrid {
public GroupMembersComparisonListGrid() {
super();
setShowAllRecords(true);
setGroupByField(GroupMembersComparisonDataSource.FIELD_METRIC_NAME);
setGroupStartOpen(GroupStartOpen.ALL);
setCanCollapseGroup(false);
// this grouped view can't display a useful total rows value, so show nothing
setShowFooter(false);
}
}
public class GroupMembersComparisonDataSource extends RPCDataSource<MetricDisplaySummary, Criteria> {
public static final String FIELD_MIN_VALUE = "min";
public static final String FIELD_MAX_VALUE = "max";
public static final String FIELD_AVG_VALUE = "avg";
public static final String FIELD_LAST_VALUE = "last";
public static final String FIELD_METRIC_NAME = "metricName";
public static final String FIELD_ICON = "icon";
public static final String ATTR_RESOURCE_ID = "resourceId";
public static final String ATTR_DEFINITION_ID = "definitionId";
private final MeasurementUserPreferences measurementUserPrefs;
public GroupMembersComparisonDataSource() {
measurementUserPrefs = new MeasurementUserPreferences(UserSessionManager.getUserPreferences());
}
public ArrayList<ListGridField> getListGridFields() {
ArrayList<ListGridField> fields = new ArrayList<ListGridField>();
ListGridField metricNameField = new ListGridField(FIELD_METRIC_NAME, MSG.common_title_name());
metricNameField.setHidden(true);
metricNameField.setGroupValueFunction(new GroupValueFunction() {
public Object getGroupValue(Object value, ListGridRecord record, ListGridField field, String fieldName,
ListGrid grid) {
// just create a group for each metric display name
return value;
}
});
fields.add(metricNameField);
IconField iconField = new IconField(FIELD_ICON);
iconField.setWidth(25);
// click an icon, win a chart
iconField.addRecordClickHandler(new RecordClickHandler() {
@Override
public void onRecordClick(RecordClickEvent event) {
Record record = event.getRecord();
String title = record.getAttribute(NAME.propertyName());
ChartViewWindow window = new ChartViewWindow("", title);
int defId = record.getAttributeAsInt(GroupMembersComparisonDataSource.ATTR_DEFINITION_ID);
ResourceGroup group = groupComposite.getResourceGroup();
EntityContext context = EntityContext.forGroup(group);
CompositeGroupD3GraphListView graph = new CompositeGroupD3MultiLineGraph(context, defId);
window.addItem(graph);
graph.populateData();
window.show();
}
});
fields.add(iconField);
ListGridField nameField = new ListGridField(NAME.propertyName(), NAME.title(), 250);
nameField.setCellFormatter(new CellFormatter() {
public String format(Object value, ListGridRecord record, int rowNum, int colNum) {
String invStatus = record.getAttribute(INVENTORY_STATUS.propertyName());
if (InventoryStatus.COMMITTED == InventoryStatus.valueOf(invStatus)) {
String url = LinkManager.getResourceLink(record.getAttributeAsInt(ATTR_RESOURCE_ID));
String name = StringUtility.escapeHtml(value.toString());
return LinkManager.getHref(url, name);
} else {
return value.toString();
}
}
});
nameField.setShowHover(true);
nameField.setHoverCustomizer(new HoverCustomizer() {
public String hoverHTML(Object value, ListGridRecord listGridRecord, int rowNum, int colNum) {
return AncestryUtil.getResourceHoverHTML(listGridRecord, 0);
}
});
fields.add(nameField);
ListGridField ancestryField = AncestryUtil.setupAncestryListGridField();
fields.add(ancestryField);
ListGridField minField = new ListGridField(FIELD_MIN_VALUE, MSG.common_title_monitor_minimum());
minField.setWidth("15%");
fields.add(minField);
ListGridField maxField = new ListGridField(FIELD_MAX_VALUE, MSG.common_title_monitor_maximum());
maxField.setWidth("15%");
fields.add(maxField);
ListGridField avgField = new ListGridField(FIELD_AVG_VALUE, MSG.common_title_monitor_average());
avgField.setWidth("15%");
fields.add(avgField);
ListGridField lastField = new ListGridField(FIELD_LAST_VALUE, MSG.view_resource_monitor_table_last());
lastField.setWidth("15%");
fields.add(lastField);
return fields;
}
@Override
protected void executeFetch(final DSRequest request, final DSResponse response, final Criteria unused) {
final ResourceGroup group = groupComposite.getResourceGroup();
// Load the fully fetched ResourceType.
ResourceType groupType = group.getResourceType();
ResourceTypeRepository.Cache.getInstance().getResourceTypes(groupType.getId(),
EnumSet.of(ResourceTypeRepository.MetadataType.measurements),
new ResourceTypeRepository.TypeLoadedCallback() {
public void onTypesLoaded(ResourceType type) {
group.setResourceType(type);
//metric definitions
Set<MeasurementDefinition> definitions = type.getMetricDefinitions();
ArrayList<MeasurementDefinition> filteredDefinitions = new ArrayList<MeasurementDefinition>();
// TODO: make this a filter to allow summary or detail
boolean summaryOnly = false;
for (MeasurementDefinition d : definitions) {
if (DataType.MEASUREMENT == d.getDataType()
&& (!summaryOnly || (d.getDisplayType() != DisplayType.SUMMARY))) {
filteredDefinitions.add(d);
}
}
int[] definitionIds = new int[filteredDefinitions.size()];
int i = 0;
for (MeasurementDefinition d : filteredDefinitions) {
definitionIds[i++] = d.getId();
}
long begin = measurementUserPrefs.getMetricRangePreferences().begin;
long end = measurementUserPrefs.getMetricRangePreferences().end;
GWTServiceLookup.getMeasurementChartsService().getMetricDisplaySummariesForMetricsCompare(
resourceIds, definitionIds, begin, end,
new AsyncCallback<Map<MeasurementDefinition, List<MetricDisplaySummary>>>() {
public void onFailure(Throwable caught) {
CoreGUI.getErrorHandler().handleError("Cannot load comparison data", caught);
}
public void onSuccess(Map<MeasurementDefinition, List<MetricDisplaySummary>> result) {
List<MetricDisplaySummary> all = new ArrayList<MetricDisplaySummary>();
for (MeasurementDefinition key : result.keySet()) {
all.addAll(result.get(key));
}
dataRetrieved(all, response, request);
}
});
}
});
}
protected void dataRetrieved(final List<MetricDisplaySummary> result, final DSResponse response,
final DSRequest request) {
HashSet<Integer> typesSet = new HashSet<Integer>();
HashSet<String> ancestries = new HashSet<String>();
for (MetricDisplaySummary mds : result) {
Resource resource = mds.getResource();
ResourceType type = resource.getResourceType();
if (type != null) {
typesSet.add(type.getId());
}
ancestries.add(resource.getAncestry());
}
// In addition to the types of the result resources, get the types of their ancestry
typesSet.addAll(AncestryUtil.getAncestryTypeIds(ancestries));
ResourceTypeRepository typeRepo = ResourceTypeRepository.Cache.getInstance();
typeRepo.getResourceTypes(typesSet.toArray(new Integer[typesSet.size()]), new TypesLoadedCallback() {
@Override
public void onTypesLoaded(Map<Integer, ResourceType> types) {
// SmartGWT has issues storing a Map as a ListGridRecord attribute. Wrap it in a pojo.
AncestryUtil.MapWrapper typesWrapper = new AncestryUtil.MapWrapper(types);
Record[] records = buildRecords(result);
for (Record record : records) {
// replace type id with type name
Integer typeId = record.getAttributeAsInt(TYPE.propertyName());
ResourceType type = types.get(typeId);
if (type != null) {
record.setAttribute(TYPE.propertyName(), ResourceTypeUtility.displayName(type));
record.setAttribute(TYPE_ID.propertyName(), type.getId());
}
// To avoid a lot of unnecessary String construction, be lazy about building ancestry hover text.
// Store the types map off the records so we can build a detailed hover string as needed.
record.setAttribute(AncestryUtil.RESOURCE_ANCESTRY_TYPES, typesWrapper);
// Build the decoded ancestry Strings now for display
record.setAttribute(AncestryUtil.RESOURCE_ANCESTRY_VALUE, AncestryUtil.getAncestryValue(record));
}
response.setData(records);
processResponse(request.getRequestId(), response);
}
});
}
@Override
protected Criteria getFetchCriteria(DSRequest request) {
return null;
}
@Override
public ListGridRecord copyValues(MetricDisplaySummary from) {
ListGridRecord record = new ListGridRecord();
record.setAttribute(FIELD_ID, from.getMetricName() + "_" + from.getResourceId());
record.setAttribute(FIELD_METRIC_NAME, from.getLabel());
Resource resource = from.getResource();
record.setAttribute(NAME.propertyName(), resource.getName());
record.setAttribute(INVENTORY_STATUS.propertyName(), resource.getInventoryStatus());
record.setAttribute(CATEGORY.propertyName(), resource.getResourceType().getCategory().name());
record.setAttribute(AncestryUtil.RESOURCE_ANCESTRY, resource.getAncestry());
record.setAttribute(AncestryUtil.RESOURCE_TYPE_ID, resource.getResourceType().getId());
MeasurementUtility.formatSimpleMetrics(from);
record.setAttribute(FIELD_MIN_VALUE, getMetricStringValue(from.getMinMetric()));
record.setAttribute(FIELD_MAX_VALUE, getMetricStringValue(from.getMaxMetric()));
record.setAttribute(FIELD_AVG_VALUE, getMetricStringValue(from.getAvgMetric()));
record.setAttribute(FIELD_LAST_VALUE, getMetricStringValue(from.getLastMetric()));
record.setAttribute(FIELD_ICON, ImageManager.getMonitorIcon());
record.setAttribute(ATTR_RESOURCE_ID, from.getResourceId());
record.setAttribute(ATTR_DEFINITION_ID, from.getDefinitionId());
return record;
}
protected String getMetricStringValue(MetricDisplayValue value) {
return (value != null) ? value.toString() : "";
}
@Override
public MetricDisplaySummary copyValues(Record from) {
return null;
}
}
}