package rocks.inspectit.server.influx; import java.util.concurrent.ExecutorService; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import javax.annotation.Resource; import org.influxdb.InfluxDB; import org.slf4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import rocks.inspectit.shared.all.spring.logger.Log; /** * Continuous checker component to monitor the status of an influxDB and get notified if its state * (available/not available) changes. * * @author Marius Oehler * */ @Component public class InfluxAvailabilityChecker implements Runnable { /** * The listener interface to get notified. */ public interface InfluxAvailabilityListener { /** * Is called if the influxDB gets unavailable. */ void onDisconnection(); /** * Is called if the influxDB gets available again. */ void onReconnection(); } /** * The current state of the monitored influxDB. */ private enum State { /** * The status has not been checked. */ NOT_CHECKED, /** * The database is available. */ AVAILABLE, /** * The database is not available. */ NOT_AVAILABLE; } /** * The delay in seconds between consecutive checks. */ private static final Long[] EXECUTION_DELAYS = { 5L, 15L, 30L, 60L, 120L }; /** * Logger for the class. */ @Log Logger log; /** * {@link ExecutorService} instance. */ @Autowired @Resource(name = "scheduledExecutorService") private ScheduledExecutorService scheduledExecutorService; /** * The database which states should be checked. */ private InfluxDB influx; /** * The listener to notify about changes of the status. */ @Autowired private InfluxAvailabilityListener availabilityListener; /** * The future of the next or currently running check. */ private ScheduledFuture<?> scheduledFuture; /** * The current state of the {@link #influx}. */ private State currentState = State.NOT_CHECKED; /** * The index of the next used execution delay. */ private int executionDelayIndex = 0; /** * Whether this checker is active and continuously executes a check. */ private boolean active = false; /** * Sets {@link #influx}. * * @param influx * New value for {@link #influx} */ public void setInflux(InfluxDB influx) { this.influx = influx; } /** * Activates the continuously check of the {@link #influx}. */ public void activate() { if ((scheduledFuture == null) || scheduledFuture.isDone()) { active = true; scheduledFuture = scheduledExecutorService.schedule(this, 0L, TimeUnit.SECONDS); } } /** * Deactivates the continuously check of the {@link #influx}. */ public void deactivate() { if ((scheduledFuture != null) && !scheduledFuture.isDone()) { active = false; scheduledFuture.cancel(false); currentState = State.NOT_CHECKED; } } /** * {@inheritDoc} */ @Override public void run() { try { State newState = isAvailable(); if (availabilityListener != null) { if ((currentState == State.AVAILABLE) && (newState == State.NOT_AVAILABLE)) { // switched from available to not available availabilityListener.onDisconnection(); } else if ((currentState == State.NOT_AVAILABLE) && (newState == State.AVAILABLE)) { // switched from not available to available availabilityListener.onReconnection(); } } // increasing delay when database is not available if (currentState == State.NOT_AVAILABLE) { if (executionDelayIndex < (EXECUTION_DELAYS.length - 1)) { executionDelayIndex++; } } else { executionDelayIndex = 0; } currentState = newState; } catch (RuntimeException e) { // this catch ensures that this runnable is not crashing and is not related to a "not // available state" of the database if (log.isWarnEnabled()) { log.warn("An unexpected exception has been thrown during availability check.", e); } } if (active) { long delay = EXECUTION_DELAYS[executionDelayIndex]; scheduledFuture = scheduledExecutorService.schedule(this, delay, TimeUnit.SECONDS); } } /** * Checks if the remote influxDB instance is available. * * @return Returns {@link State#AVAILABLE} if the influxDB is available else * {@link State#NOT_AVAILABLE}. */ private State isAvailable() { if (influx == null) { return State.NOT_AVAILABLE; } try { influx.ping(); return State.AVAILABLE; } catch (Exception e) { if (log.isTraceEnabled()) { log.trace("Ping to the influxDB failed.", e); } return State.NOT_AVAILABLE; } } }