package de.otto.edison.health.indicator; import net.jcip.annotations.ThreadSafe; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.slf4j.Marker; import org.slf4j.MarkerFactory; import org.springframework.boot.actuate.health.Health; import org.springframework.boot.actuate.health.HealthIndicator; import org.springframework.context.SmartLifecycle; import static org.springframework.boot.actuate.health.Health.down; import static org.springframework.boot.actuate.health.Health.up; @ThreadSafe public class GracefulShutdownHealthIndicator implements SmartLifecycle, HealthIndicator { private static final Logger LOG = LoggerFactory.getLogger(GracefulShutdownHealthIndicator.class); private static final Marker SHUTDOWN_MARKER = MarkerFactory.getMarker("EDISON_SHUTDOWN"); private final GracefulShutdownProperties properties; private volatile Health health = up().build(); public GracefulShutdownHealthIndicator(final GracefulShutdownProperties properties) { this.properties = properties; } @Override public Health health() { return health; } @Override public void stop(Runnable callback) { try { waitForSettingHealthCheckToDown(); LOG.info(SHUTDOWN_MARKER, "set health check to down"); indicateDown(); waitForShutdown(); } catch (InterruptedException e) { LOG.error(SHUTDOWN_MARKER, "graceful shutdown interrupted", e); } finally { callback.run(); } } private void indicateDown() { health = down().build(); } void waitForSettingHealthCheckToDown() throws InterruptedException { LOG.info(SHUTDOWN_MARKER, "shutdown signal received ..."); Thread.sleep(properties.getIndicateErrorAfter()); } void waitForShutdown() throws InterruptedException { Thread.sleep(properties.getPhaseOutAfter()); LOG.info(SHUTDOWN_MARKER, "grace period ended, starting shutdown now"); } @Override public void start() { } @Override public void stop() { } @Override public boolean isRunning() { return true; } @Override public int getPhase() { return Integer.MAX_VALUE; } @Override public boolean isAutoStartup() { return true; } }