/**
* NOTE: This copyright does *not* cover user programs that use HQ
* program services by normal system calls through the application
* program interfaces provided as part of the Hyperic Plug-in Development
* Kit or the Hyperic Client Development Kit - this is merely considered
* normal use of the program, and does *not* fall under the heading of
* "derived work".
*
* Copyright (C) [2009-2010], VMware, Inc.
* This file is part of HQ.
*
* HQ is free software; you can redistribute it and/or modify
* it under the terms version 2 of the GNU General Public License as
* published by the Free Software Foundation. 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
* USA.
*
*/
package org.hyperic.hq.events.server.session;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hyperic.hq.bizapp.server.shared.HeartbeatCurrentTime;
import org.hyperic.hq.context.Bootstrap;
import org.hyperic.hq.measurement.MeasurementConstants;
import org.hyperic.hq.measurement.server.session.AlertConditionsSatisfiedZEvent;
import org.hyperic.hq.measurement.server.session.AlertConditionsSatisfiedZEventPayload;
import org.hyperic.hq.zevents.ZeventEnqueuer;
/**
*
* Implementation of {@link ExecutionStrategy} that fires an
* {@link AlertConditionsSatisfiedZEvent} when a certain number of events have
* occurred within a given time window. Logic originally kept in a
* CounterTrigger class.
* @author jhickey
*
*/
public class CounterExecutionStrategy implements ExecutionStrategy {
private final Object lock = new Object();
private final long count;
private final long timeRange; // timeRange is in milliseconds
private final Log log = LogFactory.getLog(CounterExecutionStrategy.class);
private final ZeventEnqueuer zeventEnqueuer;
private List expirations;
private HeartbeatCurrentTime heartbeatCurrentTime;
/**
*
* @param count The number of times the alert conditions must be satisfied
* @param timeRange The time window (in milliseconds) in which the specified
* count of alert conditions must be satisfied.
* @param zeventEnqueuer The {@link ZeventEnqueuer} to use for sending
* {@link AlertConditionsSatisfiedZEvent}s
*/
public CounterExecutionStrategy(long count, long timeRange, ZeventEnqueuer zeventEnqueuer) {
this(count, timeRange, zeventEnqueuer, Bootstrap.getBean(HeartbeatCurrentTime.class));
}
// only used for tests mainly to inject HeartbeatCurrentTime
CounterExecutionStrategy(long count, long timeRange, ZeventEnqueuer zeventEnqueuer,
HeartbeatCurrentTime heartbeatCurrentTime) {
this.count = count;
this.timeRange = timeRange;
this.zeventEnqueuer = zeventEnqueuer;
this.expirations = new ArrayList();
this.heartbeatCurrentTime = heartbeatCurrentTime;
}
private void clearExpired() {
for (Iterator<Long> iterator = expirations.iterator(); iterator.hasNext();) {
Long expiration = iterator.next();
if (expiration.longValue() < heartbeatCurrentTime.getTimeMillis()) {
iterator.remove();
}
}
}
public boolean conditionsSatisfied(AlertConditionsSatisfiedZEvent event) {
synchronized (lock) {
AlertConditionsSatisfiedZEventPayload payload = (AlertConditionsSatisfiedZEventPayload) event.getPayload();
expirations.add(Long.valueOf(payload.getTimestamp() + timeRange));
clearExpired();
if (expirations.size() >= count) {
payload.setMessage("Occurred " + expirations.size() + " times in the span of " + (timeRange /
MeasurementConstants.MINUTE) + " minutes");
try {
if (log.isDebugEnabled()) {
log.debug("Firing event " + event);
}
zeventEnqueuer.enqueueEvent(event);
expirations.clear();
return true;
} catch (InterruptedException e) {
log.warn("Interrupted enqueuing an AlertConditionsSatisfiedZEvent. Event: " +
event +
" may not be processed unless triggering condition occurs again within the specified time range. Cause: " +
e.getMessage());
}
}
}
return false;
}
long getCount() {
return count;
}
public Serializable getState() {
return (Serializable) this.expirations;
}
long getTimeRange() {
return timeRange;
}
public void initialize(Serializable initialState) {
if(initialState == null) {
return;
}
if (!(initialState instanceof List)) {
log.warn("Received persisted state that was not an instance of list. Count will be reset to 0.");
return;
}
this.expirations = (List) initialState;
}
}