/**
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.seyren.core.service.schedule;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.joda.time.DateTime;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Optional;
import com.seyren.core.domain.Alert;
import com.seyren.core.domain.AlertType;
import com.seyren.core.domain.Check;
import com.seyren.core.domain.Subscription;
import com.seyren.core.service.checker.TargetChecker;
import com.seyren.core.service.checker.ValueChecker;
import com.seyren.core.service.notification.NotificationService;
import com.seyren.core.store.AlertsStore;
import com.seyren.core.store.ChecksStore;
public class CheckRunner implements Runnable {
private static final Logger LOGGER = LoggerFactory.getLogger(CheckRunner.class);
private final Check check;
private final AlertsStore alertsStore;
private final ChecksStore checksStore;
private final TargetChecker targetChecker;
private final ValueChecker valueChecker;
private final Iterable<NotificationService> notificationServices;
public CheckRunner(Check check, AlertsStore alertsStore, ChecksStore checksStore, TargetChecker targetChecker, ValueChecker valueChecker,
Iterable<NotificationService> notificationServices) {
this.check = check;
this.alertsStore = alertsStore;
this.checksStore = checksStore;
this.targetChecker = targetChecker;
this.valueChecker = valueChecker;
this.notificationServices = notificationServices;
}
@Override
public final void run() {
if (!check.isEnabled()) {
return;
}
try {
Map<String, Optional<BigDecimal>> targetValues = targetChecker.check(check);
DateTime now = new DateTime();
BigDecimal warn = check.getWarn();
BigDecimal error = check.getError();
AlertType worstState;
if (check.isAllowNoData()) {
worstState = AlertType.OK;
} else {
worstState = AlertType.UNKNOWN;
}
List<Alert> interestingAlerts = new ArrayList<Alert>();
for (Entry<String, Optional<BigDecimal>> entry : targetValues.entrySet()) {
String target = entry.getKey();
Optional<BigDecimal> value = entry.getValue();
if (!value.isPresent()) {
if (!check.isAllowNoData()) {
LOGGER.warn("No value present for {} and check must have data", target);
}
continue;
}
BigDecimal currentValue = value.get();
Alert lastAlert = alertsStore.getLastAlertForTargetOfCheck(target, check.getId());
AlertType lastState;
if (lastAlert == null) {
lastState = AlertType.OK;
} else {
lastState = lastAlert.getToType();
}
AlertType currentState = valueChecker.checkValue(currentValue, warn, error);
if (currentState.isWorseThan(worstState)) {
worstState = currentState;
}
if (isStillOk(lastState, currentState)) {
continue;
}
Alert alert = createAlert(target, currentValue, warn, error, lastState, currentState, now);
alertsStore.createAlert(check.getId(), alert);
// Only notify if the alert has changed state
if (stateIsTheSame(lastState, currentState)) {
continue;
}
interestingAlerts.add(alert);
}
Check updatedCheck = checksStore.updateStateAndLastCheck(check.getId(), worstState, DateTime.now());
if (interestingAlerts.isEmpty()) {
return;
}
for (Subscription subscription : updatedCheck.getSubscriptions()) {
if (!subscription.shouldNotify(now, worstState)) {
continue;
}
for (NotificationService notificationService : notificationServices) {
if (notificationService.canHandle(subscription.getType())) {
try {
notificationService.sendNotification(updatedCheck, subscription, interestingAlerts);
} catch (Exception e) {
LOGGER.warn("Notifying {} by {} failed.", subscription.getTarget(), subscription.getType(), e);
}
}
}
}
} catch (Exception e) {
LOGGER.warn("{} failed", check.getName(), e);
}
}
private boolean isStillOk(AlertType last, AlertType current) {
return last == AlertType.OK && current == AlertType.OK;
}
private boolean stateIsTheSame(AlertType last, AlertType current) {
return last == current;
}
private Alert createAlert(String target, BigDecimal value, BigDecimal warn, BigDecimal error, AlertType from, AlertType to, DateTime now) {
return new Alert()
.withTarget(target)
.withValue(value)
.withWarn(warn)
.withError(error)
.withFromType(from)
.withToType(to)
.withTimestamp(now);
}
}