modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/alert/ListAlertHistoryUIBean.java
| 10
modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/ParamConstants.java
| 11
modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/AlertDefUtil.java
| 21 +
modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/ConditionBean.java
| 76 ++++
modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/DefinitionForm.java
| 37 +-
modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/DefinitionFormPrepareAction.java
| 40 ++
modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/condition/CallTimeDataConverterValidator.java
| 159 ++++++++++
modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/condition/ConverterValidatorManager.java
| 16 -
modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/subsystem/SubsystemAlertHistoryUIBean.java
| 12
modules/enterprise/gui/portal-war/src/main/webapp-filtered/WEB-INF/classes/ApplicationResources.properties
| 11
modules/enterprise/gui/portal-war/src/main/webapp/resource/common/monitor/alerts/config/DefinitionCondition.jsp
| 156 +++++++--
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/AlertConditionCacheManagerBean.java
| 9
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/AlertConditionCacheManagerLocal.java
| 15
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/internal/AbstractConditionCache.java
| 3
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/internal/AgentConditionCache.java
| 146 ++++++++-
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/internal/AlertConditionCacheCoordinator.java
| 46 ++
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/jms/CachedConditionProducerBean.java
| 11
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/jms/CachedConditionProducerLocal.java
| 6
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/model/AbstractCacheElement.java
| 6
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/model/CallTimeDataCacheElement.java
| 152 +++++++++
modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerBean.java
| 14
21 files changed, 869 insertions(+), 88 deletions(-)
New commits:
commit 99d3e5e1ae37b44344a4bba4dd913eb60e541d18
Author: Frank Brueseke <fbrueseke(a)s-lab.upb.de>
Date: Mon Mar 1 20:50:00 2010 +0100
Allow alerting on call-time data. BZ 535756
diff --git
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/alert/ListAlertHistoryUIBean.java
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/alert/ListAlertHistoryUIBean.java
index 0b6023a..ad6a2d7 100644
---
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/alert/ListAlertHistoryUIBean.java
+++
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/alert/ListAlertHistoryUIBean.java
@@ -40,6 +40,7 @@ import org.rhq.core.domain.alert.AlertPriority;
import org.rhq.core.domain.alert.composite.AlertWithLatestConditionLog;
import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.common.composite.IntegerOptionItem;
+import org.rhq.core.domain.measurement.DataType;
import org.rhq.core.domain.measurement.util.MeasurementConverter;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.util.PageControl;
@@ -114,8 +115,8 @@ public class ListAlertHistoryUIBean extends PagedDataTableUIBean {
public SelectItem[] getAlertDefinitionSelectItems() {
if (alertDefinitionSelectItems == null) {
- List<IntegerOptionItem> optionItems =
alertDefinitionManager.findAlertDefinitionOptionItemsForResource(getSubject(),
- getResource().getId());
+ List<IntegerOptionItem> optionItems =
alertDefinitionManager.findAlertDefinitionOptionItemsForResource(
+ getSubject(), getResource().getId());
alertDefinitionSelectItems =
SelectItemUtils.convertFromListOptionItem(optionItems, true);
}
@@ -267,7 +268,8 @@ public class ListAlertHistoryUIBean extends PagedDataTableUIBean {
String displayText =
AlertDefUtil.formatAlertConditionForDisplay(condition, request);
String firedValue = log.getValue();
- if (condition.getMeasurementDefinition() != null) {
+ if (condition.getMeasurementDefinition() != null
+ &&
!condition.getMeasurementDefinition().getDataType().equals(DataType.CALLTIME)) {
firedValue =
MeasurementConverter.format(Double.valueOf(log.getValue()), condition
.getMeasurementDefinition().getUnits(), true);
}
@@ -302,4 +304,4 @@ public class ListAlertHistoryUIBean extends PagedDataTableUIBean {
private String[] getSelectedAlerts() {
return
FacesContextUtility.getRequest().getParameterValues("selectedAlerts");
}
-}
\ No newline at end of file
+}
diff --git
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/ParamConstants.java
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/ParamConstants.java
index a659e89..d113c15 100755
---
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/ParamConstants.java
+++
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/ParamConstants.java
@@ -84,6 +84,15 @@ public interface ParamConstants {
*/
public static final String ALERT_THRESHOLD_COMPARATOR_LT = "<";
+ public static final String CALLTIME_MAX = "MAX";
+ public static final String CALLTIME_MIN = "MIN";
+ public static final String CALLTIME_AVG = "AVG";
+ public static final String CALLTIME_COUNT = "COUNT";
+
+ public static final String CALLTIME_CHANGES = "CH";
+ public static final String CALLTIME_SHRINKS = "LO";
+ public static final String CALLTIME_GROWS = "HI";
+
/**
* The request parameter that specifies the resource type of the autogroup resources
of the currently viewed
* resource.
@@ -535,4 +544,4 @@ public interface ParamConstants {
String LIST_NAME_PARAM = "listName";
String LIST_INDEX_PARAM = "listIndex";
-}
\ No newline at end of file
+}
diff --git
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/AlertDefUtil.java
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/AlertDefUtil.java
index c22d823..a3fdb84 100644
---
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/AlertDefUtil.java
+++
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/AlertDefUtil.java
@@ -35,6 +35,7 @@ import org.rhq.core.domain.alert.AlertDampening;
import org.rhq.core.domain.alert.AlertDefinition;
import org.rhq.core.domain.alert.AlertDefinitionContext;
import org.rhq.core.domain.auth.Subject;
+import org.rhq.core.domain.measurement.DataType;
import org.rhq.core.domain.measurement.MeasurementSchedule;
import org.rhq.core.domain.measurement.MeasurementUnits;
import org.rhq.core.domain.measurement.util.MeasurementConverter;
@@ -104,6 +105,12 @@ public final class AlertDefUtil {
} else if (category == AlertConditionCategory.RESOURCE_CONFIG) {
textValue.append(RequestUtils.message(request,
"alert.config.props.CB.Content.ResourceConfiguration"))
.append(' ');
+ } else if (cond.getMeasurementDefinition() != null
+ &&
cond.getMeasurementDefinition().getDataType().equals(DataType.CALLTIME)) {
+ textValue.append(cond.getMeasurementDefinition().getDisplayName());
+ textValue.append(" (");
+ textValue.append(cond.getOption());
+ textValue.append(") ");
} else {
textValue.append(cond.getName()).append(' ');
}
@@ -135,7 +142,17 @@ public final class AlertDefUtil {
}
} else if (category == AlertConditionCategory.RESOURCE_CONFIG || category ==
AlertConditionCategory.CHANGE
|| category == AlertConditionCategory.TRAIT) {
- textValue.append(RequestUtils.message(request,
"alert.current.list.ValueChanged"));
+ if (cond.getComparator() != null) {
+ textValue.append(RequestUtils.message(request,
"common.field.value")).append(' ');
+ textValue.append(RequestUtils.message(request,
"alert.config.props.CB.Content.CalltimeOperators."
+ + cond.getComparator()));
+ } else
+ textValue.append(RequestUtils.message(request,
"alert.current.list.ValueChanged"));
+ if (cond.getThreshold() != null) {
+ textValue.append(" ").append(RequestUtils.message(request,
"alert.config.props.CB.Content.AtLeast"));
+ textValue.append(" ").append(
+ MeasurementConverter.format(cond.getThreshold(),
MeasurementUnits.PERCENTAGE, true));
+ }
} else if (category == AlertConditionCategory.EVENT) {
String msgKey = "alert.config.props.CB.EventSeverity";
List<String> args = new ArrayList<String>(2);
@@ -298,4 +315,4 @@ public final class AlertDefUtil {
return "N/A";
}
-}
\ No newline at end of file
+}
diff --git
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/ConditionBean.java
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/ConditionBean.java
index ce09ad9..9d8cbd4 100644
---
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/ConditionBean.java
+++
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/ConditionBean.java
@@ -39,6 +39,14 @@ public final class ConditionBean {
private String metricName;
private String absoluteValue;
private String percentage;
+ private String calltimeComparator;
+ private String ctAbsoluteValue;
+ private String calltimeAbsOption;
+ private String calltimeAbsPattern;
+ private String ctPercentage;
+ private String calltimeChgOp;
+ private String calltimeChgOption;
+ private String calltimeChgPattern;
private String baselineOption;
private String controlAction;
private String controlActionStatus;
@@ -207,6 +215,72 @@ public final class ConditionBean {
this.traitName = traitName;
}
+ public String getCalltimeComparator() {
+ return calltimeComparator;
+ }
+
+ public void setCalltimeComparator(String calltimeComparator) {
+ this.calltimeComparator = calltimeComparator;
+ }
+
+ public String getCtAbsoluteValue() {
+ return ctAbsoluteValue;
+ }
+
+ public void setCtAbsoluteValue(String ctAbsoluteValue) {
+ this.ctAbsoluteValue = ctAbsoluteValue;
+ }
+
+ public String getCalltimeAbsOption() {
+ return calltimeAbsOption;
+ }
+
+ public void setCalltimeAbsOption(String calltimeAbsOption) {
+ this.calltimeAbsOption = calltimeAbsOption;
+ }
+
+ public String getCalltimeAbsPattern() {
+ return calltimeAbsPattern;
+ }
+
+ public void setCalltimeAbsPattern(String calltimeAbsPattern) {
+ if (calltimeAbsPattern != null &&
!calltimeAbsPattern.trim().equals(""))
+ this.calltimeAbsPattern = calltimeAbsPattern;
+ }
+
+ public String getCtPercentage() {
+ return ctPercentage;
+ }
+
+ public void setCtPercentage(String ctPercentage) {
+ this.ctPercentage = ctPercentage;
+ }
+
+ public String getCalltimeChgOption() {
+ return calltimeChgOption;
+ }
+
+ public void setCalltimeChgOption(String calltimeChgOption) {
+ this.calltimeChgOption = calltimeChgOption;
+ }
+
+ public String getCalltimeChgPattern() {
+ return calltimeChgPattern;
+ }
+
+ public void setCalltimeChgPattern(String calltimeChgPattern) {
+ if (calltimeChgPattern != null &&
!calltimeChgPattern.trim().equals(""))
+ this.calltimeChgPattern = calltimeChgPattern;
+ }
+
+ public String getCalltimeChgOp() {
+ return calltimeChgOp;
+ }
+
+ public void setCalltimeChgOp(String calltimeChgOp) {
+ this.calltimeChgOp = calltimeChgOp;
+ }
+
public void importProperties(AlertCondition cond, Subject subject) throws Exception
{
ConverterValidatorManager.importProperties(subject, cond, this);
}
@@ -218,4 +292,4 @@ public final class ConditionBean {
public boolean validate(HttpServletRequest request, ActionErrors errors, int index)
{
return ConverterValidatorManager.validate(this, errors, index);
}
-}
\ No newline at end of file
+}
diff --git
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/DefinitionForm.java
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/DefinitionForm.java
index a29da61..fe9764d 100644
---
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/DefinitionForm.java
+++
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/DefinitionForm.java
@@ -102,6 +102,7 @@ public final class DefinitionForm extends ResourceForm {
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;
@@ -137,6 +138,12 @@ public final class DefinitionForm extends ResourceForm {
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, Constants.CALLTIME_COUNT };
+
+ private static String[] calltimeOperators = new String[] {
Constants.CALLTIME_SHRINKS, Constants.CALLTIME_CHANGES,
+ Constants.CALLTIME_GROWS };
+
// special form handlers
private boolean addingCondition;
private int deletedCondition;
@@ -232,12 +239,6 @@ public final class DefinitionForm extends ResourceForm {
*/
public void setMetricId(int metricId) {
this.metricId = metricId;
-
- // Select default metric
- if (metricId > 0) {
- this.getCondition(0).setMetricId(metricId);
- this.getCondition(0).setTrigger("onMeasurement");
- }
}
public String getMetricName() {
@@ -763,4 +764,28 @@ public final class DefinitionForm extends ResourceForm {
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;
+ }
}
\ No newline at end of file
diff --git
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/DefinitionFormPrepareAction.java
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/DefinitionFormPrepareAction.java
index bf20899..ffc4e7c 100644
---
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/DefinitionFormPrepareAction.java
+++
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/DefinitionFormPrepareAction.java
@@ -92,11 +92,9 @@ public abstract class DefinitionFormPrepareAction extends TilesAction
{
log.trace("defForm.numConditions=" + defForm.getNumConditions());
request.setAttribute("numConditions", defForm.getNumConditions());
- request.setAttribute("showMetrics", defForm.getMetrics().size() >
0);
- request.setAttribute("showTraits", defForm.getTraits().size() > 0);
- request.setAttribute("showResourceConfiguration",
defForm.isResourceConfigurationSupported());
- request.setAttribute("showAvailability",
defForm.getAvailabilityOptions().length > 0);
- request.setAttribute("showOperations",
defForm.getControlActions().size() > 0);
+ // process data types from bottom to top and set the initialise
+ // the top-most "trigger" radio button
+ boolean triggerNotSet = defForm.getCondition(0).getTrigger() == null;
ResourceType type = null;
Subject overlord = LookupUtil.getSubjectManager().getOverlord();
@@ -113,6 +111,32 @@ public abstract class DefinitionFormPrepareAction extends TilesAction
{
}
int eventDefinitionCount =
LookupUtil.getEventManager().getEventDefinitionCountForResourceType(type.getId());
request.setAttribute("showEvents", eventDefinitionCount > 0);
+ if (eventDefinitionCount > 0 && triggerNotSet)
+ defForm.getCondition(0).setTrigger("onEvent");
+
+ request.setAttribute("showOperations",
defForm.getControlActions().size() > 0);
+ if (defForm.getControlActions().size() > 0 && triggerNotSet)
+ defForm.getCondition(0).setTrigger("onOperation");
+
+ request.setAttribute("showAvailability",
defForm.getAvailabilityOptions().length > 0);
+ if (defForm.getAvailabilityOptions().length > 0 && triggerNotSet)
+ defForm.getCondition(0).setTrigger("onAvailability");
+
+ request.setAttribute("showResourceConfiguration",
defForm.isResourceConfigurationSupported());
+ if (defForm.isResourceConfigurationSupported() && triggerNotSet)
+ defForm.getCondition(0).setTrigger("onResourceConfiguration");
+
+ request.setAttribute("showTraits", defForm.getTraits().size() > 0);
+ if (defForm.getTraits().size() > 0 && triggerNotSet)
+ defForm.getCondition(0).setTrigger("onTrait");
+
+ request.setAttribute("showCalltimeMetrics",
defForm.getCalltimeMetrics().size() > 0);
+ if (defForm.getCalltimeMetrics().size() > 0 && triggerNotSet)
+ defForm.getCondition(0).setTrigger("onCallTime");
+
+ request.setAttribute("showMetrics", defForm.getMetrics().size() >
0);
+ if (defForm.getMetrics().size() > 0 && triggerNotSet)
+ defForm.getCondition(0).setTrigger("onMeasurement");
return null;
}
@@ -152,6 +176,9 @@ public abstract class DefinitionFormPrepareAction extends TilesAction
{
List<MeasurementDefinition> dataDefinitions =
definitionManager.findMeasurementDefinitionsByResourceType(
subject, type.getId(), DataType.MEASUREMENT, null);
+ List<MeasurementDefinition> calltimeDefinitions =
definitionManager.findMeasurementDefinitionsByResourceType(
+ subject, type.getId(), DataType.CALLTIME, null);
+
List<MeasurementDefinition> traitDefinitions =
definitionManager.findMeasurementDefinitionsByResourceType(
subject, type.getId(), DataType.TRAIT, null);
@@ -184,6 +211,7 @@ public abstract class DefinitionFormPrepareAction extends TilesAction
{
}
defForm.setMetrics(dataDefinitions);
+ defForm.setCalltimeMetrics(calltimeDefinitions);
defForm.setTraits(traitDefinitions);
defForm.setBaselines(baselines);
@@ -231,4 +259,4 @@ public abstract class DefinitionFormPrepareAction extends TilesAction
{
return list;
}
-}
\ No newline at end of file
+}
diff --git
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/condition/ConverterValidatorManager.java
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/condition/ConverterValidatorManager.java
index 3f388a2..d41a3cd 100644
---
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/condition/ConverterValidatorManager.java
+++
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/condition/ConverterValidatorManager.java
@@ -23,12 +23,14 @@ import org.apache.struts.action.ActionErrors;
import org.rhq.core.domain.alert.AlertCondition;
import org.rhq.core.domain.alert.AlertConditionCategory;
import org.rhq.core.domain.auth.Subject;
+import org.rhq.core.domain.measurement.DataType;
import
org.rhq.enterprise.gui.legacy.action.resource.common.monitor.alerts.config.ConditionBean;
public class ConverterValidatorManager {
private static AvailabilityConverterValidator availabilityConverter;
private static EventsConverterValidator eventsConverter;
private static MeasurementConverterValidator measurementConverter;
+ private static CallTimeDataConverterValidator calltimeConverter;
private static OperationConverterValidator operationConverter;
private static TraitConverterValidator traitConverter;
private static ResourceConfigurationConverterValidator
resourceConfigurationConverter;
@@ -37,6 +39,7 @@ public class ConverterValidatorManager {
availabilityConverter = new AvailabilityConverterValidator();
eventsConverter = new EventsConverterValidator();
measurementConverter = new MeasurementConverterValidator();
+ calltimeConverter = new CallTimeDataConverterValidator();
operationConverter = new OperationConverterValidator();
traitConverter = new TraitConverterValidator();
resourceConfigurationConverter = new ResourceConfigurationConverterValidator();
@@ -49,6 +52,8 @@ public class ConverterValidatorManager {
measurementConverter.exportProperties(subject, fromBean, toCondition);
} else if (fromBean.getTrigger().equals(traitConverter.getTriggerName())) {
traitConverter.exportProperties(subject, fromBean, toCondition);
+ } else if (fromBean.getTrigger().equals(calltimeConverter.getTriggerName())) {
+ calltimeConverter.exportProperties(subject, fromBean, toCondition);
} else if (fromBean.getTrigger().equals(eventsConverter.getTriggerName())) {
eventsConverter.exportProperties(subject, fromBean, toCondition);
} else if (fromBean.getTrigger().equals(availabilityConverter.getTriggerName()))
{
@@ -70,7 +75,13 @@ public class ConverterValidatorManager {
if ((category == AlertConditionCategory.THRESHOLD) || (category ==
AlertConditionCategory.BASELINE)
|| (category == AlertConditionCategory.CHANGE)) {
- measurementConverter.importProperties(subject, fromCondition, toBean);
+ DataType measDataType = null;
+ if (fromCondition.getMeasurementDefinition() != null)
+ measDataType = fromCondition.getMeasurementDefinition().getDataType();
+ if (DataType.CALLTIME.equals(measDataType))
+ calltimeConverter.importProperties(subject, fromCondition, toBean);
+ else
+ measurementConverter.importProperties(subject, fromCondition, toBean);
} else if (category == AlertConditionCategory.TRAIT) {
traitConverter.importProperties(subject, fromCondition, toBean);
} else if (category == AlertConditionCategory.EVENT) {
@@ -90,6 +101,8 @@ public class ConverterValidatorManager {
public static boolean validate(ConditionBean bean, ActionErrors errors, int index) {
if (bean.getTrigger().equals(measurementConverter.getTriggerName())) {
return measurementConverter.validate(bean, errors, index);
+ } else if (bean.getTrigger().equals(calltimeConverter.getTriggerName())) {
+ return calltimeConverter.validate(bean, errors, index);
} else if (bean.getTrigger().equals(traitConverter.getTriggerName())) {
return traitConverter.validate(bean, errors, index);
} else if (bean.getTrigger().equals(eventsConverter.getTriggerName())) {
@@ -107,7 +120,6 @@ public class ConverterValidatorManager {
}
public static void setDefaults(ConditionBean bean) {
- bean.setTrigger(measurementConverter.getTriggerName());
bean.setThresholdType(MeasurementConverterValidator.TYPE_ABS);
}
}
\ No newline at end of file
diff --git
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/subsystem/SubsystemAlertHistoryUIBean.java
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/subsystem/SubsystemAlertHistoryUIBean.java
index 0ad0c7a..3483950 100644
---
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/subsystem/SubsystemAlertHistoryUIBean.java
+++
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/subsystem/SubsystemAlertHistoryUIBean.java
@@ -33,6 +33,7 @@ import org.rhq.core.domain.alert.AlertConditionCategory;
import org.rhq.core.domain.alert.AlertConditionLog;
import org.rhq.core.domain.alert.composite.AlertHistoryComposite;
import org.rhq.core.domain.auth.Subject;
+import org.rhq.core.domain.measurement.DataType;
import org.rhq.core.domain.measurement.util.MeasurementConverter;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.util.PageControl;
@@ -67,12 +68,12 @@ public class SubsystemAlertHistoryUIBean extends SubsystemView {
private SelectItem[] categoryFilterItems;
private static final IntExtractor<AlertHistoryComposite> RESOURCE_ID_EXTRACTOR
= new IntExtractor<AlertHistoryComposite>() {
-
+
public int extract(AlertHistoryComposite object) {
return object.getAlert().getAlertDefinition().getResource().getId();
}
};
-
+
public SubsystemAlertHistoryUIBean() {
datePattern =
EnterpriseFacesContextUtility.getWebUser().getWebPreferences().getDateTimeDisplayPreferences()
.getDateTimeFormatTrigger();
@@ -221,7 +222,8 @@ public class SubsystemAlertHistoryUIBean extends SubsystemView {
String displayText =
AlertDefUtil.formatAlertConditionForDisplay(condition, request);
String firedValue = log.getValue();
- if (condition.getMeasurementDefinition() != null) {
+ if (condition.getMeasurementDefinition() != null
+ &&
!condition.getMeasurementDefinition().getDataType().equals(DataType.CALLTIME)) {
firedValue =
MeasurementConverter.format(Double.valueOf(log.getValue()), condition
.getMeasurementDefinition().getUnits(), true);
}
@@ -243,7 +245,7 @@ public class SubsystemAlertHistoryUIBean extends SubsystemView {
protected IntExtractor<AlertHistoryComposite> getResourceIdExtractor() {
return RESOURCE_ID_EXTRACTOR;
}
-
+
private void getDataFromRequest() {
SubsystemAlertHistoryUIBean outer = SubsystemAlertHistoryUIBean.this;
outer.resourceFilter =
FacesContextUtility.getOptionalRequestParameter(FORM_PREFIX +
"resourceFilter");
@@ -269,4 +271,4 @@ public class SubsystemAlertHistoryUIBean extends SubsystemView {
return null;
}
}
-}
\ No newline at end of file
+}
diff --git
a/modules/enterprise/gui/portal-war/src/main/webapp-filtered/WEB-INF/classes/ApplicationResources.properties
b/modules/enterprise/gui/portal-war/src/main/webapp-filtered/WEB-INF/classes/ApplicationResources.properties
index 8132311..bae0844 100644
---
a/modules/enterprise/gui/portal-war/src/main/webapp-filtered/WEB-INF/classes/ApplicationResources.properties
+++
b/modules/enterprise/gui/portal-war/src/main/webapp-filtered/WEB-INF/classes/ApplicationResources.properties
@@ -2055,6 +2055,7 @@ alert.config.props.CB.Content.Filter=Filter notification actions
that are define
alert.config.props.CB.ActualValue=Actual Value:
alert.config.props.CB.NoActualValue=Not Available
alert.config.props.CB.Content.Metric=Metric:
+alert.config.props.CB.Content.Calltime=Call-time metric:
alert.config.props.CB.Content.Trait=Trait:
alert.config.props.CB.Content.ResourceConfiguration=Resource Configuration
alert.config.props.CB.Content.CustomProperty=Inventory Property:
@@ -2068,6 +2069,8 @@ alert.config.props.CB.Content.WithStatus=with status:
alert.config.props.CB.Content.Is=is
alert.config.props.CB.Content.AbsoluteValue=(absolute value)
alert.config.props.CB.Content.Changes=value changes
+alert.config.props.CB.Content.AtLeast=by at least
+alert.config.props.CB.Content.CallDestMatches=and call destination matches expression
(optional)
alert.config.props.CB.Content.Match=and matches expression (optional):
alert.config.props.CB.Content.Log=Events/Logs Level:
alert.config.props.CB.Content.Recalculate=Recalculate...
@@ -2075,6 +2078,13 @@ alert.config.props.CB.Content.Percent=% of
alert.config.props.CB.Content.Comparator.>= > (Greater than)
alert.config.props.CB.Content.Comparator.<= < (Less than)
alert.config.props.CB.Content.Comparator.\== = (Equal to)
+alert.config.props.CB.Content.CalltimeOptions.MAX=Maximum
+alert.config.props.CB.Content.CalltimeOptions.MIN=Minimum
+alert.config.props.CB.Content.CalltimeOptions.AVG=Average
+alert.config.props.CB.Content.CalltimeOptions.COUNT=Count
+alert.config.props.CB.Content.CalltimeOperators.LO=shrinks
+alert.config.props.CB.Content.CalltimeOperators.CH=changes
+alert.config.props.CB.Content.CalltimeOperators.HI=grows
alert.config.props.CB.Content.ActionFilters=Action Filters:
alert.config.props.CB.Content.Disregard=Disregard operations that are defined for related
alerts.
@@ -2195,6 +2205,7 @@ events.alert.AlertNotificationActionTitle=Alert Notification Action
alert.config.error.InvalidEmailAddress=The email addresses {0} has invalid syntax: {1}.
alert.config.error.InvalidEmailAddresses=One or more of the email addresses entered had
invalid syntax: {0}.
alert.config.error.NoMetricSelected=You must select a metric for this condition.
+alert.config.error.CalltimePatternError=You must enter a valid Java regular expression.
alert.config.error.NoCustomPropertySelected=You must select an inventory property for
this condition.
alert.config.error.NoControlActionSelected=You must select an operation for this
condition.
alert.config.error.NoControlActionStatusSelected=You must select an operation status for
this condition.
diff --git
a/modules/enterprise/gui/portal-war/src/main/webapp/resource/common/monitor/alerts/config/DefinitionCondition.jsp
b/modules/enterprise/gui/portal-war/src/main/webapp/resource/common/monitor/alerts/config/DefinitionCondition.jsp
index cb76ebe..5ddc201 100644
---
a/modules/enterprise/gui/portal-war/src/main/webapp/resource/common/monitor/alerts/config/DefinitionCondition.jsp
+++
b/modules/enterprise/gui/portal-war/src/main/webapp/resource/common/monitor/alerts/config/DefinitionCondition.jsp
@@ -17,15 +17,15 @@
<td width="20%" class="BlockLabel">
<b><fmt:message
key="alert.config.props.CB.IfCondition"/></b>
</td>
-
+
<c:if test="${showMetrics}" > <!-- begin conditional metric display
logic -->
-
+
<logic:messagesPresent property="condition[${i}].metricId"><td
width="80%" class="ErrorField"></logic:messagesPresent>
<logic:messagesNotPresent property="condition[${i}].metricId"><td
width="80%" class="BlockContent"></logic:messagesNotPresent>
<html:radio property="condition[${i}].trigger"
value="onMeasurement"/>
<fmt:message key="alert.config.props.CB.Content.Metric"/>
<c:set var="seldd"><fmt:message
key="alert.dropdown.SelectOption"/></c:set>
- <html:select property="condition[${i}].metricId"
+ <html:select property="condition[${i}].metricId"
onchange="javascript:selectMetric('condition[${i}].metricId',
'condition[${i}].metricName');changeDropDown('condition[${i}].metricId',
'condition[${i}].baselineOption', '${seldd}');">
<html:option value="-1"
key="alert.dropdown.SelectOption"/>
<html:optionsCollection property="metrics"
label="displayName" value="id"/>
@@ -40,14 +40,14 @@
<tr>
<td class="BlockLabel"> </td>
<td class="BlockContent">
-
+
<table width="100%" border="0" cellspacing="0"
cellpadding="2">
- <tr>
+ <tr>
<td nowrap="true"><div style="width: 60px; position:
relative;"/><html:img page="/images/schedule_return.gif"
width="17" height="21" border="0"
align="right"/></td>
<logic:messagesPresent
property="condition[${i}].absoluteValue"><td width="100%"
class="ErrorField"></logic:messagesPresent>
<logic:messagesNotPresent
property="condition[${i}].absoluteValue"><td
width="100%"></logic:messagesNotPresent>
<html:radio property="condition[${i}].thresholdType"
value="absolute"/>
-
+
<fmt:message key="alert.config.props.CB.Content.Is"/>
<html:select property="condition[${i}].absoluteComparator">
<hq:optionMessageList property="comparators"
baseKey="alert.config.props.CB.Content.Comparator" filter="true"/>
@@ -58,7 +58,7 @@
</logic:messagesPresent>
</td>
</tr>
- <tr>
+ <tr>
<td> </td>
<logic:messagesPresent
property="condition[${i}].percentage"><c:set
var="percentageErrs" value="true"/></logic:messagesPresent>
<logic:messagesPresent
property="condition[${i}].baselineOption"><c:set
var="baselineOptionErrs"
value="true"/></logic:messagesPresent>
@@ -86,7 +86,7 @@
var baselineOption = '<c:out
value="${NewAlertDefinitionForm.conditions[i].baselineOption}"/>';
changeDropDown('condition[<c:out
value="${i}"/>].metricId', 'condition[<c:out
value="${i}"/>].baselineOption', '<c:out
value="${seldd}"/>', baselineOption);
</script>
- </c:if>
+ </c:if>
<c:if test="${percentageErrs || baselineOptionErrs}">
<span class="ErrorFieldContent">
<c:if test="${percentageErrs}"><br>- <html:errors
property="condition[${i}].percentage"/></c:if>
@@ -103,7 +103,98 @@
</td>
</tr>
</table>
-
+
+ </td>
+</tr>
+
+<tr>
+ <td class="BlockLabel"> </td>
+
+</c:if> <!-- end conditional metric display logic -->
+<c:if test="${showCalltimeMetrics}" > <!-- begin conditional call-time
metric display logic -->
+
+ <logic:messagesPresent property="condition[${i}].metricId"><td
width="80%" class="ErrorField"></logic:messagesPresent>
+ <logic:messagesNotPresent property="condition[${i}].metricId"><td
width="80%" class="BlockContent"></logic:messagesNotPresent>
+ <html:radio property="condition[${i}].trigger"
value="onCallTime"/>
+ <fmt:message key="alert.config.props.CB.Content.Calltime"/>
+ <c:set var="seldd"><fmt:message
key="alert.dropdown.SelectOption"/></c:set>
+ <html:select property="condition[${i}].metricId">
+ <html:option value="-1"
key="alert.dropdown.SelectOption"/>
+ <html:optionsCollection property="calltimeMetrics"
label="displayName" value="id"/>
+ </html:select>
+ <logic:messagesPresent property="condition[${i}].metricId">
+ <span class="ErrorFieldContent">- <html:errors
property="condition[${i}].metricId"/></span>
+ </logic:messagesPresent>
+ </td>
+</tr>
+
+<tr>
+ <td class="BlockLabel"> </td>
+ <td class="BlockContent">
+
+ <table width="100%" border="0" cellspacing="0"
cellpadding="2">
+ <tr>
+ <td nowrap="true"><div style="width: 60px; position:
relative;"/><html:img page="/images/schedule_return.gif"
width="17" height="21" border="0"
align="right"/></td>
+ <logic:messagesPresent
property="condition[${i}].ctAbsoluteValue"><c:set
var="ctAbsValueErrs" value="true"/></logic:messagesPresent>
+ <logic:messagesPresent
property="condition[${i}].calltimeAbsPattern"><c:set
var="callDestAbsPatternErrs"
value="true"/></logic:messagesPresent>
+ <c:choose>
+ <c:when test="${ctAbsValueErrs || callDestAbsPatternErrs}"><td
width="100%" class="ErrorField"></c:when>
+ <c:otherwise><td width="100%"></c:otherwise>
+ </c:choose>
+ <logic:messagesPresent
property="condition[${i}].calltimeOption"><c:set
var="calltimeOptionErrs"
value="true"/></logic:messagesPresent>
+ <html:radio property="condition[${i}].thresholdType"
value="absolute"/>
+
+ <html:select property="condition[${i}].calltimeAbsOption">
+ <hq:optionMessageList property="calltimeOptions"
baseKey="alert.config.props.CB.Content.CalltimeOptions"
filter="true"/>
+ </html:select>
+ <fmt:message key="alert.config.props.CB.Content.Is"/>
+ <html:select property="condition[${i}].calltimeComparator">
+ <hq:optionMessageList property="comparators"
baseKey="alert.config.props.CB.Content.Comparator" filter="true"/>
+ </html:select>
+ <html:text property="condition[${i}].ctAbsoluteValue"
size="8" maxlength="15"/> <fmt:message
key="alert.config.props.CB.Content.AbsoluteValue"/>
+ <fmt:message
key="alert.config.props.CB.Content.CallDestMatches"/>
+ <html:text property="condition[${i}].calltimeAbsPattern"
size="8" maxlength="15"/>
+ <c:if test="${ctAbsValueErrs || callDestAbsPatternErrs}">
+ <span class="ErrorFieldContent">
+ <c:if test="${ctAbsValueErrs}"><br>- <html:errors
property="condition[${i}].ctAbsoluteValue"/></c:if>
+ <c:if test="${callDestAbsPatternErrs}"><br>-
<html:errors property="condition[${i}].calltimeAbsPattern"/></c:if>
+ </span>
+ </c:if>
+ </td>
+ </tr>
+ <tr>
+ <td> </td>
+ <logic:messagesPresent
property="condition[${i}].ctPercentage"><c:set var="ctPercErrs"
value="true"/></logic:messagesPresent>
+ <logic:messagesPresent
property="condition[${i}].calltimeChgPattern"><c:set
var="callDestChgPatternErrs"
value="true"/></logic:messagesPresent>
+ <c:choose>
+ <c:when test="${ctPercErrs || callDestChgPatternErrs}"><td
width="100%" class="ErrorField"></c:when>
+ <c:otherwise><td width="100%"></c:otherwise>
+ </c:choose>
+ <logic:messagesPresent
property="condition[${i}].calltimeOption"><c:set
var="calltimeOptionErrs"
value="true"/></logic:messagesPresent>
+ <html:radio property="condition[${i}].thresholdType"
value="changed"/>
+ <html:select property="condition[${i}].calltimeChgOption">
+ <hq:optionMessageList property="calltimeOptions"
baseKey="alert.config.props.CB.Content.CalltimeOptions"
filter="true"/>
+ </html:select>
+ <html:select property="condition[${i}].calltimeChgOp">
+ <hq:optionMessageList property="calltimeOperators"
baseKey="alert.config.props.CB.Content.CalltimeOperators"
filter="true"/>
+ </html:select>
+ <fmt:message
key="alert.config.props.CB.Content.AtLeast"/>
+ <html:text property="condition[${i}].ctPercentage"
size="6" maxlength="6"/> %
+ <logic:messagesPresent
property="condition[${i}].ctPercentage">
+ <br><span class="ErrorFieldContent">- <html:errors
property="condition[${i}].percentage"/></span>
+ </logic:messagesPresent>
+ <fmt:message
key="alert.config.props.CB.Content.CallDestMatches"/>
+ <html:text property="condition[${i}].calltimeChgPattern"
size="8" maxlength="15"/>
+ <c:if test="${ctPercErrs || callDestChgPatternErrs}">
+ <span class="ErrorFieldContent">
+ <c:if test="${ctPercErrs}"><br>- <html:errors
property="condition[${i}].ctPercentage"/></c:if>
+ <c:if test="${callDestChgPatternErrs}"><br>-
<html:errors property="condition[${i}].calltimeChgPattern"/></c:if>
+ </span>
+ </c:if>
+ </td>
+ </tr>
+ </table>
+
</td>
</tr>
@@ -111,12 +202,13 @@
<td class="BlockLabel"> </td>
</c:if> <!-- end conditional metric display logic -->
+
<c:if test="${showTraits}" > <!-- begin conditional traits display
logic -->
-
+
<logic:messagesPresent property="condition[${i}].traitStatus">
<c:set var="traitStatusErrs" value="true"/>
</logic:messagesPresent>
-
+
<c:choose>
<c:when test="${traitStatusErrs}">
<td class="ErrorField">
@@ -125,7 +217,7 @@
<td class="BlockContent">
</c:otherwise>
</c:choose>
-
+
<html:radio property="condition[${i}].trigger"
value="onTrait"/>
<fmt:message key="alert.config.props.CB.Content.Trait"/>
<html:select property="condition[${i}].traitId"
@@ -134,28 +226,28 @@
<html:optionsCollection property="traits"
label="displayName" value="id"/>
</html:select>
<fmt:message key="alert.config.props.CB.Content.Changes"/>
-
+
<c:if test="${traitStatusErrs}">
<br>
<span class="ErrorFieldContent">- <html:errors
property="condition[${i}].traitStatus"/></span>
</c:if>
-
+
<html:hidden property="condition[${i}].traitName"/>
-
+
</td>
</tr>
<tr>
<td class="BlockLabel"> </td>
-
+
</c:if> <!-- end conditional traits display logic -->
<c:if test="${showResourceConfiguration}" > <!-- begin conditional
resource config display logic -->
-
+
<logic:messagesPresent
property="condition[${i}].resourceConfigurationStatus">
<c:set var="resourceConfigurationStatusErrs"
value="true"/>
</logic:messagesPresent>
-
+
<c:choose>
<c:when test="${resourceConfigurationStatusErrs}">
<td class="ErrorField">
@@ -164,7 +256,7 @@
<td class="BlockContent">
</c:otherwise>
</c:choose>
-
+
<html:radio property="condition[${i}].trigger"
value="onResourceConfiguration"/>
<fmt:message
key="alert.config.props.CB.Content.ResourceConfiguration"/>
<fmt:message key="alert.config.props.CB.Content.Changes"/>
@@ -174,14 +266,14 @@
<tr>
<td class="BlockLabel"> </td>
-
+
</c:if> <!-- end conditional resource config display logic -->
<c:if test="${showAvailability}"> <!-- begin conditional
availabilityOptions display logic -->
-
+
<logic:messagesPresent property="condition[${i}].availabilityStatus">
<c:set var="availabilityStatusErrs" value="true"/>
</logic:messagesPresent>
-
+
<c:choose>
<c:when test="${availabilityStatusErrs}">
<td class="ErrorField">
@@ -190,14 +282,14 @@
<td class="BlockContent">
</c:otherwise>
</c:choose>
-
+
<html:radio property="condition[${i}].trigger"
value="onAvailability"/>
<fmt:message
key="alert.config.props.CB.Content.Availability"/>
<html:select property="condition[${i}].availability">
<html:option value=""
key="alert.dropdown.SelectOption"/>
<html:optionsCollection property="availabilityOptions"/>
</html:select>
-
+
<c:if test="${availabilityStatusErrs}">
<br>
<span class="ErrorFieldContent">- <html:errors
property="condition[${i}].availabilityStatus"/></span>
@@ -207,10 +299,10 @@
<tr>
<td class="BlockLabel"> </td>
-
+
</c:if> <!-- end conditional availabilityOptions display logic -->
<c:if test="${showOperations}"> <!-- begin conditional controlActions
display logic -->
-
+
<logic:messagesPresent property="condition[${i}].controlAction">
<c:set var="controlActionErrs" value="true"/>
</logic:messagesPresent>
@@ -247,10 +339,10 @@
<tr>
<td class="BlockLabel"> </td>
-
+
</c:if> <!-- end conditional controlActions display logic -->
<c:if test="${showEvents}"> <!-- begin conditional events display
logic -->
-
+
<logic:messagesPresent property="condition[${i}].eventSeverity">
<c:set var="eventSeverityErrs" value="true"/>
</logic:messagesPresent>
@@ -294,10 +386,10 @@
<tr>
<td class="BlockLabel"> </td>
-</c:if> <!-- end conditional events display logic -->
+</c:if> <!-- end conditional events display logic -->
<td class="BlockContent">
-
+
<html:link href="javascript:document.${formName}.submit()"
onclick="clickRemove('${formName}', '${i}');"
titleKey="alert.config.props.CB.Delete">
@@ -315,11 +407,11 @@
<tr>
<td class="BlockLabel"> </td>
-</c:if> <!-- end conditional events display logic -->
+</c:if> <!-- end conditional events display logic -->
<c:if test="${numConditions != 1}">
<tr>
- <td class="BlockLabel"> </td>
+ <td class="BlockLabel"> </td>
</c:if>
<td class="BlockContent">
<html:hidden property="addingCondition" value="false"/>
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/AlertConditionCacheManagerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/AlertConditionCacheManagerBean.java
index fd6d8d3..5ca45b3 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/AlertConditionCacheManagerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/AlertConditionCacheManagerBean.java
@@ -33,6 +33,7 @@ import org.rhq.core.domain.event.Event;
import org.rhq.core.domain.event.EventSource;
import org.rhq.core.domain.measurement.Availability;
import org.rhq.core.domain.measurement.MeasurementData;
+import org.rhq.core.domain.measurement.calltime.CallTimeData;
import org.rhq.core.domain.operation.OperationHistory;
import org.rhq.core.domain.resource.Agent;
import org.rhq.enterprise.server.alert.engine.internal.AlertConditionCacheCoordinator;
@@ -60,6 +61,12 @@ public class AlertConditionCacheManagerBean implements
AlertConditionCacheManage
return stats;
}
+ public AlertConditionCacheStats checkConditions(CallTimeData... callTime) {
+ AlertConditionCacheStats stats;
+ stats = AlertConditionCacheCoordinator.getInstance().checkConditions(callTime);
+ return stats;
+ }
+
public AlertConditionCacheStats checkConditions(OperationHistory operationHistory) {
AlertConditionCacheStats stats;
stats =
AlertConditionCacheCoordinator.getInstance().checkConditions(operationHistory);
@@ -102,4 +109,4 @@ public class AlertConditionCacheManagerBean implements
AlertConditionCacheManage
}
}
-}
\ No newline at end of file
+}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/AlertConditionCacheManagerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/AlertConditionCacheManagerLocal.java
index eaf2a0d..d8d64c4 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/AlertConditionCacheManagerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/AlertConditionCacheManagerLocal.java
@@ -29,6 +29,7 @@ import org.rhq.core.domain.event.Event;
import org.rhq.core.domain.event.EventSource;
import org.rhq.core.domain.measurement.Availability;
import org.rhq.core.domain.measurement.MeasurementData;
+import org.rhq.core.domain.measurement.calltime.CallTimeData;
import org.rhq.core.domain.operation.OperationHistory;
/**
@@ -49,6 +50,14 @@ public interface AlertConditionCacheManagerLocal {
AlertConditionCacheStats checkConditions(MeasurementData... measurementData);
/**
+ * A MeasurementReport is full of CallTimeData objects. Each of these could
potentially match against one of the
+ * cache's conditions. So, each must be checked against it, to see whether it
fires against any of the conditions.
+ *
+ * @return the number of conditions that were true against this argument
+ */
+ AlertConditionCacheStats checkConditions(CallTimeData... callTime);
+
+ /**
* Operation history occurs in two distinct phases. The first is when the operation
is first triggered. An
* OperationHistory element gets persisted whose status is INPROGRESS. The request is
sent down to some agent that
* carries out the requested work. Each agent will send a response back out-of-band
and update the operation
@@ -73,7 +82,7 @@ public interface AlertConditionCacheManagerLocal {
/**
* An EventReport is full of Event objects. Each of these could potentially match
against one of the cache's
* conditions. So, each must be checked against it, to see whether it fires against
any of the conditions.
- *
+ *
* @return the number of conditions that were true against this argument
*/
AlertConditionCacheStats checkConditions(EventSource source, Event... events);
@@ -81,7 +90,7 @@ public interface AlertConditionCacheManagerLocal {
/**
* ResourceConfiguration changes can be made through the UI or on the managed
resource. Whenever a new
* ResourceConfigurationUpdate is created, it should be passed to this method
- *
+ *
* @return the number of conditions that were true against this argument
*/
AlertConditionCacheStats checkConditions(ResourceConfigurationUpdate update);
@@ -91,4 +100,4 @@ public interface AlertConditionCacheManagerLocal {
void reloadGlobalCache();
void reloadAllCaches();
-}
\ No newline at end of file
+}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/internal/AbstractConditionCache.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/internal/AbstractConditionCache.java
index 00f6950..74f59f0 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/internal/AbstractConditionCache.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/internal/AbstractConditionCache.java
@@ -66,7 +66,8 @@ abstract class AbstractConditionCache {
*/
cacheElement.setActive(true); // no harm to always set active
(though, technically, STATELESS operators don't need it)
cachedConditionProducer.sendActivateAlertConditionMessage(
- cacheElement.getAlertConditionTriggerId(), timestamp,
providedValue, extraParams);
+ cacheElement.getAlertConditionTriggerId(), timestamp,
cacheElement
+ .convertValueToString(providedValue), extraParams);
stats.matched++;
} catch (Exception e) {
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/internal/AgentConditionCache.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/internal/AgentConditionCache.java
index 2e37c34..dc84ae6 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/internal/AgentConditionCache.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/internal/AgentConditionCache.java
@@ -18,10 +18,14 @@
*/
package org.rhq.enterprise.server.alert.engine.internal;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.EnumSet;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.concurrent.TimeUnit;
import org.rhq.core.domain.alert.AlertCondition;
import org.rhq.core.domain.alert.AlertConditionCategory;
@@ -35,9 +39,13 @@ import org.rhq.core.domain.auth.Subject;
import org.rhq.core.domain.event.Event;
import org.rhq.core.domain.event.EventSeverity;
import org.rhq.core.domain.event.EventSource;
+import org.rhq.core.domain.measurement.DataType;
import org.rhq.core.domain.measurement.MeasurementData;
import org.rhq.core.domain.measurement.MeasurementDataNumeric;
import org.rhq.core.domain.measurement.MeasurementDataTrait;
+import org.rhq.core.domain.measurement.MeasurementDefinition;
+import org.rhq.core.domain.measurement.calltime.CallTimeData;
+import org.rhq.core.domain.measurement.calltime.CallTimeDataValue;
import org.rhq.core.domain.resource.Resource;
import org.rhq.core.domain.util.PageControl;
import org.rhq.core.domain.util.PageList;
@@ -46,12 +54,14 @@ import
org.rhq.enterprise.server.alert.engine.AlertConditionCacheStats;
import
org.rhq.enterprise.server.alert.engine.internal.AlertConditionCacheCoordinator.Cache;
import org.rhq.enterprise.server.alert.engine.mbean.AlertConditionCacheMonitor;
import org.rhq.enterprise.server.alert.engine.model.AlertConditionOperator;
+import org.rhq.enterprise.server.alert.engine.model.CallTimeDataCacheElement;
import org.rhq.enterprise.server.alert.engine.model.EventCacheElement;
import org.rhq.enterprise.server.alert.engine.model.InvalidCacheElementException;
import org.rhq.enterprise.server.alert.engine.model.MeasurementBaselineCacheElement;
import org.rhq.enterprise.server.alert.engine.model.MeasurementNumericCacheElement;
import org.rhq.enterprise.server.alert.engine.model.MeasurementTraitCacheElement;
import org.rhq.enterprise.server.alert.engine.model.NumericDoubleCacheElement;
+import
org.rhq.enterprise.server.alert.engine.model.CallTimeDataCacheElement.CallTimeElementValue;
import org.rhq.enterprise.server.auth.SubjectManagerLocal;
import org.rhq.enterprise.server.common.EntityManagerFacadeLocal;
import org.rhq.enterprise.server.measurement.MeasurementDataManagerLocal;
@@ -64,6 +74,7 @@ class AgentConditionCache extends AbstractConditionCache {
private Map<Integer, List<NumericDoubleCacheElement>>
measurementDataCache;
private Map<Integer, List<MeasurementTraitCacheElement>>
measurementTraitCache;
+ private Map<Integer, List<CallTimeDataCacheElement>> callTimeCache;
private Map<Integer, List<EventCacheElement>> eventsCache;
private AlertConditionManagerLocal alertConditionManager;
@@ -80,6 +91,7 @@ class AgentConditionCache extends AbstractConditionCache {
measurementDataCache = new HashMap<Integer,
List<NumericDoubleCacheElement>>();
measurementTraitCache = new HashMap<Integer,
List<MeasurementTraitCacheElement>>();
+ callTimeCache = new HashMap<Integer,
List<CallTimeDataCacheElement>>();
eventsCache = new HashMap<Integer, List<EventCacheElement>>();
alertConditionManager = LookupUtil.getAlertConditionManager();
@@ -93,9 +105,9 @@ class AgentConditionCache extends AbstractConditionCache {
/**
* This method is used to do the initial loading from the database for a particular
agent. In the high availability
- * infrastructure each server instance in the cloud will only be responsible for
monitoring a select number of
+ * infrastructure each server instance in the cloud will only be responsible for
monitoring a select number of
* agents at any given point in time. When an agent makes a connection to this server
instance, the caches for that
- * agent will be loaded at that time.
+ * agent will be loaded at that time.
*
* @return the number of conditions that re/loaded
*/
@@ -160,20 +172,54 @@ class AgentConditionCache extends AbstractConditionCache {
AlertCondition alertCondition = composite.getCondition();
int alertConditionId = alertCondition.getId(); // auto-unboxing is safe here
because as the PK it's guaranteed to be non-null
+ MeasurementDefinition mdef = alertCondition.getMeasurementDefinition();
AlertConditionCategory alertConditionCategory = alertCondition.getCategory();
AlertConditionOperator alertConditionOperator =
AlertConditionCacheUtils.getAlertConditionOperator(
alertConditionCategory, alertCondition.getComparator(),
alertCondition.getOption());
- if (alertConditionCategory == AlertConditionCategory.BASELINE) {
+ if (mdef != null && mdef.getDataType().equals(DataType.CALLTIME)) { //
call-time cases start here
+ if (alertConditionCategory == AlertConditionCategory.CHANGE) {
+ AlertConditionChangesCategoryComposite changesComposite =
(AlertConditionChangesCategoryComposite) composite;
+ int scheduleId = changesComposite.getScheduleId();
+
+ try {
+ CallTimeDataCacheElement cacheElement = new
CallTimeDataCacheElement(alertConditionOperator,
+ CallTimeElementValue.valueOf(alertCondition.getOption()),
alertCondition.getComparator(),
+ alertCondition.getThreshold(), alertConditionId,
alertCondition.getName());
+
+ addTo("measurementDataCache", callTimeCache, scheduleId,
cacheElement, alertConditionId, stats);
+ } catch (InvalidCacheElementException icee) {
+ log.info("Failed to create NumericDoubleCacheElement with
parameters: "
+ +
AlertConditionCacheUtils.getCacheElementErrorString(alertConditionId,
alertConditionOperator,
+ null, alertCondition.getThreshold()));
+ }
+ } else if (alertConditionCategory == AlertConditionCategory.THRESHOLD) {
+ AlertConditionScheduleCategoryComposite thresholdComposite =
(AlertConditionScheduleCategoryComposite) composite;
+
+ try {
+ CallTimeDataCacheElement cacheElement = new
CallTimeDataCacheElement(alertConditionOperator,
+ CallTimeElementValue.valueOf(alertCondition.getOption()), null,
alertCondition.getThreshold(),
+ alertConditionId, alertCondition.getName());
+
+ addTo("measurementDataCache", callTimeCache,
thresholdComposite.getScheduleId(), cacheElement,
+ alertConditionId, stats);
+ } catch (InvalidCacheElementException icee) {
+ log.info("Failed to create NumberDoubleCacheElement with
parameters: "
+ +
AlertConditionCacheUtils.getCacheElementErrorString(alertConditionId,
alertConditionOperator,
+ null, alertCondition.getThreshold()));
+ }
+
+ }// last call-time case
+ } else if (alertConditionCategory == AlertConditionCategory.BASELINE) { // normal
cases start here
AlertConditionBaselineCategoryComposite baselineComposite =
(AlertConditionBaselineCategoryComposite) composite;
// option status for baseline gets set to "mean", but it's
rather useless since the UI
// current doesn't allow alerting off of other baseline properties such
as "min" and "max"
Double threshold = alertCondition.getThreshold();
String optionStatus = alertCondition.getOption();
- /*
- * yes, calculatedValue may be null, but that's OK because the match
+ /*
+ * yes, calculatedValue may be null, but that's OK because the match
* method for MeasurementBaselineCacheElement handles nulls just fine
*/
Double calculatedValue = getCalculatedBaselineValue(alertConditionId,
baselineComposite, optionStatus,
@@ -212,7 +258,7 @@ class AgentConditionCache extends AbstractConditionCache {
String value = traitsComposite.getValue();
try {
- /*
+ /*
* don't forget special defensive handling to allow for null trait
calculation;
* this might happen if a newly committed resource has some alert
template applied to
* it for some trait that it has not yet gotten from the agent
@@ -308,6 +354,84 @@ class AgentConditionCache extends AbstractConditionCache {
return stats;
}
+ public AlertConditionCacheStats checkConditions(CallTimeData... callTime) {
+ if ((callTime == null) || (callTime.length == 0)) {
+ return new AlertConditionCacheStats();
+ }
+ AlertConditionCacheStats stats = new AlertConditionCacheStats();
+ HashMap<Integer, HashMap<String, ArrayList<CallTimeDataValue>>>
order = produceOrderedCallTimeDataStructure(callTime);
+ for (Integer scheduleId : order.keySet()) {
+ List<? extends CallTimeDataCacheElement> conditionCacheElements =
lookupCallTimeDataCacheElements(scheduleId);
+ for (String callDest : order.get(scheduleId).keySet())
+ for (CallTimeDataValue provided : order.get(scheduleId).get(callDest)) {
+ processCacheElements(conditionCacheElements, provided,
provided.getBeginTime(), stats, callDest);
+ }
+ }
+ return stats;
+ }
+
+ private HashMap<Integer, HashMap<String,
ArrayList<CallTimeDataValue>>> produceOrderedCallTimeDataStructure(
+ CallTimeData... callTime) {
+ long beginTime = 0;
+ if (log.isDebugEnabled())
+ beginTime = System.nanoTime();
+
+ //Insert all CallTimeDataValue in data structure
+ HashMap<Integer, HashMap<String, ArrayList<CallTimeDataValue>>>
order = new HashMap<Integer, HashMap<String,
ArrayList<CallTimeDataValue>>>();
+ for (CallTimeData ctd : callTime) {
+ if (!order.containsKey(ctd.getScheduleId()))
+ order.put(ctd.getScheduleId(), new HashMap<String,
ArrayList<CallTimeDataValue>>());
+
+ HashMap<String, ArrayList<CallTimeDataValue>> partialOrder =
order.get(ctd.getScheduleId());
+ for (String callDestination : ctd.getValues().keySet()) {
+ if (!partialOrder.containsKey(callDestination))
+ partialOrder.put(callDestination, new
ArrayList<CallTimeDataValue>());
+
+ ArrayList<CallTimeDataValue> list =
partialOrder.get(callDestination);
+ list.add(ctd.getValues().get(callDestination));
+ }
+ }
+
+ //sort all lists in the data structure
+ for (HashMap<String, ArrayList<CallTimeDataValue>> topList :
order.values())
+ for (ArrayList<CallTimeDataValue> bottomList : topList.values())
+ Collections.sort(bottomList, getCallTimeComparator());
+
+ if (log.isDebugEnabled())
+ log.debug("sorting call-time data during alerting took: "
+ + TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - beginTime) +
"ms");
+ return order;
+ }
+
+ private Comparator<CallTimeDataValue> getCallTimeComparator() {
+ return new Comparator<CallTimeDataValue>() {
+ public int compare(CallTimeDataValue arg0, CallTimeDataValue arg1) {
+ if (arg0 == null || arg1 == null)
+ throw new IllegalArgumentException("Call-time data value entries
must not be null!");
+
+ if (arg0 == arg1)
+ return 0;
+
+ //differing begin times:
+ if (arg0.getBeginTime() < arg1.getBeginTime())
+ return -1;
+ if (arg0.getBeginTime() > arg1.getBeginTime())
+ return 1;
+
+ // begin time equality:
+ if (arg0.getBeginTime() == arg1.getBeginTime()) {
+ if (arg0.getEndTime() == arg1.getEndTime())
+ return 0;
+ if (arg0.getEndTime() < arg1.getEndTime())
+ return -1;
+ if (arg0.getEndTime() > arg1.getEndTime())
+ return 1;
+ }
+ return Integer.MIN_VALUE;
+ }
+ };
+ }
+
public AlertConditionCacheStats checkConditions(EventSource source, Event... events)
{
if ((events == null) || (events.length == 0)) {
return new AlertConditionCacheStats();
@@ -337,6 +461,10 @@ class AgentConditionCache extends AbstractConditionCache {
return measurementDataCache.get(scheduleId); // yup, might be null
}
+ private List<? extends CallTimeDataCacheElement>
lookupCallTimeDataCacheElements(int scheduleId) {
+ return callTimeCache.get(scheduleId); // yup, might be null
+ }
+
private List<MeasurementTraitCacheElement>
lookupMeasurementTraitCacheElements(int scheduleId) {
return measurementTraitCache.get(scheduleId); // yup, might be null
}
@@ -354,7 +482,7 @@ class AgentConditionCache extends AbstractConditionCache {
+ "]: threshold was null");
}
- // auto-unboxing of threshold is safe here
+ // auto-unboxing of threshold is safe here
Double baselineValue = 0.0;
if (optionStatus == null) {
@@ -386,6 +514,8 @@ class AgentConditionCache extends AbstractConditionCache {
return AlertConditionCacheUtils.getMapListCount(measurementDataCache);
} else if (cache == AlertConditionCacheCoordinator.Cache.MeasurementTraitCache)
{
return AlertConditionCacheUtils.getMapListCount(measurementTraitCache);
+ } else if (cache == AlertConditionCacheCoordinator.Cache.CallTimeDataCache) {
+ return AlertConditionCacheUtils.getMapListCount(callTimeCache);
} else if (cache == AlertConditionCacheCoordinator.Cache.EventsCache) {
return AlertConditionCacheUtils.getMapListCount(eventsCache);
} else {
@@ -394,4 +524,4 @@ class AgentConditionCache extends AbstractConditionCache {
}
}
-}
\ No newline at end of file
+}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/internal/AlertConditionCacheCoordinator.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/internal/AlertConditionCacheCoordinator.java
index 4d13534..340849e 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/internal/AlertConditionCacheCoordinator.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/internal/AlertConditionCacheCoordinator.java
@@ -32,6 +32,7 @@ import org.rhq.core.domain.event.Event;
import org.rhq.core.domain.event.EventSource;
import org.rhq.core.domain.measurement.Availability;
import org.rhq.core.domain.measurement.MeasurementData;
+import org.rhq.core.domain.measurement.calltime.CallTimeData;
import org.rhq.core.domain.operation.OperationHistory;
import org.rhq.enterprise.server.alert.engine.AlertConditionCacheStats;
import org.rhq.enterprise.server.alert.engine.model.AbstractCacheElement;
@@ -44,9 +45,9 @@ import org.rhq.enterprise.server.util.LookupUtil;
* reported by an agent, and thus can be perfectly segmented on an agent-by-agent basis.
On the other hand,
* the {@link GlobalConditionCache} will maintain {@link AbstractCacheElement}s for data
that can either be
* agent-side or server-side initiated.
- *
+ *
* This manager forms a centralized interface through which to interact with the children
caches.
- *
+ *
* @author Joseph Marques
*/
public final class AlertConditionCacheCoordinator {
@@ -58,6 +59,7 @@ public final class AlertConditionCacheCoordinator {
public enum Cache {
MeasurementDataCache(Type.Agent), //
MeasurementTraitCache(Type.Agent), //
+ CallTimeDataCache(Type.Agent), //
ResourceOperationCache(Type.Global), //
AvailabilityCache(Type.Global), //
EventsCache(Type.Agent), //
@@ -159,6 +161,37 @@ public final class AlertConditionCacheCoordinator {
return stats;
}
+ public AlertConditionCacheStats checkConditions(CallTimeData... callTime) {
+ if (callTime == null || callTime.length == 0) {
+ return new AlertConditionCacheStats();
+ }
+
+ CallTimeData datum = callTime[0];
+ Integer agentId = getAgentId(datum);
+ if (agentId == null) {
+ log.error("Could not find agent for scheduleId = " +
datum.getScheduleId());
+ return new AlertConditionCacheStats();
+ }
+
+ AlertConditionCacheStats stats = null;
+ AgentConditionCache agentCache = null;
+ agentReadWriteLock.readLock().lock();
+ try {
+ agentCache = agentCaches.get(agentId);
+ } catch (Throwable t) {
+ log.error("Error during checkConditions", t); // don't let any
exceptions bubble up to the calling SLSB layer
+ } finally {
+ agentReadWriteLock.readLock().unlock();
+ }
+ if (agentCache != null) {
+ stats = agentCache.checkConditions(callTime);
+ } else {
+ stats = new AlertConditionCacheStats();
+ }
+
+ return stats;
+ }
+
public AlertConditionCacheStats checkConditions(OperationHistory operationHistory) {
AlertConditionCacheStats stats = null;
try {
@@ -238,9 +271,16 @@ public final class AlertConditionCacheCoordinator {
return null;
}
+ private Integer getAgentId(CallTimeData ctd) {
+ return getAgentId(ctd.getScheduleId());
+ }
+
private Integer getAgentId(MeasurementData datum) {
+ return getAgentId(datum.getScheduleId());
+ }
+
+ private Integer getAgentId(int scheduleId) {
try {
- int scheduleId = datum.getScheduleId();
Integer agentId = agentManager.getAgentIdByScheduleId(scheduleId);
return agentId;
} catch (Throwable t) {
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/jms/CachedConditionProducerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/jms/CachedConditionProducerBean.java
index bc901d4..3341af8 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/jms/CachedConditionProducerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/jms/CachedConditionProducerBean.java
@@ -32,14 +32,13 @@ import javax.jms.Session;
import org.rhq.enterprise.server.alert.engine.jms.model.ActiveAlertConditionMessage;
import org.rhq.enterprise.server.alert.engine.jms.model.InactiveAlertConditionMessage;
-import org.rhq.enterprise.server.alert.engine.model.AbstractCacheElement;
/**
* A convenience class that will be used by the AlertConditionCacheManager to send
messages to a JMS queue for
* processing. These messages reference alert conditions that have triggered (become
true). The contents will be picked
* at on the other end of the non-durable queue by the consumer, which will then create
and persist the appropriate log
* message.
- *
+ *
* @author Joseph Marques
*/
@Stateless
@@ -54,8 +53,8 @@ public class CachedConditionProducerBean implements
CachedConditionProducerLocal
private Queue alertConditionQueue;
@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
- public <T extends AbstractCacheElement<S>, S> void
sendActivateAlertConditionMessage(int alertConditionId,
- long timestamp, S value, Object... extraParams) throws JMSException {
+ public void sendActivateAlertConditionMessage(int alertConditionId, long timestamp,
String value,
+ Object... extraParams) throws JMSException {
Connection connection = factory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
MessageProducer sender = session.createProducer(alertConditionQueue);
@@ -65,7 +64,7 @@ public class CachedConditionProducerBean implements
CachedConditionProducerLocal
* send the string representation of the value for the AbstractCacheElement in
the JMS message
*/
ActiveAlertConditionMessage conditionMessage = new
ActiveAlertConditionMessage(alertConditionId, timestamp,
- value.toString(), extraParams);
+ value, extraParams);
ObjectMessage message = session.createObjectMessage(conditionMessage);
@@ -89,4 +88,4 @@ public class CachedConditionProducerBean implements
CachedConditionProducerLocal
connection.close();
}
-}
\ No newline at end of file
+}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/jms/CachedConditionProducerLocal.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/jms/CachedConditionProducerLocal.java
index d2b4fb5..627e2f7 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/jms/CachedConditionProducerLocal.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/jms/CachedConditionProducerLocal.java
@@ -21,8 +21,6 @@ package org.rhq.enterprise.server.alert.engine.jms;
import javax.ejb.Local;
import javax.jms.JMSException;
-import org.rhq.enterprise.server.alert.engine.model.AbstractCacheElement;
-
/**
* @author Joseph Marques
*/
@@ -33,8 +31,8 @@ public interface CachedConditionProducerLocal {
* Send a message to the appropriate JMS destination for processing. This destination
handles creating and
* persisting entities corresponding to triggered alert conditions.
*/
- <T extends AbstractCacheElement<S>, S> void
sendActivateAlertConditionMessage(int alertConditionId, long timestamp,
- S value, Object... extraParams) throws JMSException;
+ void sendActivateAlertConditionMessage(int alertConditionId, long timestamp, String
value, Object... extraParams)
+ throws JMSException;
void sendDeactivateAlertConditionMessage(int alertConditionId, long timestamp) throws
JMSException;
}
\ No newline at end of file
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/model/AbstractCacheElement.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/model/AbstractCacheElement.java
index b7aae77..6026aeb 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/model/AbstractCacheElement.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/model/AbstractCacheElement.java
@@ -163,6 +163,10 @@ public abstract class AbstractCacheElement<T> {
return getOperatorSupportsType(alertConditionOperator) == type;
}
+ public String convertValueToString(T providedValue) {
+ return providedValue.toString();
+ }
+
@Override
public String toString() {
String conditionValue = null;
@@ -225,4 +229,4 @@ public abstract class AbstractCacheElement<T> {
return true;
}
-}
\ No newline at end of file
+}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerBean.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerBean.java
index b1a849e..bf2de38 100644
---
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerBean.java
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/measurement/MeasurementDataManagerBean.java
@@ -63,6 +63,7 @@ import org.rhq.core.domain.measurement.MeasurementDataTrait;
import org.rhq.core.domain.measurement.MeasurementDefinition;
import org.rhq.core.domain.measurement.MeasurementReport;
import org.rhq.core.domain.measurement.MeasurementSchedule;
+import org.rhq.core.domain.measurement.calltime.CallTimeData;
import org.rhq.core.domain.measurement.composite.MeasurementDataNumericHighLowComposite;
import org.rhq.core.domain.resource.Agent;
import org.rhq.core.domain.resource.Resource;
@@ -171,9 +172,9 @@ public class MeasurementDataManagerBean implements
MeasurementDataManagerLocal,
long start = System.currentTimeMillis();
// TODO GH: Deal with offset (this is only for situations where the clock
doesn't match on the agent)
- /*
+ /*
* even if these methods check for null/empty collections, they cross the EJB
boundary and so unnecessarily
- * start transactions. by checking the null/emptiness of a collection here, by
only create transactions
+ * start transactions. by checking the null/emptiness of a collection here, by
only create transactions
* when real work will be done;
*/
if (report.getNumericData() != null &&
!report.getNumericData().isEmpty()) {
@@ -184,6 +185,7 @@ public class MeasurementDataManagerBean implements
MeasurementDataManagerLocal,
}
if (report.getCallTimeData() != null &&
!report.getCallTimeData().isEmpty()) {
this.callTimeDataManager.addCallTimeData(report.getCallTimeData());
+ this.notifyAlertConditionCacheManager("mergeMeasurementReport",
report.getCallTimeData());
}
long time = System.currentTimeMillis() - start;
@@ -858,4 +860,12 @@ public class MeasurementDataManagerBean implements
MeasurementDataManagerLocal,
return result;
}
+
+ private void notifyAlertConditionCacheManager(String callingMethod,
Set<CallTimeData> callTime) {
+ AlertConditionCacheStats stats =
alertConditionCacheManager.checkConditions(callTime
+ .toArray(new CallTimeData[callTime.size()]));
+
+ if (log.isDebugEnabled())
+ log.debug(callingMethod + ": " + stats.toString());
+ }
}
\ No newline at end of file
commit fef127265c7eca061c27d287f1bf98da37c3be0e
Author: Frank Brueseke <fbrueseke(a)s-lab.upb.de>
Date: Mon Mar 1 20:48:12 2010 +0100
Allow alerting on call-time data. BZ 535756
diff --git
a/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/condition/CallTimeDataConverterValidator.java
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/condition/CallTimeDataConverterValidator.java
new file mode 100644
index 0000000..976ffdc
--- /dev/null
+++
b/modules/enterprise/gui/portal-war/src/main/java/org/rhq/enterprise/gui/legacy/action/resource/common/monitor/alerts/config/condition/CallTimeDataConverterValidator.java
@@ -0,0 +1,159 @@
+package
org.rhq.enterprise.gui.legacy.action.resource.common.monitor.alerts.config.condition;
+
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+import org.apache.commons.validator.GenericValidator;
+import org.apache.struts.action.ActionErrors;
+import org.apache.struts.action.ActionMessage;
+
+import org.rhq.core.domain.alert.AlertCondition;
+import org.rhq.core.domain.alert.AlertConditionCategory;
+import org.rhq.core.domain.auth.Subject;
+import org.rhq.core.domain.measurement.MeasurementDefinition;
+import org.rhq.core.domain.measurement.MeasurementUnits;
+import org.rhq.core.domain.measurement.composite.MeasurementNumericValueAndUnits;
+import org.rhq.core.domain.measurement.util.MeasurementConversionException;
+import org.rhq.core.domain.measurement.util.MeasurementConverter;
+import org.rhq.core.util.NumberUtil;
+import
org.rhq.enterprise.gui.legacy.action.resource.common.monitor.alerts.config.ConditionBean;
+import org.rhq.enterprise.server.measurement.MeasurementDefinitionManagerLocal;
+import org.rhq.enterprise.server.util.LookupUtil;
+
+public class CallTimeDataConverterValidator implements ConditionBeanConverterValidator {
+
+ public static final String DOUBLE_REGEX = "[0-9,.]+";
+
+ public static final String TYPE_ABS = "absolute";
+ public static final String TYPE_CHG = "changed";
+
+ private MeasurementDefinitionManagerLocal definitionManager;
+
+ public CallTimeDataConverterValidator() {
+ definitionManager = LookupUtil.getMeasurementDefinitionManager();
+ }
+
+ public void exportProperties(Subject subject, ConditionBean fromBean, AlertCondition
toCondition) {
+ MeasurementDefinition definition =
definitionManager.getMeasurementDefinition(subject, fromBean.getMetricId());
+ toCondition.setMeasurementDefinition(definition);
+
+ if (fromBean.getThresholdType().equals(TYPE_ABS)) {
+ MeasurementNumericValueAndUnits valueAndUnits =
MeasurementConverter.parse(fromBean.getCtAbsoluteValue(),
+ definition.getUnits());
+
+ toCondition.setThreshold(valueAndUnits.getValue());
+ toCondition.setCategory(AlertConditionCategory.THRESHOLD);
+ toCondition.setOption(fromBean.getCalltimeAbsOption());
+ toCondition.setComparator(fromBean.getCalltimeComparator());
+ toCondition.setName(fromBean.getCalltimeAbsPattern());
+
+ } else if (fromBean.getThresholdType().equals(TYPE_CHG)) {
+ MeasurementNumericValueAndUnits percentage =
MeasurementConverter.parse(fromBean.getCtPercentage(),
+ MeasurementUnits.PERCENTAGE);
+
+ toCondition.setThreshold(percentage.getValue());
+ toCondition.setCategory(AlertConditionCategory.CHANGE);
+ toCondition.setOption(fromBean.getCalltimeChgOption());
+ toCondition.setName(fromBean.getCalltimeChgPattern());
+ toCondition.setComparator(fromBean.getCalltimeChgOp());
+ }
+ }
+
+ public void importProperties(Subject subject, AlertCondition fromCondition,
ConditionBean toBean) {
+ // shared measurement processing
+ MeasurementDefinition definition = fromCondition.getMeasurementDefinition();
+ toBean.setMetricName(definition.getDisplayName());
+ toBean.setMetricId(definition.getId());
+
+ toBean.setTrigger(getTriggerName());
+
+ if (fromCondition.getCategory() == AlertConditionCategory.THRESHOLD) {
+ toBean.setThresholdType(TYPE_ABS);
+ toBean.setCalltimeAbsOption(fromCondition.getOption());
+ toBean.setCalltimeAbsPattern(fromCondition.getName());
+
+ try {
+ // this is coming from the backing store, so
+ String formattedValue =
MeasurementConverter.format(fromCondition.getThreshold(),
+ definition.getUnits(), true);
+ toBean.setCtAbsoluteValue(formattedValue);
+ } catch (MeasurementConversionException mce) {
+ toBean.setCtAbsoluteValue("Conversion Error");
+ }
+
+ toBean.setCalltimeComparator(fromCondition.getComparator());
+ toBean.setPercentageComparator(null);
+ } else if (fromCondition.getCategory() == AlertConditionCategory.CHANGE) {
+ toBean.setThresholdType(TYPE_CHG);
+ toBean.setCalltimeChgOption(fromCondition.getOption());
+ toBean.setCalltimeChgPattern(fromCondition.getName());
+ toBean.setCalltimeChgOp(fromCondition.getComparator());
+
+ Double threshold = MeasurementUnits.scaleUp(fromCondition.getThreshold(),
MeasurementUnits.PERCENTAGE);
+ toBean.setCtPercentage(MeasurementConverter.format(threshold,
MeasurementUnits.NONE, true));
+
+ toBean.setCalltimeComparator(null);
+
+ }
+
+ }
+
+ public boolean validate(ConditionBean bean, ActionErrors errors, int index) {
+ if (bean.getMetricId() <= 0) {
+ // user didn't select a metric
+ ActionMessage err = new
ActionMessage("alert.config.error.NoMetricSelected");
+ errors.add("condition[" + index + "].metricId", err);
+ return false;
+ }
+
+ if (bean.getThresholdType().equals(TYPE_ABS)) {
+ if (!GenericValidator.matchRegexp(bean.getCtAbsoluteValue(), DOUBLE_REGEX))
{
+ ActionMessage err = new ActionMessage("errors.double",
"AbsoluteValue");
+ errors.add("condition[" + index +
"].ctAbsoluteValue", err);
+ return false;
+ } else // double
+ {
+ // do nothing
+ }
+ if (bean.getCalltimeAbsPattern() != null) {
+ try {
+ Pattern p = Pattern.compile(bean.getCalltimeAbsPattern());
+ } catch (PatternSyntaxException e) {
+ ActionMessage err = new
ActionMessage("alert.config.error.CalltimePatternError");
+ errors.add("condition[" + index +
"].calltimeAbsPattern", err);
+ }
+ }
+ } else if (bean.getThresholdType().equals(TYPE_CHG)) // percentage
+ {
+ if (!GenericValidator.matchRegexp(bean.getCtPercentage(), DOUBLE_REGEX)) {
+ ActionMessage err = new ActionMessage("errors.double",
"Percentage");
+ errors.add("condition[" + index + "].ctPercentage",
err);
+ return false;
+ } else // double
+ {
+ // 0-100 range
+ double percentage =
NumberUtil.stringAsNumber(bean.getCtPercentage()).doubleValue();
+ if (!GenericValidator.isInRange(percentage, 0d, 1000d)) {
+ ActionMessage err = new ActionMessage("errors.range",
String.valueOf(percentage), String
+ .valueOf(0d), String.valueOf(1000d));
+ errors.add("condition[" + index +
"].ctPercentage", err);
+ return false;
+ }
+ }
+ if (bean.getCalltimeChgPattern() != null) {
+ try {
+ Pattern p = Pattern.compile(bean.getCalltimeChgPattern());
+ } catch (PatternSyntaxException e) {
+ ActionMessage err = new
ActionMessage("alert.config.error.CalltimePatternError");
+ errors.add("condition[" + index +
"].calltimeChgPattern", err);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public String getTriggerName() {
+ return "onCallTime";
+ }
+}
diff --git
a/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/model/CallTimeDataCacheElement.java
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/model/CallTimeDataCacheElement.java
new file mode 100644
index 0000000..d013af5
--- /dev/null
+++
b/modules/enterprise/server/jar/src/main/java/org/rhq/enterprise/server/alert/engine/model/CallTimeDataCacheElement.java
@@ -0,0 +1,152 @@
+package org.rhq.enterprise.server.alert.engine.model;
+
+import java.text.DateFormat;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Pattern;
+
+import org.rhq.core.domain.measurement.calltime.CallTimeDataValue;
+
+/**
+ *
+ * @author fbrueseke
+ */
+public class CallTimeDataCacheElement extends
AbstractCacheElement<CallTimeDataValue> {
+ public enum CallTimeElementValue {
+ MAX, MIN, AVG, COUNT;
+ }
+
+ private enum CalltimeChangeOp {
+ LO //Shrinks
+ , CH //Changes
+ , HI;//Grows
+ }
+
+ private static CallTimeDataValue dummy = new CallTimeDataValue();
+
+ private Double compareValue = null;
+ private CalltimeChangeOp comparator;
+
+ private ConcurrentHashMap<String, CallTimeDataValue> previous = new
ConcurrentHashMap<String, CallTimeDataValue>();
+ private Pattern callDestPattern = null;
+
+ private String fixPattern(String regex) {
+ boolean sw = regex.startsWith(".*");
+ boolean ew = regex.endsWith(".*");
+ return (!sw ? ".*" : "") + regex + (!ew ? ".*" :
"");
+ }
+
+ public CallTimeDataCacheElement(AlertConditionOperator operator, CallTimeElementValue
whichValue,
+ String comparator, Double value, int conditionId, String callDestPattern) {
+ super(operator, whichValue, dummy, conditionId);
+
+ if (value == null)
+ throw new InvalidCacheElementException("Invalid Cache Element: " +
"condition with id=" + conditionId + " "
+ + "and operator='" + operator.toString() + "'
" + "requires a non-null value");
+
+ if (whichValue == null)
+ throw new InvalidCacheElementException("operator '" +
operator.toString() + "'"
+ + " requires an operator option; it can not be null");
+
+ compareValue = value;
+ if (callDestPattern != null)
+ this.callDestPattern = Pattern.compile(fixPattern(callDestPattern));
+ else
+ this.callDestPattern = null;
+
+ if (comparator != null)
+ this.comparator = CalltimeChangeOp.valueOf(comparator);
+ }
+
+ @Override
+ public AlertConditionOperator.Type getOperatorSupportsType(AlertConditionOperator
operator) {
+ if ((operator == AlertConditionOperator.GREATER_THAN) || (operator ==
AlertConditionOperator.EQUALS)
+ || (operator == AlertConditionOperator.LESS_THAN) || (operator ==
AlertConditionOperator.CHANGES)) {
+ return operator.getDefaultType();
+ }
+
+ return AlertConditionOperator.Type.NONE;
+ }
+
+ @Override
+ public boolean matches(CallTimeDataValue providedValue, Object... extras) {
+
+ if (compareValue == null || compareValue.isNaN() || compareValue.isInfinite()
+ || this.alertConditionOperatorOption == null)
+ return false;
+
+ Double provided = getCallTimeElementValue(providedValue);
+ if (provided == null || provided.isInfinite() || provided.isNaN())
+ return false;
+
+ String key = "";
+ if (extras != null && extras[0] != null)
+ key = (String) extras[0];
+ if (callDestPattern != null && !callDestPattern.matcher(key).matches())
+ return false;
+
+ switch (alertConditionOperator) {
+ case EQUALS:
+ return (compareValue.compareTo(provided) == 0);
+
+ case GREATER_THAN: // threshold < measurement
+ return (compareValue.compareTo(provided) < 0);
+
+ case LESS_THAN: //threshold > measurement
+ return (compareValue.compareTo(provided) > 0);
+
+ case CHANGES:
+ // I assume that CallTimeDataValue objects are delivered to this method in
chronological order!
+ if (!previous.containsKey(key)) {
+ previous.put(key, providedValue);
+ return false;
+ }
+
+ CallTimeDataValue previousElem = previous.get(key);
+ double compare = getCallTimeElementValue(previousElem);
+ boolean result = computeChangeResult(provided, compare, comparator);
+ if (log.isTraceEnabled())
+ log.trace("Changes at least " + compareValue + "% ["
+ result + "]; current:" + providedValue
+ + "; previous:" + previousElem);
+
+ previous.put(key, providedValue);
+ return result;
+ }
+ return false;
+ }
+
+ private boolean computeChangeResult(Double provided, double compare, CalltimeChangeOp
op) {
+ switch (op) {
+ case LO:
+ return (provided < compare - compareValue * compare);
+ case CH:
+ return (provided < compare - compareValue * compare || provided >
compare + compareValue * compare);
+ case HI:
+ return (provided > compare + compareValue * compare);
+ default:
+ return false;
+ }
+ }
+
+ private Double getCallTimeElementValue(CallTimeDataValue struct) {
+ switch ((CallTimeElementValue) alertConditionOperatorOption) {
+ case MIN:
+ return struct.getMinimum();
+ case MAX:
+ return struct.getMaximum();
+ case AVG:
+ return struct.getTotal() / struct.getCount();
+ case COUNT:
+ return (double) struct.getCount();
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public String convertValueToString(CallTimeDataValue value) {
+ return "[From:"
+ + DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
DateFormat.MEDIUM).format(value.getBeginTime())
+ + ", To:" + DateFormat.getDateTimeInstance(DateFormat.MEDIUM,
DateFormat.MEDIUM).format(value.getEndTime())
+ + ", " + alertConditionOperatorOption + ":" +
getCallTimeElementValue(value) + "]";
+ }
+}