/*
* RHQ Management Platform
* Copyright (C) 2005-2013 Red Hat, Inc.
* All rights reserved.
*
* 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.resource.detail.monitoring;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.types.Overflow;
import com.smartgwt.client.widgets.layout.VLayout;
import org.rhq.core.domain.common.EntityContext;
import org.rhq.core.domain.measurement.Availability;
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.composite.MeasurementDataNumericHighLowComposite;
import org.rhq.core.domain.measurement.composite.MeasurementOOBComposite;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.util.PageList;
import org.rhq.coregui.client.CoreGUI;
import org.rhq.coregui.client.gwt.GWTServiceLookup;
import org.rhq.coregui.client.inventory.common.AbstractD3GraphListView;
import org.rhq.coregui.client.inventory.common.graph.CustomDateRangeState;
import org.rhq.coregui.client.inventory.common.graph.MetricGraphData;
import org.rhq.coregui.client.inventory.common.graph.graphtype.AvailabilityOverUnderGraphType;
import org.rhq.coregui.client.inventory.common.graph.graphtype.StackedBarMetricGraphImpl;
import org.rhq.coregui.client.inventory.resource.detail.monitoring.avail.AvailabilityD3GraphView;
import org.rhq.coregui.client.util.Log;
import org.rhq.coregui.client.util.async.Command;
import org.rhq.coregui.client.util.async.CountDownLatch;
/**
* Build the View that shows the individual graph views for multi-graph
* views if just a resource is provided and single graph view if resource
* and measurement definitionId are provided.
*
* @author Mike Thompson
*/
public class D3GraphListView extends AbstractD3GraphListView {
private static final int NUM_ASYNC_CALLS = 2; // wait for X async calls in Latch
private final Resource resource;
private Set<Integer> definitionIds = null;
private boolean useSummaryData = false;
private PageList<MeasurementOOBComposite> measurementOOBCompositeList;
private List<List<MeasurementDataNumericHighLowComposite>> metricsDataList;
private VLayout vLayout;
public static D3GraphListView createMultipleGraphs(Resource resource, Set<Integer> definitionIds,
boolean showAvailabilityGraph) {
return new D3GraphListView(resource, definitionIds, showAvailabilityGraph);
}
public static D3GraphListView createSummaryMultipleGraphs(Resource resource, boolean monitorDetailView) {
return new D3GraphListView(resource, monitorDetailView);
}
public static D3GraphListView createSingleGraph(Resource resource, Integer measurementId,
boolean showAvailabilityGraph) {
TreeSet<Integer> definitionIds = new TreeSet<Integer>();
definitionIds.add(measurementId);
return new D3GraphListView(resource, definitionIds, showAvailabilityGraph);
}
public static D3GraphListView createSingleGraphNoAvail(Resource resource, Integer measurementId) {
return D3GraphListView.createSingleGraph(resource, measurementId, false);
}
private D3GraphListView(Resource resource, Set<Integer> definitionIds, boolean showAvailabilityGraph) {
super();
this.resource = resource;
commonConstructorSettings();
this.definitionIds = definitionIds;
this.showAvailabilityGraph = showAvailabilityGraph;
}
private D3GraphListView(Resource resource, boolean showAvailabilityGraph) {
super();
this.resource = resource;
this.showAvailabilityGraph = showAvailabilityGraph;
commonConstructorSettings();
useSummaryData = true;
}
private void commonConstructorSettings() {
setOverflow(Overflow.HIDDEN);
}
@Override
protected void onDraw() {
super.onDraw();
Log.debug("D3GraphListView.onDraw() for: " + resource.getName() + " id: " + resource.getId());
destroyMembers();
addMember(buttonBarDateTimeRangeEditor);
if (showAvailabilityGraph) {
availabilityGraph = AvailabilityD3GraphView.create(
new AvailabilityOverUnderGraphType(resource.getId()));
addMember(availabilityGraph);
}
vLayout = new VLayout();
vLayout.setOverflow(Overflow.AUTO);
vLayout.setWidth100();
vLayout.setHeight100();
queryAndBuildGraphs();
addMember(vLayout);
}
public void refreshData() {
this.onDraw();
}
@Override
protected void queryAvailability(final EntityContext context, Long startTime, Long endTime,
final CountDownLatch countDownLatch) {
final long timerStart = System.currentTimeMillis();
// now return the availability
GWTServiceLookup.getAvailabilityService().getAvailabilitiesForResource(context.getResourceId(), startTime,
endTime, new AsyncCallback<List<Availability>>() {
@Override
public void onFailure(Throwable caught) {
CoreGUI.getErrorHandler().handleError(MSG.view_resource_monitor_availability_loadFailed(), caught);
if (countDownLatch != null) {
countDownLatch.countDown();
}
}
@Override
public void onSuccess(List<Availability> availList) {
Log.debug("\nSuccessfully queried availability in: " + (System.currentTimeMillis() - timerStart)
+ " ms.");
availabilityList = availList;
if (countDownLatch != null) {
countDownLatch.countDown();
}
}
});
}
/**
* Build whatever graph (summary or not) by grabbing the MeasurementDefinitions
* that are defined for the resource and then querying the metric and availability data.
*/
private void queryAndBuildGraphs() {
final long startTimer = System.currentTimeMillis();
if (showAvailabilityGraph) {
queryAvailability(EntityContext.forResource(resource.getId()), CustomDateRangeState.getInstance().getStartTime(),
CustomDateRangeState.getInstance().getEndTime(), null);
}
final ArrayList<MeasurementDefinition> measurementDefinitions = new ArrayList<MeasurementDefinition>();
final ArrayList<MeasurementDefinition> summaryMeasurementDefinitions = new ArrayList<MeasurementDefinition>();
for (MeasurementDefinition def : resource.getResourceType().getMetricDefinitions()) {
if (def.getDataType() == DataType.MEASUREMENT && def.getDisplayType() == DisplayType.SUMMARY) {
summaryMeasurementDefinitions.add(def);
}
measurementDefinitions.add(def);
}
Collections.sort(measurementDefinitions, new Comparator<MeasurementDefinition>() {
@Override
public int compare(MeasurementDefinition o1, MeasurementDefinition o2) {
return new Integer(o1.getDisplayOrder()).compareTo(o2.getDisplayOrder());
}
});
Collections.sort(summaryMeasurementDefinitions, new Comparator<MeasurementDefinition>() {
@Override
public int compare(MeasurementDefinition o1, MeasurementDefinition o2) {
return new Integer(o1.getDisplayOrder()).compareTo(o2.getDisplayOrder());
}
});
int[] measDefIdArray = new int[measurementDefinitions.size()];
for (int i = 0; i < measDefIdArray.length; i++) {
measDefIdArray[i] = measurementDefinitions.get(i).getId();
}
// setting up a deferred Command to execute after all resource queries have completed (successfully or unsuccessfully)
// we know there are exactly 2 resources
final CountDownLatch countDownLatch = CountDownLatch.create(NUM_ASYNC_CALLS, new Command() {
@Override
/**
* Satisfied only after ALL of the metric queries AND availability have completed
*/
public void execute() {
Log.debug("Total Time for async metrics/avail query: " + (System.currentTimeMillis() - startTimer));
if (null == metricsDataList || metricsDataList.isEmpty()) {
loadingLabel.setContents(MSG.view_resource_monitor_graphs_noneAvailable());
} else {
loadingLabel.hide();
if (useSummaryData) {
buildSummaryGraphs(metricsDataList, summaryMeasurementDefinitions, measurementDefinitions);
} else {
determineGraphsToBuild(metricsDataList, measurementDefinitions, definitionIds);
}
// There is a weird timing case when availabilityGraph can be null
if (null != availabilityGraph) {
// we only need the first metricData since we are only taking the
// availability data set in there for the dropdowns already
availabilityGraph.setAvailabilityList(availabilityList);
new Timer() {
@Override
public void run() {
buttonBarDateTimeRangeEditor.updateTimeRangeToNow();
availabilityGraph.drawJsniChart();
}
}.schedule(150);
}
}
}
});
queryMetricData(measDefIdArray, countDownLatch);
queryOOBMetrics(resource, countDownLatch);
// now the countDown latch will run sometime asynchronously
}
private void queryMetricData(final int[] measDefIdArray, final CountDownLatch countDownLatch) {
GWTServiceLookup.getMeasurementDataService().findDataForResource(resource.getId(), measDefIdArray,
CustomDateRangeState.getInstance().getStartTime(), CustomDateRangeState.getInstance().getEndTime(), 60,
new AsyncCallback<List<List<MeasurementDataNumericHighLowComposite>>>() {
@Override
public void onFailure(Throwable caught) {
CoreGUI.getErrorHandler().handleError(MSG.view_resource_monitor_graphs_loadFailed(), caught);
loadingLabel.setContents(MSG.view_resource_monitor_graphs_loadFailed());
countDownLatch.countDown();
}
@Override
public void onSuccess(List<List<MeasurementDataNumericHighLowComposite>> metrics) {
metricsDataList = metrics;
countDownLatch.countDown();
}
});
}
private void queryOOBMetrics(final Resource resource, final CountDownLatch countDownLatch) {
final long startTime = System.currentTimeMillis();
GWTServiceLookup.getMeasurementDataService().getHighestNOOBsForResource(resource.getId(), 60,
new AsyncCallback<PageList<MeasurementOOBComposite>>() {
@Override
public void onSuccess(PageList<MeasurementOOBComposite> measurementOOBComposites) {
measurementOOBCompositeList = measurementOOBComposites;
Log.debug("Successfully queried " + measurementOOBCompositeList.size() + " OOB records in: "
+ (System.currentTimeMillis() - startTime) + " ms.");
countDownLatch.countDown();
}
@Override
public void onFailure(Throwable caught) {
Log.debug("Error retrieving out of bound metrics for resource [" + resource.getId() + "]:"
+ caught.getMessage());
countDownLatch.countDown();
}
});
}
/**
* Spin through the measurement definitions (in order) checking to see if they are in the
* summary measurement definition set and if so build a graph.
* @param measurementData
* @param summaryMeasurementDefinitions
* @param measurementDefinitions
*/
private void buildSummaryGraphs(List<List<MeasurementDataNumericHighLowComposite>> measurementData,
List<MeasurementDefinition> summaryMeasurementDefinitions, List<MeasurementDefinition> measurementDefinitions) {
Set<Integer> summaryIds = new TreeSet<Integer>();
for (MeasurementDefinition summaryMeasurementDefinition : summaryMeasurementDefinitions) {
summaryIds.add(summaryMeasurementDefinition.getId());
}
int i = 0;
for (MeasurementDefinition measurementDefinition : measurementDefinitions) {
if (summaryIds.contains(measurementDefinition.getId())) {
buildSingleGraph(measurementOOBCompositeList, measurementDefinition, measurementData.get(i),
MULTI_CHART_HEIGHT);
}
i++;
}
}
private void determineGraphsToBuild(List<List<MeasurementDataNumericHighLowComposite>> measurementData,
List<MeasurementDefinition> measurementDefinitions, Set<Integer> definitionIds) {
int i = 0;
for (List<MeasurementDataNumericHighLowComposite> metric : measurementData) {
for (Integer selectedDefinitionId : definitionIds) {
final MeasurementDefinition measurementDefinition = measurementDefinitions.get(i);
final int measurementId = measurementDefinition.getId();
if (null != selectedDefinitionId) {
// single graph case
if (measurementId == selectedDefinitionId) {
buildSingleGraph(measurementOOBCompositeList, measurementDefinition, metric,
SINGLE_CHART_HEIGHT);
}
} else {
// multiple graph case
buildSingleGraph(measurementOOBCompositeList, measurementDefinition, metric, MULTI_CHART_HEIGHT);
}
}
i++;
}
}
private void buildSingleGraph(PageList<MeasurementOOBComposite> measurementOOBCompositeList,
MeasurementDefinition measurementDefinition, List<MeasurementDataNumericHighLowComposite> data, int height) {
MetricGraphData metricGraphData = MetricGraphData.createForResourceSummary(resource.getId(), resource.getName(),
measurementDefinition, data, measurementOOBCompositeList);
StackedBarMetricGraphImpl graph = GWT.create(StackedBarMetricGraphImpl.class);
graph.setMetricGraphData(metricGraphData);
graph.setGraphListView(this);
MetricD3Graph graphView = new MetricD3Graph<D3GraphListView>(graph, this);
graphView.setWidth("95%");
graphView.setHeight(height);
vLayout.addMember(graphView);
}
}