SystemHealthService.java
package org.darkroomlibrary.service;
import org.springframework.amqp.rabbit.connection.Connection;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.stereotype.Service;
import javax.sql.DataSource;
import java.nio.file.Files;
import java.nio.file.Path;
import java.sql.SQLException;
import java.time.Instant;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Checks core readiness separately from optional middleware availability.
*/
@Service
public class SystemHealthService {
private static final String UP = "UP";
private static final String DOWN = "DOWN";
private static final String DISABLED = "DISABLED";
private final DataSource dataSource;
private final RedisConnectionFactory redisConnectionFactory;
private final ConnectionFactory rabbitConnectionFactory;
private final Path uploadRoot;
private final boolean redisEnabled;
private final boolean rabbitEnabled;
public SystemHealthService(
DataSource dataSource,
RedisConnectionFactory redisConnectionFactory,
ConnectionFactory rabbitConnectionFactory,
@Value("${file.upload-dir:./upload/pic}") String uploadDir,
@Value("${middleware.redis.enabled:false}") boolean redisEnabled,
@Value("${middleware.rabbit.enabled:false}") boolean rabbitEnabled) {
this.dataSource = dataSource;
this.redisConnectionFactory = redisConnectionFactory;
this.rabbitConnectionFactory = rabbitConnectionFactory;
this.uploadRoot = Path.of(uploadDir).toAbsolutePath().normalize();
this.redisEnabled = redisEnabled;
this.rabbitEnabled = rabbitEnabled;
}
public HealthReport checkReadiness() {
Map<String, String> components = new LinkedHashMap<>();
components.put("database", checkDatabase());
components.put("fileStorage", checkFileStorage());
components.put("redis", redisEnabled ? checkRedis() : DISABLED);
components.put("rabbitMq", rabbitEnabled ? checkRabbitMq() : DISABLED);
boolean coreReady = UP.equals(components.get("database"))
&& UP.equals(components.get("fileStorage"));
boolean optionalReady = (!redisEnabled || UP.equals(components.get("redis")))
&& (!rabbitEnabled || UP.equals(components.get("rabbitMq")));
String status = coreReady ? (optionalReady ? UP : "DEGRADED") : DOWN;
return new HealthReport(status, Instant.now(), Map.copyOf(components));
}
private String checkDatabase() {
try (java.sql.Connection connection = dataSource.getConnection()) {
return connection.isValid(2) ? UP : DOWN;
} catch (SQLException exception) {
return DOWN;
}
}
private String checkFileStorage() {
try {
Files.createDirectories(uploadRoot);
return Files.isDirectory(uploadRoot) && Files.isWritable(uploadRoot) ? UP : DOWN;
} catch (Exception exception) {
return DOWN;
}
}
private String checkRedis() {
try (RedisConnection connection = redisConnectionFactory.getConnection()) {
return "PONG".equalsIgnoreCase(connection.ping()) ? UP : DOWN;
} catch (Exception exception) {
return DOWN;
}
}
private String checkRabbitMq() {
try (Connection connection = rabbitConnectionFactory.createConnection()) {
return connection.isOpen() ? UP : DOWN;
} catch (Exception exception) {
return DOWN;
}
}
public record HealthReport(
String status,
Instant checkedAt,
Map<String, String> components) {
public boolean acceptsTraffic() {
return !DOWN.equals(status);
}
}
}