/*
* 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.server.alert.engine.model;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.rhq.core.domain.alert.AlertConditionOperator;
/**
* @author Joseph Marques
*/
public abstract class AbstractCacheElement<T> {
protected final Log log = LogFactory.getLog(getClass());
protected AlertConditionOperator alertConditionOperator;
protected Object alertConditionOperatorOption;
protected T alertConditionValue;
protected int alertConditionTriggerId;
protected CacheElementActivity activity = CacheElementActivity.UNKNOWN;
protected AbstractCacheElement<?> nextCacheElement;
public AbstractCacheElement(AlertConditionOperator operator, T value, int conditionTriggerId) {
this(operator, null, value, conditionTriggerId);
}
public AbstractCacheElement(AlertConditionOperator operator, Object operatorOption, T value, int conditionId) {
if (getOperatorSupportsType(operator) == AlertConditionOperator.Type.NONE) {
throw new UnsupportedAlertConditionOperatorException("operator '" + operator.toString() + "'"
+ " is unsupported in class " + getClass().getSimpleName() + " for AlertConditionId=" + conditionId);
}
/*
* the value argument is always required.
*
* with absolute comparison operators (LESS_THAN, EQUALS, GREATER_THAN) the value represents the user-specified
* value to compare against the system-collected items; it will not be updated unless the corresponding
* alertCondition is explicitly updated by the user.
*
* with delta comparison operators (CHANGES, CHANGES_TO, or CHANGES_FROM) the value represents the most recently
* seen value for the particular system-collected item that this cached element is going to be compared against;
* every time the cache is checked against a particular item, the current value of that item will replace the
* data stored in alertConditionValue; this allows the cache element itself to keep running knowledge of the
* current value for this item without hitting the backing store; however, for correct processing, when this
* cached element is created, it needs to know what the current value for the corresponding system-collected
* item is; therefore, it's a required argument;
*
* unfortunately, for several subsystems null is a valid value (no availability data yet, no measurement data yet,
* etc); accordingly, even though this argument is semantically required, it seems the most appropriate thing to
* do would be to lift the null restriction altogether for CHANGES* operators and just educate the callers to
* pass the proper data.
*/
if (value == null) {
if ((operator == AlertConditionOperator.CHANGES) || (operator == AlertConditionOperator.CHANGES_TO)
|| (operator == AlertConditionOperator.CHANGES_FROM)) {
if (log.isDebugEnabled()) {
log.debug("Possible invalid Cache Element: " + "condition with id=" + conditionId + " "
+ "and operator='" + operator.toString() + "' " + "passed a null value argument");
}
} else {
throw new InvalidCacheElementException("Invalid Cache Element: " + "condition with id=" + conditionId
+ " " + "and operator='" + operator.toString() + "' " + "requires a non-null value");
}
}
if ((operatorOption == null)
&& ((operator == AlertConditionOperator.CHANGES_TO) || (operator == AlertConditionOperator.CHANGES_FROM))) {
throw new InvalidCacheElementException("operator '" + operator.toString() + "'"
+ " requires an operator option; it can not be null");
}
this.alertConditionOperatorOption = operatorOption;
this.alertConditionOperator = operator;
this.alertConditionValue = value;
this.alertConditionTriggerId = conditionId;
}
public void setNextCacheElement(AbstractCacheElement<?> nextCacheElement) {
this.nextCacheElement = nextCacheElement;
}
public AlertConditionOperator getAlertConditionOperator() {
return alertConditionOperator;
}
public Object getAlertConditionOperatorOption() {
return alertConditionOperatorOption;
}
public T getAlertConditionValue() {
return alertConditionValue;
}
public void setAlertConditionValue(T updatedValue) {
this.alertConditionValue = updatedValue;
}
public int getAlertConditionTriggerId() {
return alertConditionTriggerId;
}
public CacheElementActivity getActivity() {
return activity;
}
public void setActivity(CacheElementActivity activity) {
this.activity = activity;
}
public final boolean process(T providedValue, Object... extraParams) {
String beforeString = null;
String afterString = null;
if (log.isDebugEnabled()) {
beforeString = this.toString();
}
boolean match = matches(providedValue, extraParams);
if (log.isDebugEnabled()) {
afterString = this.toString();
log.debug("comparing " + ((providedValue == null) ? "<null>" : providedValue) + " " + "against "
+ beforeString + " " + (match ? "match" : ""));
if (afterString.equals(beforeString) == false) {
log.debug("element updated: " + afterString);
}
}
return match;
}
/*
* this is where the brunt of the comparison work occurs. each subclass should override this method, which is
* called by process(T, boolean) to determine whether or not the incoming value to the cache matches this cache
* element or not.
*/
public abstract boolean matches(T providedValue, Object... extras);
/**
* For the most part, the operator itself denotes whether it makes comparisons against a sliding scale or not.
* However, this doesn't hold in every conceivable scenario. Thus, the defaultSupportsType will support 95% of the
* use cases, and the AbstractCacheElement's getOperatorSupportsType will have a chance to override this.
*
* @see AlertConditionOperator
*/
public abstract AlertConditionOperator.Type getOperatorSupportsType(AlertConditionOperator operator);
/**
* convenience method to test whether this cache element instance equals the specified type
*/
public boolean isType(AlertConditionOperator.Type type) {
return getOperatorSupportsType(alertConditionOperator) == type;
}
public String convertValueToString(T providedValue) {
return providedValue.toString();
}
@Override
public String toString() {
String conditionValue = null;
try {
conditionValue = alertConditionValue.toString();
} catch (Throwable t) {
conditionValue = t.getClass().getSimpleName() + "(" + alertConditionValue.getClass().getSimpleName() + ")";
}
return getClass().getSimpleName() + "[ " + "alertConditionTriggerId=" + alertConditionTriggerId + ", "
+ "alertConditionOperator=" + alertConditionOperator
+ ((alertConditionOperatorOption != null) ? ("(" + alertConditionOperatorOption + ")") : "") + ", "
+ "alertConditionValue=" + conditionValue + " ]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = (prime * result) + ((alertConditionOperator == null) ? 0 : alertConditionOperator.hashCode());
result = (prime * result)
+ ((alertConditionOperatorOption == null) ? 0 : alertConditionOperatorOption.hashCode());
result = (prime * result) + alertConditionTriggerId;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final AbstractCacheElement<?> other = (AbstractCacheElement<?>) obj;
if (alertConditionOperator == null) {
if (other.alertConditionOperator != null) {
return false;
}
} else if (!alertConditionOperator.equals(other.alertConditionOperator)) {
return false;
}
if (alertConditionOperatorOption == null) {
if (other.alertConditionOperatorOption != null) {
return false;
}
} else if (!alertConditionOperatorOption.equals(other.alertConditionOperatorOption)) {
return false;
}
if (alertConditionTriggerId != other.alertConditionTriggerId) {
return false;
}
return true;
}
}