package rocks.inspectit.server.alerting;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;
import rocks.inspectit.server.alerting.state.AlertingState;
import rocks.inspectit.server.ci.event.AbstractAlertingDefinitionEvent;
import rocks.inspectit.shared.all.cmr.property.spring.PropertyUpdate;
import rocks.inspectit.shared.all.spring.logger.Log;
import rocks.inspectit.shared.cs.ci.AlertingDefinition;
import rocks.inspectit.shared.cs.communication.data.cmr.AlertClosingReason;
/**
* Scheduler for the {@link ThresholdChecker} to check the threshold specified by the
* {@link AlertingDefinition}.
*
* @author Marius Oehler
*
*/
@Component
public class AlertingScheduler implements Runnable, ApplicationListener<AbstractAlertingDefinitionEvent> {
/**
* The execution interval in minutes of this runnable.
*/
static final long CHECK_INTERVAL = 1L;
/**
* Logger for the class.
*/
@Log
Logger log;
/**
* Activation state of this service.
*/
@Value("${alerting.active}")
boolean active;
/**
* {@link ExecutorService} instance.
*/
@Autowired
@Resource(name = "scheduledExecutorService")
ScheduledExecutorService executorService;
/**
* {@link ThresholdChecker} instance.
*/
@Autowired
ThresholdChecker thresholdChecker;
/**
* {@link ScheduledFuture} of the currently executed {@link AlertingScheduler}.
*/
private ScheduledFuture<?> scheduledFuture;
/**
* The currently {@link AlertingState}s holding the existing {@link AlertingDefinition}s..
*/
private List<AlertingState> alertingStates = new CopyOnWriteArrayList<>();
/**
* Updates the state of this {@link AlertingDefinition} instance. It is getting enabled or
* disabled according to the {@link #active} field.
*/
@PostConstruct
@PropertyUpdate(properties = { "alerting.active" })
public void updateState() {
if (active) {
if ((scheduledFuture == null) || scheduledFuture.isDone()) {
scheduledFuture = executorService.scheduleAtFixedRate(this, 0L, CHECK_INTERVAL, TimeUnit.MINUTES);
}
if (log.isInfoEnabled()) {
log.info("|-Alerting scheduler has been started..");
}
} else {
if ((scheduledFuture != null) && !scheduledFuture.isDone()) {
scheduledFuture.cancel(false);
}
if (log.isInfoEnabled()) {
log.info("|-Alerting scheduler has been stopped..");
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void run() {
if (log.isDebugEnabled()) {
log.debug("|-Checking alert definitions...");
}
long currentTime = System.currentTimeMillis();
for (AlertingState alertingState : alertingStates) {
try {
long nextCheckTime = alertingState.getLastCheckTime() + alertingState.getAlertingDefinition().getTimeRange(TimeUnit.MILLISECONDS);
if (nextCheckTime <= currentTime) {
thresholdChecker.checkThreshold(alertingState);
}
} catch (Exception e) {
if (log.isErrorEnabled()) {
log.error("Unexpected exception occured.", e);
}
}
}
}
/**
* {@inheritDoc}
*/
@Override
public void onApplicationEvent(AbstractAlertingDefinitionEvent event) {
if (event == null) {
return;
}
switch (event.getType()) {
case LOADED:
loadedAlertingDefinitions(event);
break;
case ADDED:
createdAlertingDefinition(event);
break;
case REMOVED:
deletedAlertingDefinition(event);
break;
case UPDATE:
updatedAlertingDefinition(event);
break;
default:
break;
}
}
/**
* Handle the {@link AbstractAlertingDefinitionEvent}.
*
* @param event
* the received {@link AbstractAlertingDefinitionEvent}
*/
private void loadedAlertingDefinitions(AbstractAlertingDefinitionEvent event) {
alertingStates.clear();
for (AlertingDefinition definition : event.getAlertingDefinitions()) {
alertingStates.add(new AlertingState(definition));
}
}
/**
* Handle the {@link AbstractAlertingDefinitionEvent}.
*
* @param event
* the received {@link AbstractAlertingDefinitionEvent}
*/
private void createdAlertingDefinition(AbstractAlertingDefinitionEvent event) {
alertingStates.add(new AlertingState(event.getFirst()));
}
/**
* Handle the {@link AbstractAlertingDefinitionEvent}.
*
* @param event
* the received {@link AbstractAlertingDefinitionEvent}
*/
private void deletedAlertingDefinition(AbstractAlertingDefinitionEvent event) {
for (AlertingState state : alertingStates) {
if (Objects.equals(state.getAlertingDefinition().getId(), event.getFirst().getId())) {
if (null != state.getAlert()) {
state.getAlert().close(System.currentTimeMillis(), AlertClosingReason.ALERTING_DEFINITION_DELETED);
}
alertingStates.remove(state);
break;
}
}
}
/**
* Handle the {@link AbstractAlertingDefinitionEvent}.
*
* @param event
* the received {@link AbstractAlertingDefinitionEvent}
*/
private void updatedAlertingDefinition(AbstractAlertingDefinitionEvent event) {
Iterator<AlertingState> iterator = alertingStates.iterator();
while (iterator.hasNext()) {
AlertingState state = iterator.next();
if (Objects.equals(state.getAlertingDefinition().getId(), event.getFirst().getId())) {
state.setAlertingDefinition(event.getFirst());
if (null != state.getAlert()) {
state.getAlert().setAlertingDefinition(event.getFirst());
}
break;
}
}
}
}