/*
* RHQ Management Platform
* Copyright (C) 2005-2008 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.enterprise.gui.legacy.action.resource.common.monitor.alerts.config;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.validator.GenericValidator;
import org.apache.struts.action.ActionErrors;
import org.apache.struts.action.ActionMapping;
import org.apache.struts.action.ActionMessage;
import org.rhq.core.domain.alert.AlertCondition;
import org.rhq.core.domain.alert.AlertDampening;
import org.rhq.core.domain.alert.AlertDefinition;
import org.rhq.core.domain.alert.AlertPriority;
import org.rhq.core.domain.alert.BooleanExpression;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.event.EventSeverity;
import org.rhq.core.domain.measurement.AvailabilityType;
import org.rhq.core.domain.measurement.MeasurementDefinition;
import org.rhq.core.domain.operation.OperationRequestStatus;
import org.rhq.core.domain.util.PageControl;
import org.rhq.enterprise.gui.legacy.Constants;
import org.rhq.enterprise.gui.legacy.action.resource.ResourceForm;
import org.rhq.enterprise.gui.legacy.beans.OptionItem;
import org.rhq.enterprise.gui.legacy.util.RequestUtils;
import org.rhq.enterprise.server.alert.AlertDefinitionManagerLocal;
import org.rhq.enterprise.server.alert.AlertTemplateManagerLocal;
import org.rhq.enterprise.server.alert.GroupAlertDefinitionManagerLocal;
import org.rhq.enterprise.server.util.LookupUtil;
/**
* Form for editing / creating new alert definitions.
*/
public final class DefinitionForm extends ResourceForm {
private Log log = LogFactory.getLog(DefinitionForm.class);
// alert definition properties
private Integer ad; // nullable
private String name;
private String description;
private int priority;
private boolean active;
private int recoverId;
private String conditionExpression;
// conditions
private List<ConditionBean> conditions;
// default metric to select, if available
private int metricId;
private String metricName;
// default metric to select, if available
private int callTimeMetricId;
private String callTimeMetricName;
// dampening rules (1 = every time)
// 2) Each time condition set is true
private String consecutiveCountValue;
// 3) If condition set is true X times out of the last Y times it is evaluated
private String partialCountValue;
private String partialCountPeriod;
// 4) If condition set is true, but ignoring it the next X times
private String inverseCountValue;
// 5) Once every X times conditions are exceeded within a time period of Y <time>
private String durationCountValue;
private String durationCountPeriod;
private int durationCountPeriodUnits;
// enable action recovery & filters
private int whenEnabled; // alertDef.id of "Recovery Alert for <alertDef.name>"
private boolean disableForRecovery; // whenEnable isn't valid if this is checked
private boolean filteringControlActions;
private boolean filteringNotificationActions;
// for drop-downs
private Collection baselines;
private List<MeasurementDefinition> metrics;
private List<MeasurementDefinition> traits;
private List<MeasurementDefinition> calltimeMetrics;
private Collection<Map.Entry<String, Integer>> alertnames;
private Collection<Map.Entry<String, String>> conditionExpressionNames;
private boolean resourceConfigurationSupported;
// private boolean cascade;
private boolean readOnly;
private static String[] controlActionStatuses = { OperationRequestStatus.INPROGRESS.name(),
OperationRequestStatus.SUCCESS.name(), OperationRequestStatus.FAILURE.name(),
OperationRequestStatus.CANCELED.name() };
private static String[] eventSeverities = { EventSeverity.DEBUG.name(), EventSeverity.INFO.name(),
EventSeverity.WARN.name(), EventSeverity.ERROR.name(), EventSeverity.FATAL.name() };
private static OptionItem[] availabilityOptions;
static {
AvailabilityType[] values = AvailabilityType.values();
availabilityOptions = new OptionItem[values.length];
for (int i = 0; i < availabilityOptions.length; i++) {
availabilityOptions[i] = new OptionItem("Goes " + values[i].name(), values[i].name());
}
}
private static int[] priorities = new int[] { AlertPriority.HIGH.ordinal(), AlertPriority.MEDIUM.ordinal(),
AlertPriority.LOW.ordinal() };
private static int[] timeUnits = new int[] { Constants.ALERT_ACTION_ENABLE_UNITS_MINUTES,
Constants.ALERT_ACTION_ENABLE_UNITS_HOURS, Constants.ALERT_ACTION_ENABLE_UNITS_DAYS,
Constants.ALERT_ACTION_ENABLE_UNITS_WEEKS };
private static String[] comparators = new String[] { Constants.ALERT_THRESHOLD_COMPARATOR_GT,
Constants.ALERT_THRESHOLD_COMPARATOR_EQ, Constants.ALERT_THRESHOLD_COMPARATOR_LT };
private static String[] calltimeOptions = new String[] { Constants.CALLTIME_MAX, Constants.CALLTIME_MIN,
Constants.CALLTIME_AVG };
private static String[] calltimeOperators = new String[] { Constants.CALLTIME_SHRINKS, Constants.CALLTIME_CHANGES,
Constants.CALLTIME_GROWS };
// special form handlers
private boolean addingCondition;
private int deletedCondition;
public DefinitionForm() {
// do nothing
}
public Integer getAd() {
return ad;
}
public void setAd(Integer ad) {
this.ad = ad;
}
@Override
public String getName() {
return name;
}
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String getDescription() {
return description;
}
@Override
public void setDescription(String description) {
this.description = description;
}
public int getPriority() {
return priority;
}
public void setPriority(int priority) {
this.priority = priority;
}
public boolean isActive() {
return active;
}
public void setActive(boolean active) {
this.active = active;
}
/**
* @return Returns the recoverId.
*/
public int getRecoverId() {
return recoverId;
}
/**
* @param recoverId The recoverId to set.
*/
public void setRecoverId(int recoverId) {
this.recoverId = recoverId;
}
/*----------------------------------------------------------------
This method is needed for some fancy Javascript processing. Do
not remove.
----------------------------------------------------------------*/
public ConditionBean[] getConditions() {
ConditionBean[] conds = new ConditionBean[conditions.size()];
return conditions.toArray(conds);
}
public ConditionBean getCondition(int index) {
if (index >= conditions.size()) {
setNumConditions(index + 1);
}
return conditions.get(index);
}
public int getMetricId() {
return metricId;
}
public void setMetricId(int metricId) {
this.metricId = metricId;
}
public String getMetricName() {
return metricName;
}
public void setMetricName(String metricName) {
this.metricName = metricName;
// Select default metric
this.getCondition(0).setMetricName(metricName);
}
public int getCallTimeMetricId() {
return callTimeMetricId;
}
public void setCallTimeMetricId(int callTimeMetricId) {
this.callTimeMetricId = callTimeMetricId;
}
public String getCallTimeMetricName() {
return callTimeMetricName;
}
public void setCallTimeMetricName(String callTimeMetricName) {
this.callTimeMetricName = callTimeMetricName;
// Select default metric
this.getCondition(0).setCallTimeMetricName(callTimeMetricName);
}
/*
* NEVER INVOKED public void setCondition(int index, ConditionBean condition) { log.trace("setCondition(" + index +
* ", " + condition + ")"); if (conditions.size() > index) { conditions.set(index, condition); } else {
* conditions.add(condition); } }
*/
public int getNumConditions() {
return conditions.size();
}
public void setNumConditions(int numConditions) {
while (conditions.size() < numConditions) {
conditions.add(new ConditionBean());
}
while (conditions.size() > numConditions) {
conditions.remove(conditions.size() - 1);
}
}
/**
* Get when to enable the actions.
*/
public int getWhenEnabled() {
return whenEnabled;
}
public void setWhenEnabled(int whenEnabled) {
this.whenEnabled = whenEnabled;
}
public String getPartialCountValue() {
return partialCountValue;
}
public void setPartialCountValue(String partialCountValue) {
this.partialCountValue = partialCountValue;
}
public String getPartialCountPeriod() {
return partialCountPeriod;
}
public void setPartialCountPeriod(String partialCountPeriod) {
this.partialCountPeriod = partialCountPeriod;
}
public String getDurationCountValue() {
return durationCountValue;
}
public void setDurationCountValue(String durationCountValue) {
this.durationCountValue = durationCountValue;
}
public String getDurationCountPeriod() {
return durationCountPeriod;
}
public void setDurationCountPeriod(String durationCountPeriod) {
this.durationCountPeriod = durationCountPeriod;
}
public int getDurationCountPeriodUnits() {
return durationCountPeriodUnits;
}
public void setDurationCountPeriodUnits(int durationCountPeriodUnits) {
this.durationCountPeriodUnits = durationCountPeriodUnits;
}
/**
* @return Returns the disableForRecovery.
*/
public boolean isDisableForRecovery() {
return disableForRecovery;
}
/**
* @param disableForRecovery The disableForRecovery to set.
*/
public void setDisableForRecovery(boolean disableForRecovery) {
this.disableForRecovery = disableForRecovery;
}
public boolean isFilteringControlActions() {
return filteringControlActions;
}
public void setFilteringControlActions(boolean filteringControlActions) {
this.filteringControlActions = filteringControlActions;
}
public boolean isFilteringNotificationActions() {
return filteringNotificationActions;
}
public void setFilteringNotificationActions(boolean filteringNotificationActions) {
this.filteringNotificationActions = filteringNotificationActions;
}
public boolean isAddingCondition() {
return addingCondition;
}
public void setAddingCondition(boolean addingCondition) {
log.trace("setAddingCondition(" + addingCondition + ")");
this.addingCondition = addingCondition;
}
public int getDeletedCondition() {
return deletedCondition;
}
public void setDeletedCondition(int deletedCondition) {
log.trace("setDeletedCondition(" + deletedCondition + ")");
this.deletedCondition = deletedCondition;
}
public void deleteCondition(int deletedCondition) {
if (deletedCondition < conditions.size()) {
conditions.remove(deletedCondition);
}
}
public Collection getBaselines() {
return baselines;
}
public void setBaselines(Collection baselines) {
this.baselines = baselines;
}
public List<MeasurementDefinition> getMetrics() {
return metrics;
}
public void setMetrics(List<MeasurementDefinition> metrics) {
this.metrics = metrics;
}
public List<MeasurementDefinition> getTraits() {
return traits;
}
public void setTraits(List<MeasurementDefinition> traits) {
this.traits = traits;
}
/**
* @return Returns the alertnames.
*/
public Collection<Map.Entry<String, Integer>> getAlertnames() {
return alertnames;
}
/**
* @param alertnames The alertnames to set.
*/
public void setAlertnames(Collection<Map.Entry<String, Integer>> alertnames) {
this.alertnames = alertnames;
}
public String[] getControlActionStatuses() {
return controlActionStatuses;
}
public String[] getEventSeverities() {
return eventSeverities;
}
public OptionItem[] getAvailabilityOptions() {
return availabilityOptions;
}
public int[] getPriorities() {
return priorities;
}
public int[] getTimeUnits() {
return timeUnits;
}
public String[] getComparators() {
return comparators;
}
@Override
@SuppressWarnings("deprecation")
public void reset(ActionMapping mapping, HttpServletRequest request) {
super.reset(mapping, request);
setDefaults();
try {
Subject subject = RequestUtils.getSubject(request);
List<AlertDefinition> alertDefinitions = null;
// if there's a "type" param, then its an alert template (can't use isAlertTemplate because the
// definitionForm was just cleared on reset()
if (request.getParameter("id") != null && !request.getParameter("id").equals("")) {
AlertDefinitionManagerLocal alertDefinitionManager = LookupUtil.getAlertDefinitionManager();
Integer resourceId = RequestUtils.getResourceId(request);
alertDefinitions = alertDefinitionManager.findAlertDefinitions(subject, resourceId, PageControl
.getUnlimitedInstance());
} else if (request.getParameter("type") != null && !request.getParameter("type").equals("")) {
AlertTemplateManagerLocal alertTemplateManager = LookupUtil.getAlertTemplateManager();
Integer resourceTypeId = RequestUtils.getResourceTypeId(request);
alertDefinitions = alertTemplateManager.getAlertTemplates(subject, resourceTypeId, PageControl
.getUnlimitedInstance());
} else {
GroupAlertDefinitionManagerLocal groupAlertDefinitionManager = LookupUtil
.getGroupAlertDefinitionManager();
Integer groupId = RequestUtils.getGroupId(request);
alertDefinitions = groupAlertDefinitionManager.findGroupAlertDefinitions(subject, groupId, PageControl
.getUnlimitedInstance());
}
Map<String, Integer> alertDefinitionNameToIdMap = new HashMap<String, Integer>(alertDefinitions.size());
for (AlertDefinition alertDefinition : alertDefinitions) {
if (alertDefinition.getRecoveryId() != 0) {
continue; // already a recovery definition for something else
}
alertDefinitionNameToIdMap.put(alertDefinition.getName(), alertDefinition.getId());
}
setAlertnames(alertDefinitionNameToIdMap.entrySet());
} catch (Exception e) {
setAlertnames(new ArrayList<Map.Entry<String, Integer>>(0));
}
Map<String, String> expressionsMap = new HashMap<String, String>();
expressionsMap.put(BooleanExpression.ALL.toString(), BooleanExpression.ALL.name());
expressionsMap.put(BooleanExpression.ANY.toString(), BooleanExpression.ANY.name());
setConditionExpressionNames(expressionsMap.entrySet());
}
/**
* Import basic properties from The AlertDefinitionValue to this form.
*
* @param alertDef
*/
public void importProperties(AlertDefinition alertDef) {
this.setAd(alertDef.getId());
this.setName(alertDef.getName());
this.setDescription(alertDef.getDescription());
this.setActive(alertDef.getEnabled());
this.setPriority(alertDef.getPriority().ordinal());
this.setReadOnly(alertDef.isReadOnly());
// no point is getting this here, because it's edited on the conditions tiles
//this.setConditionExpression( alertDef.getConditionExpression().name() );
}
/**
* Export basic properties from this form to the AlertDefinitionValue.
*
* @param alertDef
*/
public void exportProperties(AlertDefinition alertDef) {
//alertDef.setId( this.getId() );
alertDef.setName(this.getName());
alertDef.setDescription(this.getDescription());
alertDef.setEnabled(this.isActive());
alertDef.setPriority(AlertPriority.values()[this.getPriority()]);
alertDef.setReadOnly(this.isReadOnly());
// no point is setting this here, because it's edited on the conditions tiles
//alertDef.setConditionExpression( BooleanExpression.valueOf( this.getConditionExpression() ) );
}
/**
* Import the conditions and enablement properties from the AlertDefinitionValue to this form.
*/
public void importConditionsEnablement(AlertDefinition alertDef, Subject subject) throws Exception {
// we import the id here, too, so that the update will work
this.setAd(alertDef.getId());
this.setConditionExpression(alertDef.getConditionExpression().name());
/*
* conditions
*/
Set<AlertCondition> conds = alertDef.getConditions();
setNumConditions(conds.size());
int i = 0;
for (AlertCondition cond : conds) {
ConditionBean condBean = conditions.get(i++);
condBean.importProperties(cond, subject);
}
/*
* recovery
*/
recoverId = alertDef.getRecoveryId();
disableForRecovery = alertDef.getWillRecover();
filteringControlActions = alertDef.getControlFiltered();
filteringNotificationActions = alertDef.getNotifyFiltered();
/*
* dampening
*/
AlertDampening alertDampening = alertDef.getAlertDampening();
AlertDampening.Category category = alertDampening.getCategory();
if (AlertDampening.Category.CONSECUTIVE_COUNT == category) {
consecutiveCountValue = String.valueOf(alertDampening.getValue());
} else if (AlertDampening.Category.PARTIAL_COUNT == category) {
partialCountPeriod = String.valueOf(alertDampening.getPeriod());
partialCountValue = String.valueOf(alertDampening.getValue());
} else if (AlertDampening.Category.INVERSE_COUNT == category) {
inverseCountValue = String.valueOf(alertDampening.getValue());
} else if (AlertDampening.Category.DURATION_COUNT == category) {
durationCountValue = String.valueOf(alertDampening.getValue());
durationCountPeriod = String.valueOf(alertDampening.getPeriod());
durationCountPeriodUnits = alertDampening.getPeriodUnits().ordinal() + 1;
}
whenEnabled = alertDampening.getCategory().ordinal();
}
/**
* Export the conditions and enablement properties from this form to the specified alert def.
*/
public void exportConditionsEnablement(AlertDefinition alertDef, HttpServletRequest request, Subject subject)
throws Exception {
alertDef.setConditionExpression(BooleanExpression.valueOf(this.getConditionExpression()));
/*
* conditions
*/
log.debug("Exporting " + this.getNumConditions() + " conditions...");
alertDef.removeAllConditions();
for (int i = 0; i < this.getNumConditions(); ++i) {
ConditionBean condBean = this.getCondition(i);
AlertCondition newCondition = condBean.exportProperties(request, subject);
alertDef.addCondition(newCondition);
}
/*
* recovery
*/
alertDef.setRecoveryId(this.getRecoverId());
alertDef.setWillRecover(this.isDisableForRecovery());
alertDef.setNotifyFiltered(this.isFilteringNotificationActions());
alertDef.setControlFiltered(this.isFilteringControlActions());
/*
* dampening
*/
AlertDampening alertDampening = new AlertDampening(AlertDampening.Category.values()[this.getWhenEnabled()]);
alertDef.setAlertDampening(alertDampening);
AlertDampening.Category category = alertDampening.getCategory();
if (category == AlertDampening.Category.CONSECUTIVE_COUNT) {
alertDampening.setValue(Integer.valueOf(this.getConsecutiveCountValue()));
} else if (category == AlertDampening.Category.PARTIAL_COUNT) {
alertDampening.setValue(Integer.valueOf(this.getPartialCountValue()));
alertDampening.setPeriod(Integer.valueOf(this.getPartialCountPeriod()));
} else if (category == AlertDampening.Category.INVERSE_COUNT) {
alertDampening.setValue(Integer.valueOf(this.getInverseCountValue()));
} else if (alertDampening.getCategory() == AlertDampening.Category.DURATION_COUNT) {
alertDampening.setValue(Integer.valueOf(this.getDurationCountValue()));
alertDampening.setPeriod(Integer.valueOf(this.getDurationCountPeriod()));
alertDampening.setPeriodUnits(AlertDampening.TimeUnits.values()[this.getDurationCountPeriodUnits() - 1]);
}
}
public void resetConditions() {
conditions = new ArrayList<ConditionBean>();
setNumConditions(1);
}
@Override
public ActionErrors validate(ActionMapping mapping, HttpServletRequest request) {
// don't validate if we are preparing the form ...
if (!shouldValidate(mapping, request)) {
return null;
}
ActionErrors errs = super.validate(mapping, request);
if (null == errs) {
errs = new ActionErrors();
}
// only do this advanced validation if we are editing
// conditions or creating a new definition
if (mapping.getName().equals("NewAlertDefinitionForm")
|| mapping.getName().equals("EditAlertDefinitionConditionsForm")) {
for (int i = 0; i < getNumConditions(); ++i) {
ConditionBean cond = getCondition(i);
cond.validate(request, errs, i);
}
AlertDampening.Category selectedCategory = AlertDampening.Category.values()[getWhenEnabled()];
if (selectedCategory == AlertDampening.Category.CONSECUTIVE_COUNT) {
validatePositive(getConsecutiveCountValue(), "consecutiveCountValue", errs);
} else if (selectedCategory == AlertDampening.Category.PARTIAL_COUNT) {
// using a boolean expression will shortcut the checking to the first failure
if (validatePositive(getPartialCountValue(), "partialCountValue", errs)
&& validatePositive(getPartialCountPeriod(), "partialCountPeriod", errs)
&& (Integer.parseInt(getPartialCountValue()) > Integer.parseInt(getPartialCountPeriod()))) {
errs.add("partialCountPeriod", new ActionMessage("alert.config.error.PartialCountRangeTooSmall"));
}
} else if (selectedCategory == AlertDampening.Category.INVERSE_COUNT) {
validatePositive(getInverseCountValue(), "inverseCountValue", errs);
} else if (selectedCategory == AlertDampening.Category.DURATION_COUNT) {
// using a boolean expression will shortcut the checking to the first failure
boolean valid = validatePositive(getDurationCountValue(), "durationCountValue", errs)
&& validatePositive(getDurationCountPeriod(), "durationCountPeriod", errs);
}
}
return errs;
}
private boolean validatePositive(String field, String fieldName, ActionErrors errs) {
if (GenericValidator.isBlankOrNull(field)) {
errs.add(fieldName, new ActionMessage("alert.config.error.DampeningFieldRequired"));
return false;
}
if ((GenericValidator.isInt(field) == false) || (Integer.parseInt(field) < 1)) {
errs.add(fieldName, new ActionMessage("alert.config.error.InvalidDampeningField"));
return false;
}
return true;
}
private void setDefaults() {
ad = null;
name = null;
description = null;
priority = AlertPriority.MEDIUM.ordinal();
active = true;
resetConditions();
conditionExpression = BooleanExpression.ALL.name();
whenEnabled = AlertDampening.Category.NONE.ordinal();
consecutiveCountValue = null;
partialCountValue = null;
partialCountPeriod = null;
inverseCountValue = null;
durationCountValue = null;
durationCountPeriod = null;
durationCountPeriodUnits = AlertDampening.TimeUnits.MINUTES.ordinal() + 1;
filteringControlActions = false;
filteringNotificationActions = false;
addingCondition = false;
deletedCondition = Constants.ALERT_CONDITION_NONE_DELETED;
baselines = null;
// cascade = false;
}
public void setAvailabilityActions(List<OptionItem> availabilityActions) {
// TODO Auto-generated method stub
}
public String getConditionExpression() {
return conditionExpression;
}
public void setConditionExpression(String conditionExpression) {
this.conditionExpression = conditionExpression;
}
public Collection<Map.Entry<String, String>> getConditionExpressionNames() {
return conditionExpressionNames;
}
public void setConditionExpressionNames(Collection<Map.Entry<String, String>> conditionExpressionNames) {
this.conditionExpressionNames = conditionExpressionNames;
}
public boolean isResourceConfigurationSupported() {
return resourceConfigurationSupported;
}
public void setResourceConfigurationSupported(boolean resourceConfigurationSupported) {
this.resourceConfigurationSupported = resourceConfigurationSupported;
}
public String getConsecutiveCountValue() {
return consecutiveCountValue;
}
public void setConsecutiveCountValue(String consecutiveTimes) {
this.consecutiveCountValue = consecutiveTimes;
}
public String getInverseCountValue() {
return inverseCountValue;
}
public void setInverseCountValue(String inverseCountValue) {
this.inverseCountValue = inverseCountValue;
}
public boolean isReadOnly() {
return readOnly;
}
public boolean getReadOnly() {
return readOnly;
}
public void setReadOnly(boolean readOnly) {
this.readOnly = readOnly;
}
public List<MeasurementDefinition> getCalltimeMetrics() {
return calltimeMetrics;
}
public void setCalltimeMetrics(List<MeasurementDefinition> calltimeMetrics) {
this.calltimeMetrics = calltimeMetrics;
}
public String[] getCalltimeOptions() {
return calltimeOptions;
}
public void setCalltimeOptions(String[] calltimeOptions) {
DefinitionForm.calltimeOptions = calltimeOptions;
}
public String[] getCalltimeOperators() {
return calltimeOperators;
}
public void setCalltimeOperators(String[] calltimeOperators) {
DefinitionForm.calltimeOperators = calltimeOperators;
}
}