/*
* 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.dashboard.portlets.inventory.groups.graph;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.smartgwt.client.data.Criteria;
import com.smartgwt.client.types.Overflow;
import com.smartgwt.client.types.TitleOrientation;
import com.smartgwt.client.widgets.Canvas;
import com.smartgwt.client.widgets.HTMLFlow;
import com.smartgwt.client.widgets.Label;
import com.smartgwt.client.widgets.events.ResizedEvent;
import com.smartgwt.client.widgets.events.ResizedHandler;
import com.smartgwt.client.widgets.form.DynamicForm;
import com.smartgwt.client.widgets.form.events.SubmitValuesEvent;
import com.smartgwt.client.widgets.form.events.SubmitValuesHandler;
import com.smartgwt.client.widgets.form.fields.CanvasItem;
import com.smartgwt.client.widgets.form.fields.SelectItem;
import com.smartgwt.client.widgets.form.fields.SpacerItem;
import com.smartgwt.client.widgets.form.fields.TextItem;
import com.smartgwt.client.widgets.grid.ListGridRecord;
import org.rhq.core.domain.common.EntityContext;
import org.rhq.core.domain.configuration.PropertySimple;
import org.rhq.core.domain.criteria.ResourceGroupCriteria;
import org.rhq.core.domain.dashboard.DashboardPortlet;
import org.rhq.core.domain.measurement.MeasurementDefinition;
import org.rhq.core.domain.measurement.composite.MeasurementDataNumericHighLowComposite;
import org.rhq.core.domain.resource.ResourceType;
import org.rhq.core.domain.resource.group.GroupCategory;
import org.rhq.core.domain.resource.group.ResourceGroup;
import org.rhq.core.domain.util.PageList;
import org.rhq.coregui.client.CoreGUI;
import org.rhq.coregui.client.LinkManager;
import org.rhq.coregui.client.components.form.SortedSelectItem;
import org.rhq.coregui.client.components.selector.AssignedItemsChangedEvent;
import org.rhq.coregui.client.components.selector.AssignedItemsChangedHandler;
import org.rhq.coregui.client.dashboard.AutoRefreshPortlet;
import org.rhq.coregui.client.dashboard.AutoRefreshUtil;
import org.rhq.coregui.client.dashboard.CustomSettingsPortlet;
import org.rhq.coregui.client.dashboard.Portlet;
import org.rhq.coregui.client.dashboard.PortletViewFactory;
import org.rhq.coregui.client.dashboard.PortletWindow;
import org.rhq.coregui.client.gwt.GWTServiceLookup;
import org.rhq.coregui.client.gwt.ResourceGroupGWTServiceAsync;
import org.rhq.coregui.client.inventory.common.graph.MetricGraphData;
import org.rhq.coregui.client.inventory.common.graph.graphtype.StackedBarMetricGraphImpl;
import org.rhq.coregui.client.inventory.resource.detail.monitoring.MetricD3Graph;
import org.rhq.coregui.client.inventory.resource.detail.monitoring.ResourceScheduledMetricDatasource;
import org.rhq.coregui.client.inventory.resource.selection.SingleResourceGroupSelector;
import org.rhq.coregui.client.inventory.resource.type.ResourceTypeRepository;
import org.rhq.coregui.client.util.BrowserUtility;
import org.rhq.coregui.client.util.Log;
import org.rhq.enterprise.server.measurement.util.MeasurementUtils;
/**
* @author Greg Hinkle
* @author Jay Shaughnessy
* @author Mike Thompson
*/
public class ResourceGroupD3GraphPortlet extends MetricD3Graph implements AutoRefreshPortlet, CustomSettingsPortlet {
// A non-displayed, persisted identifier for the portlet
public static final String KEY = "ResourceGroupMetricD3";
// A default displayed, persisted name for the portlet
public static final String NAME = MSG.view_portlet_defaultName_groupMetric();
// set on initial configuration, the window for this portlet view.
private PortletWindow portletWindow;
public static final String CFG_RESOURCE_GROUP_ID = "resourceGroupId";
public static final String CFG_DEFINITION_ID = "definitionId";
public static final String CFG_CUSTOM_TITLE = "customTitle";
public ResourceGroupD3GraphPortlet() {
setOverflow(Overflow.CLIP_H);
addResizedHandler(new ResizedHandler() {
public void onResized(ResizedEvent event) {
refresh();
}
});
}
@Override
/**
* Portlet Charts are defined by an additional portletId to enable a particular groupId/measurementId
* combination to be valid in multiple dashboards.
*/
public String getFullChartId() {
if (portletWindow != null && graph != null && graph.getMetricGraphData() != null) {
return "rChart-" + graph.getMetricGraphData().getChartId() + "-" + portletWindow.getStoredPortlet().getId();
} else {
// handle the case where the portlet has not been configured yet
return "";
}
}
public void configure(PortletWindow portletWindow, DashboardPortlet storedPortlet) {
if (null == this.portletWindow && null != portletWindow) {
this.portletWindow = portletWindow;
}
destroyMembers();
if ((null == storedPortlet) || (null == storedPortlet.getConfiguration())) {
return;
} else if (BrowserUtility.isBrowserPreIE9()) {
addMember(new Label("<i>" + MSG.chart_ie_not_supported() + "</i>"));
return;
}
graph = GWT.create(StackedBarMetricGraphImpl.class);
graph.setMetricGraphData(MetricGraphData.createForDashboard(portletWindow.getStoredPortlet().getId()));
if (storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_GROUP_ID) != null) {
refreshFromConfiguration(storedPortlet);
}
}
private void refreshFromConfiguration(DashboardPortlet storedPortlet) {
PropertySimple resourceIdProperty = storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_GROUP_ID);
PropertySimple measurementDefIdProperty = storedPortlet.getConfiguration().getSimple(CFG_DEFINITION_ID);
String customTitle = storedPortlet.getConfiguration().getSimpleValue(CFG_CUSTOM_TITLE, "");
if (resourceIdProperty != null && measurementDefIdProperty != null) {
final Integer entityId = resourceIdProperty.getIntegerValue();
final Integer measurementDefId = measurementDefIdProperty.getIntegerValue();
if (entityId != null && measurementDefId != null) {
queryResourceGroup(entityId, measurementDefId, customTitle);
}
}
}
private void queryResourceGroup(Integer entityId, final Integer measurementDefId, String customTitle) {
ResourceGroupGWTServiceAsync resourceService = GWTServiceLookup.getResourceGroupService();
final String portletTitle = customTitle == null || customTitle.isEmpty() ? NAME : customTitle;
ResourceGroupCriteria resourceCriteria = new ResourceGroupCriteria();
resourceCriteria.addFilterId(entityId);
resourceService.findResourceGroupsByCriteria(resourceCriteria, new AsyncCallback<PageList<ResourceGroup>>() {
@Override
public void onFailure(Throwable caught) {
CoreGUI.getErrorHandler().handleError(MSG.view_resource_monitor_graphs_lookupFailed(), caught);
}
@Override
public void onSuccess(PageList<ResourceGroup> result) {
if (result.isEmpty()) {
return;
}
// only concerned with first resource since this is a query by id
final ResourceGroup group = result.get(0);
HashSet<Integer> typesSet = new HashSet<Integer>();
typesSet.add(group.getResourceType().getId());
ResourceTypeRepository.Cache.getInstance().getResourceTypes(
typesSet.toArray(new Integer[typesSet.size()]),
EnumSet.of(ResourceTypeRepository.MetadataType.measurements),
new ResourceTypeRepository.TypesLoadedCallback() {
@Override
public void onTypesLoaded(Map<Integer, ResourceType> types) {
ResourceType type = types.get(group.getResourceType().getId());
for (final MeasurementDefinition def : type.getMetricDefinitions()) {
if (def.getId() == measurementDefId) {
Log.debug("Found portlet measurement definition !" + def);
// Adding the resource group link in the portlet pushed the chart down too far, so
// I'm adding it to the title. TODO: In the future (RHQ Metrics) the link
// back should be done better.
portletWindow.setTitle(portletTitle
+ " - "
+ LinkManager.getHref(LinkManager.getResourceGroupLink(group.getId()),
group.getName()));
graph.setEntityId(group.getId());
graph.setEntityName(group.getName());
graph.setDefinition(def);
final long startTime = System.currentTimeMillis();
GWTServiceLookup.getMeasurementDataService().findDataForCompatibleGroupForLast(
group.getId(), new int[] { def.getId() }, 8, MeasurementUtils.UNIT_HOURS,
60, new AsyncCallback<List<List<MeasurementDataNumericHighLowComposite>>>() {
@Override
public void onFailure(Throwable caught) {
CoreGUI.getErrorHandler().handleError(
MSG.view_resource_monitor_graphs_loadFailed(), caught);
}
@Override
public void onSuccess(
final List<List<MeasurementDataNumericHighLowComposite>> measurementData) {
Log.debug("Dashboard Metric data in: "
+ (System.currentTimeMillis() - startTime) + " ms.");
graph.getMetricGraphData().setMetricData(measurementData.get(0));
drawGraph();
}
});
break;
}
}
}
});
}
});
}
@Override
protected void setupGraphDiv(HTMLFlow graphDiv) {
graphDiv.setHeight100();
graphDiv.setWidth("750px");
}
public Canvas getHelpCanvas() {
return new HTMLFlow(MSG.view_portlet_help_graph());
}
@Override
protected void onDraw() {
DashboardPortlet storedPortlet = portletWindow.getStoredPortlet();
if (BrowserUtility.isBrowserPreIE9()) {
removeMembers(getMembers());
addMember(new Label("<i>" + MSG.chart_ie_not_supported() + "</i>"));
} else {
PropertySimple simple = storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_GROUP_ID);
if (simple == null || simple.getIntegerValue() == null) {
removeMembers(getMembers());
addMember(new Label("<i>" + MSG.view_portlet_configure_needed() + "</i>"));
} else {
super.onDraw();
}
}
}
public DynamicForm getCustomSettingsForm() {
final DynamicForm form = new DynamicForm();
form.setWidth(750);
form.setNumCols(1);
final CanvasItem selectorItem = new CanvasItem();
selectorItem.setTitleOrientation(TitleOrientation.TOP);
selectorItem.setShowTitle(false);
final SingleResourceGroupSelector resourceGroupSelector = new SingleResourceGroupSelector(
GroupCategory.COMPATIBLE, false);
resourceGroupSelector.setWidth(700);
resourceGroupSelector.setHeight(300);
final SelectItem metric = new SortedSelectItem(CFG_DEFINITION_ID, MSG.common_title_metric()) {
@Override
protected Criteria getPickListFilterCriteria() {
Criteria criteria = new Criteria();
if (resourceGroupSelector.getSelection().size() == 1) {
int groupId = resourceGroupSelector.getSelection().iterator().next();
criteria.addCriteria(CFG_RESOURCE_GROUP_ID, groupId);
form.setValue(CFG_RESOURCE_GROUP_ID, groupId);
}
return criteria;
}
};
final TextItem customTitle = new TextItem(CFG_CUSTOM_TITLE, MSG.view_portlet_configure_customTitle());
customTitle.setWidth(300);
customTitle.setTitleOrientation(TitleOrientation.TOP);
metric.setWidth(300);
metric.setTitleOrientation(TitleOrientation.TOP);
metric.setValueField("id");
metric.setDisplayField("displayName");
metric.setOptionDataSource(new ResourceScheduledMetricDatasource());
resourceGroupSelector.addAssignedItemsChangedHandler(new AssignedItemsChangedHandler() {
public void onSelectionChanged(AssignedItemsChangedEvent event) {
if (resourceGroupSelector.getSelection().size() == 1) {
metric.fetchData();
form.clearValue(CFG_DEFINITION_ID);
}
}
});
final DashboardPortlet storedPortlet = portletWindow.getStoredPortlet();
if (storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_GROUP_ID) != null) {
Integer integerValue = storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_GROUP_ID).getIntegerValue();
if (integerValue != null) {
form.setValue(CFG_RESOURCE_GROUP_ID, integerValue);
ListGridRecord group = new ListGridRecord();
group.setAttribute("id", integerValue);
ListGridRecord[] groups = { group };
resourceGroupSelector.setAssigned(groups);
}
PropertySimple propertySimple = storedPortlet.getConfiguration().getSimple(CFG_DEFINITION_ID);
if (propertySimple != null && propertySimple.getIntegerValue() != null)
form.setValue(CFG_DEFINITION_ID, propertySimple.getIntegerValue());
form.setValue(CFG_CUSTOM_TITLE, storedPortlet.getConfiguration().getSimpleValue(CFG_CUSTOM_TITLE));
}
selectorItem.setCanvas(resourceGroupSelector);
form.setFields(selectorItem, metric, customTitle, new SpacerItem());
form.addSubmitValuesHandler(new SubmitValuesHandler() {
public void onSubmitValues(SubmitValuesEvent submitValuesEvent) {
ResourceGroup selectedGroup = resourceGroupSelector.getSelectedGroup();
String groupId = selectedGroup == null ? null : String.valueOf(selectedGroup.getId()); // can be null
storedPortlet.getConfiguration().put(new PropertySimple(CFG_RESOURCE_GROUP_ID, groupId));
storedPortlet.getConfiguration().put(
new PropertySimple(CFG_DEFINITION_ID, form.getValue(CFG_DEFINITION_ID)));
storedPortlet.getConfiguration().put(
new PropertySimple(CFG_CUSTOM_TITLE, form.getValueAsString(CFG_CUSTOM_TITLE)));
configure(portletWindow, storedPortlet);
redraw();
}
});
return form;
}
@Override
public void redraw() {
super.redraw();
removeMembers(getMembers());
DashboardPortlet storedPortlet = portletWindow.getStoredPortlet();
PropertySimple simple = storedPortlet.getConfiguration().getSimple(CFG_RESOURCE_GROUP_ID);
if (BrowserUtility.isBrowserPreIE9()) {
addMember(new Label("<i>" + MSG.chart_ie_not_supported() + "</i>"));
} else if (simple == null || simple.getIntegerValue() == null) {
addMember(new Label("<i>" + MSG.view_portlet_configure_needed() + "</i>"));
} else {
drawGraph();
}
}
public void startRefreshCycle() {
refreshTimer = AutoRefreshUtil.startRefreshCycleWithPageRefreshInterval(this, this, refreshTimer);
}
@Override
protected void onDestroy() {
AutoRefreshUtil.onDestroy(refreshTimer);
super.onDestroy();
}
public boolean isRefreshing() {
return false;
}
//Custom refresh operation as we are not directly extending Table
@Override
public void refresh() {
if (isVisible() && !isRefreshing()) {
refreshFromConfiguration(portletWindow.getStoredPortlet());
}
}
public static final class Factory implements PortletViewFactory {
public static final PortletViewFactory INSTANCE = new Factory();
public final Portlet getInstance(EntityContext context) {
return new ResourceGroupD3GraphPortlet();
}
}
}