package org.rhq.enterprise.server.alert.engine.model; import java.text.DateFormat; import java.util.concurrent.ConcurrentHashMap; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.rhq.core.domain.alert.AlertConditionOperator; 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) try { this.callDestPattern = Pattern.compile(fixPattern(callDestPattern)); } catch (PatternSyntaxException e) { throw new InvalidCacheElementException("The destination regular expression '" + callDestPattern + "' is invalid: " + e.getMessage()); } 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) + "]"; } }