/* * RHQ Management Platform * Copyright (C) 2005-2014 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., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ package org.rhq.coregui.client.dashboard.portlets.recent.alerts; import java.util.ArrayList; import java.util.List; import java.util.Set; import com.google.gwt.user.client.Timer; import com.smartgwt.client.data.DSRequest; import com.smartgwt.client.data.DSResponse; import com.smartgwt.client.types.Overflow; import com.smartgwt.client.widgets.Canvas; import com.smartgwt.client.widgets.HTMLFlow; 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.CheckboxItem; import com.smartgwt.client.widgets.form.fields.FormItem; import com.smartgwt.client.widgets.form.fields.SelectItem; import com.smartgwt.client.widgets.form.fields.TextItem; import com.smartgwt.client.widgets.grid.CellFormatter; import com.smartgwt.client.widgets.grid.ListGridField; import com.smartgwt.client.widgets.grid.ListGridRecord; import org.rhq.core.domain.alert.Alert; import org.rhq.core.domain.alert.AlertFilter; import org.rhq.core.domain.alert.AlertPriority; import org.rhq.core.domain.authz.Permission; import org.rhq.core.domain.common.EntityContext; import org.rhq.core.domain.configuration.Configuration; import org.rhq.core.domain.configuration.PropertySimple; import org.rhq.core.domain.criteria.AlertCriteria; import org.rhq.core.domain.dashboard.DashboardPortlet; import org.rhq.core.domain.resource.composite.ResourcePermission; import org.rhq.core.domain.util.OrderingField; import org.rhq.core.domain.util.PageControl; import org.rhq.core.domain.util.PageList; import org.rhq.core.domain.util.PageOrdering; import org.rhq.coregui.client.CoreGUI; import org.rhq.coregui.client.LinkManager; import org.rhq.coregui.client.alert.AlertDataSource; import org.rhq.coregui.client.alert.AlertHistoryView; import org.rhq.coregui.client.components.measurement.CustomConfigMeasurementRangeEditor; import org.rhq.coregui.client.components.table.TimestampCellFormatter; 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.PortletWindow; import org.rhq.coregui.client.dashboard.portlets.PortletConfigurationEditorComponent; import org.rhq.coregui.client.dashboard.portlets.PortletConfigurationEditorComponent.Constant; import org.rhq.coregui.client.util.MeasurementUtility; import org.rhq.coregui.client.util.enhanced.EnhancedVLayout; /** * A base class for deriving recent alerts portlets for different entity contexts. In this way the * basic plumbing is shared, giving a consistent behavior and configuration for the concrete portlets. * * @author Jay Shaughnessy * @author Simeon Pinder */ public abstract class AbstractRecentAlertsPortlet extends AlertHistoryView implements CustomSettingsPortlet, AutoRefreshPortlet { // set on initial configuration, the window for this portlet view. private PortletWindow portletWindow; private AlertsPortletDataSource dataSource; // autorefresh timer private Timer refreshTimer; private String baseViewPath; public AbstractRecentAlertsPortlet(EntityContext entityContext) { super(null, entityContext); this.baseViewPath = LinkManager.getEntityTabLink(getContext(), "Alerts", "History"); setShowFilterForm(false); //disable filter form for portlet setOverflow(Overflow.VISIBLE); setShowFooterRefresh(false); //disable footer refresh button as redundant for portlets setShowHeader(false);//disable header for portlets } @Override protected String getBasePath() { return this.baseViewPath; } @Override protected CellFormatter getDetailsLinkColumnCellFormatter() { return new CellFormatter() { public String format(Object value, ListGridRecord record, int i, int i1) { String url = getAlertDetailLink(record); String formattedValue = TimestampCellFormatter.format(value); return LinkManager.getHref(url, formattedValue); } }; } @Override public void showDetails(ListGridRecord record) { String url = getAlertDetailLink(record); CoreGUI.goToView(url); } private String getAlertDetailLink(ListGridRecord record) { Integer alertId = getId(record); return LinkManager.getAlertDetailLink(getContext(), alertId); } @Override protected boolean canSupportDeleteAndAcknowledgeAll() { return false; } public Timer getRefreshTimer() { return refreshTimer; } public PortletWindow getPortletWindow() { return portletWindow; } @Override public AlertsPortletDataSource getDataSource() { if (this.dataSource == null) { this.dataSource = new AlertsPortletDataSource(getContext()); } return this.dataSource; } @Override public Canvas getHelpCanvas() { return new HTMLFlow(MSG.view_portlet_help_recentAlerts()); } @Override protected void configureTable() { super.configureTable(); ArrayList<ListGridField> dataSourceFields = getDataSource().getListGridFields(false); getListGrid().setFields(dataSourceFields.toArray(new ListGridField[dataSourceFields.size()])); } @Override public void configure(PortletWindow portletWindow, DashboardPortlet storedPortlet) { // the portletWindow does not change, so we can hold onto it locally if (null == this.portletWindow && null != portletWindow) { this.portletWindow = portletWindow; } // if there is no configuration there is nothing to set if ((null == storedPortlet) || (null == storedPortlet.getConfiguration())) { return; } Configuration config = storedPortlet.getConfiguration(); // not sure I love the fact that this common portlet config assigns some irrelevant/unused config props, // may be better to prune the common set and add the specific properties locally in this method for (String key : PortletConfigurationEditorComponent.CONFIG_PROPERTY_INITIALIZATION.keySet()) { if (config.getSimple(key) == null) { config.put(new PropertySimple(key, PortletConfigurationEditorComponent.CONFIG_PROPERTY_INITIALIZATION .get(key))); } } getDataSource().setConfiguration(config); } @Override public DynamicForm getCustomSettingsForm() { DynamicForm customSettingsForm = new DynamicForm(); EnhancedVLayout page = new EnhancedVLayout(); DynamicForm filterForm = new DynamicForm(); filterForm.setMargin(5); final DashboardPortlet storedPortlet = this.portletWindow.getStoredPortlet(); final Configuration portletConfig = storedPortlet.getConfiguration(); // alert name filter final TextItem alertNameFilter = PortletConfigurationEditorComponent.getAlertNameEditor(portletConfig); // alert priority selector final SelectItem alertPrioritySelector = PortletConfigurationEditorComponent .getAlertPriorityEditor(portletConfig); // result count selector final SelectItem resultCountSelector = PortletConfigurationEditorComponent.getResultCountEditor(portletConfig); // range selector final CustomConfigMeasurementRangeEditor measurementRangeEditor = PortletConfigurationEditorComponent .getMeasurementRangeEditor(portletConfig); // "Filter acknowledged", "Filter recovery alerts", "Filter recovered" - drop down final SelectItem filterSelector = PortletConfigurationEditorComponent.getAlertFilterEditor(portletConfig); filterForm.setItems(alertNameFilter, alertPrioritySelector, resultCountSelector, filterSelector); //submit handler customSettingsForm.addSubmitValuesHandler(new SubmitValuesHandler() { @Override public void onSubmitValues(SubmitValuesEvent event) { // alert name String selectedValue = (null == alertNameFilter.getValue()) ? "" : alertNameFilter.getValue() .toString(); portletConfig.put(new PropertySimple(Constant.ALERT_NAME, selectedValue)); // alert severity selectedValue = (null == alertPrioritySelector.getValue()) ? "" : alertPrioritySelector .getValue().toString(); if ((selectedValue.trim().isEmpty()) || (selectedValue.split(",").length == AlertPriority.values().length)) { // no severity filter selectedValue = Constant.ALERT_PRIORITY_DEFAULT; } portletConfig.put(new PropertySimple(Constant.ALERT_PRIORITY, selectedValue)); // result count selectedValue = resultCountSelector.getValue().toString(); if (selectedValue.trim().isEmpty()) { selectedValue = Constant.RESULT_COUNT_DEFAULT; } portletConfig.put(new PropertySimple(Constant.RESULT_COUNT, selectedValue)); // time range settings saveMeasurementRangeEditorSettings(measurementRangeEditor, portletConfig); // filter settings if(filterSelector.getValue() == null) { selectedValue = Constant.ALERT_FILTER_DEFAULT; } else { selectedValue = filterSelector.getValue().toString(); } portletConfig.put(new PropertySimple(Constant.ALERT_FILTER, selectedValue)); // persist and reload portlet storedPortlet.setConfiguration(portletConfig); configure(portletWindow, storedPortlet); //apply latest settings to the visible result set refresh(); } }); page.addMember(measurementRangeEditor); page.addMember(filterForm); customSettingsForm.addChild(page); return customSettingsForm; } /** * Takes the current value of the widget and persists it into the configuration object passed in. * * @param measurementRangeEditor metric range editor widget * @param portletConfig - the config to be updated */ private void saveMeasurementRangeEditorSettings(final CustomConfigMeasurementRangeEditor measurementRangeEditor, Configuration portletConfig) { String selectedValue; if ((measurementRangeEditor != null) && (portletConfig != null)) { //time range filter. Check for enabled and then persist property. Dealing with compound widget. FormItem item = measurementRangeEditor.getItem(CustomConfigMeasurementRangeEditor.ENABLE_RANGE_ITEM); CheckboxItem itemC = (CheckboxItem) item; boolean persistTimeRangeSettings = itemC.getValueAsBoolean(); if (persistTimeRangeSettings) {//retrieve values and persist selectedValue = String.valueOf(itemC.getValueAsBoolean()); if (!selectedValue.trim().isEmpty()) {//then call portletConfig.put(new PropertySimple(Constant.METRIC_RANGE_ENABLE, selectedValue)); } //time advanced time filter enabled. boolean isAdvanceTimeSetting = false; selectedValue = String.valueOf(measurementRangeEditor.isAdvanced()); if ((selectedValue != null) && (!selectedValue.trim().isEmpty())) { portletConfig.put(new PropertySimple(Constant.METRIC_RANGE_BEGIN_END_FLAG, selectedValue)); isAdvanceTimeSetting = Boolean.valueOf(selectedValue); } //time frame List<Long> begEnd = measurementRangeEditor.getBeginEndTimes(); if (isAdvanceTimeSetting) {//advanced settings portletConfig.put(new PropertySimple(Constant.METRIC_RANGE, (begEnd.get(0) + "," + begEnd.get(1)))); } else { //save not advanced time range portletConfig.put(new PropertySimple(Constant.METRIC_RANGE_LASTN, measurementRangeEditor .getMetricRangePreferences().lastN)); portletConfig.put(new PropertySimple(Constant.METRIC_RANGE_UNIT, measurementRangeEditor .getMetricRangePreferences().unit)); } } else {//if disabled, reset time defaults portletConfig.put(new PropertySimple(Constant.METRIC_RANGE_ENABLE, false)); portletConfig.put(new PropertySimple(Constant.METRIC_RANGE_BEGIN_END_FLAG, false)); List<Long> rangeArray = MeasurementUtility.calculateTimeFrame( Integer.valueOf(Constant.METRIC_RANGE_LASTN_DEFAULT), Integer.valueOf(Constant.METRIC_RANGE_UNIT_DEFAULT)); // String[] range = {String.valueOf(rangeArray.get(0)),String.valueOf(rangeArray.get(1))}; portletConfig.put(new PropertySimple(Constant.METRIC_RANGE, (String.valueOf(rangeArray.get(0)) + "," + String.valueOf(rangeArray.get(1))))); } } } @Override protected void setupTableInteractions(boolean hasWriteAccess) { if (!hasWriteAccess) { Set<Permission> globalPerm = this.getPortletWindow().getGlobalPermissions(); ResourcePermission resPerm = this.getPortletWindow().getResourcePermissions(); hasWriteAccess = (globalPerm.contains(Permission.MANAGE_INVENTORY) || (null != resPerm && resPerm.isAlert())); } super.setupTableInteractions(hasWriteAccess); } @Override public void refreshTableInfo() { super.refreshTableInfo(); if (getTableInfo() != null) { int count = getListGrid().getSelectedRecords().length; getTableInfo().setContents( MSG.view_table_matchingRows(String.valueOf(getListGrid().getTotalRows()), String.valueOf(count))); } } public void startRefreshCycle() { refreshTimer = AutoRefreshUtil.startRefreshCycleWithPageRefreshInterval(this, this, refreshTimer); } public boolean isRefreshing() { return false; } @Override protected void onDestroy() { AutoRefreshUtil.onDestroy(refreshTimer); super.onDestroy(); } @Override public void refresh() { if (!isRefreshing()) { super.refresh(); } } static public class AlertsPortletDataSource extends AlertDataSource { private Configuration configuration; public AlertsPortletDataSource(EntityContext entityContext) { this(entityContext, null); } public AlertsPortletDataSource(EntityContext entityContext, Configuration configuration) { super(entityContext); this.configuration = configuration; } public Configuration getConfiguration() { return configuration; } public void setConfiguration(Configuration configuration) { this.configuration = configuration; } @Override protected void setPagingInfo(DSResponse response, PageList<?> pageList) { response.setTotalRows(pageList.size()); } /* (non-Javadoc) * This override allows us to set the total rows to the number of recent op history configured for * the portlet. This sets the counter appropriately and stops further queries to the server. * * @see org.rhq.coregui.client.operation.OperationHistoryDataSource#getTotalRows(org.rhq.core.domain.util.PageList, com.smartgwt.client.data.DSResponse, com.smartgwt.client.data.DSRequest) */ @Override protected int getTotalRows(final PageList<Alert> result, final DSResponse response, final DSRequest request) { return result.size(); } @Override protected AlertCriteria getFetchCriteria(DSRequest request) { AlertCriteria criteria = new AlertCriteria(); // name filter String currentSetting = this.configuration.getSimpleValue(Constant.ALERT_NAME, ""); criteria.addFilterName(currentSetting); // result count currentSetting = this.configuration.getSimpleValue(Constant.RESULT_COUNT, Constant.RESULT_COUNT_DEFAULT); // We have to set a PageControl override here, or RPCDataSource will apply default paging based on the // request. But, once setting a paging override the CriteriaQueryGenerator will use it for // paging *and* sorting, so we need to also ensure our desired sorting is included in the override. So, // to get the most recent alerts, apply a descending ordering on create time. int pageNumber = 0; int pageSize = Integer.valueOf(currentSetting); OrderingField orderingField = new OrderingField("ctime", PageOrdering.DESC); criteria.setPageControl(new PageControl(pageNumber, pageSize, orderingField)); // filter priority currentSetting = this.configuration .getSimpleValue(Constant.ALERT_PRIORITY, Constant.ALERT_PRIORITY_DEFAULT); String[] parsedValues = currentSetting.trim().split(","); if (!(currentSetting.trim().isEmpty() || parsedValues.length == AlertPriority.values().length)) { AlertPriority[] filterPriorities = new AlertPriority[parsedValues.length]; int indx = 0; for (String priority : parsedValues) { AlertPriority p = AlertPriority.valueOf(priority); filterPriorities[indx++] = p; } criteria.addFilterPriorities(filterPriorities); } //result timeframe if enabled PropertySimple property = configuration.getSimple(Constant.METRIC_RANGE_ENABLE); if (null != property && Boolean.valueOf(property.getBooleanValue())) {//then proceed setting boolean isAdvanced = Boolean.valueOf(configuration.getSimpleValue(Constant.METRIC_RANGE_BEGIN_END_FLAG, Constant.METRIC_RANGE_BEGIN_END_FLAG_DEFAULT)); if (isAdvanced) { //Advanced time settings currentSetting = configuration.getSimpleValue(Constant.METRIC_RANGE, Constant.METRIC_RANGE_DEFAULT); String[] range = currentSetting.split(","); if (range.length == 2) { criteria.addFilterStartTime(Long.valueOf(range[0])); criteria.addFilterEndTime(Long.valueOf(range[1])); } } else { //Simple time settings property = configuration.getSimple(Constant.METRIC_RANGE_LASTN); if (property != null) { Integer lastN = Integer.valueOf(configuration.getSimpleValue(Constant.METRIC_RANGE_LASTN, Constant.METRIC_RANGE_LASTN_DEFAULT)); Integer units = Integer.valueOf(configuration.getSimpleValue(Constant.METRIC_RANGE_UNIT, Constant.METRIC_RANGE_UNIT_DEFAULT)); ArrayList<Long> beginEnd = MeasurementUtility.calculateTimeFrame(lastN, units); criteria.addFilterStartTime(Long.valueOf(beginEnd.get(0))); criteria.addFilterEndTime(Long.valueOf(beginEnd.get(1))); } } } String filters = this.configuration.getSimpleValue(Constant.ALERT_FILTER, Constant.ALERT_FILTER_DEFAULT); if(filters != null && filters.length() > 0) { String[] filterArray = filters.trim().split(","); for(String filter : filterArray) { if(filter.equals(AlertFilter.ACKNOWLEDGED_STATUS.name())) { criteria.addFilterUnacknowledgedOnly(true); } else if(filter.equals(AlertFilter.RECOVERY_TYPE.name())) { criteria.addFilterRecoveryIds(Integer.valueOf(0)); // Filter all alerts with recoveryId = 0 ( } else if(filter.equals(AlertFilter.RECOVERED_STATUS.name())) { criteria.addFilterRecovered(true); } } } // add any context related filters switch (getEntityContext().type) { case Resource: criteria.addFilterResourceIds(getEntityContext().getResourceId()); break; case ResourceGroup: criteria.addFilterResourceGroupIds(getEntityContext().getGroupId()); break; default: // no default break; } criteria.fetchAlertDefinition(true); criteria.fetchRecoveryAlertDefinition(true); criteria.fetchConditionLogs(true); return criteria; } } @Override protected void onInit() { super.onInit(); getListGrid().setEmptyMessage(MSG.view_portlet_results_empty()); } }