HealthController.java

package com.stocks.stockease.controller;

import java.sql.Connection;
import java.sql.SQLException;

import javax.sql.DataSource;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * Application health check endpoint for orchestration and monitoring.
 * 
 * Used by:
 * - Kubernetes (K8s liveness probes, readiness probes)
 * - Docker (healthcheck instruction)
 * - Load balancers (backend health checks)
 * - Monitoring systems (Prometheus, DataDog, etc.)
 * 
 * Design principles:
 * - Lightweight: Single DB connection validation, no expensive operations
 * - Fast response: ~10ms typical latency (10-second timeout for slow DB startups)
 * - Side-effect free: No state changes, safe to call frequently
 * - Minimal contract: Simple 200 OK or 500 error with short text message
 * 
 * Success criteria:
 * - HTTP 200 OK: Database is reachable and connection valid
 * - HTTP 500 Internal Error: Database unreachable or connection timeout
 * 
 * Endpoint: GET /api/health (public, no authentication required)
 * 
 * @author Team StockEase
 * @version 1.0
 * @since 2025-01-01
 */
@RestController
@RequestMapping("/api/health")
public class HealthController {

    /** Primary JDBC DataSource used by the application. Injected by Spring. */
    private final DataSource dataSource;

    /**
     * Constructor for dependency injection.
     *
     * @param dataSource the JDBC DataSource to check for connectivity
     */
    public HealthController(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /**
     * Simple health check endpoint for orchestrators and monitoring systems.
     * 
     * Implementation strategy:
     * - Obtains connection from pool (validates JDBC driver is loaded)
     * - Calls Connection.isValid(10) to verify database liveness
     * - 10-second timeout balances startup latency vs probe responsiveness
     * - Handles SQLException for database connection failures
     * 
     * Success response: 200 OK with "Database is connected and API is running."
     * Failure response: 500 Internal Error with diagnostic message
     * 
     * Important: Keep this handler lightweight. Avoid:
     * - SELECT COUNT(*) queries (expensive)
     * - Schema migrations (causes cascading failures)
     * - Logging detailed errors (expose internal state)
     * - Caching responses (defeats real-time health checks)
     *
     * @return ResponseEntity with HTTP 200 on success, 500 on failure
     */
    @GetMapping
    public ResponseEntity<String> healthCheck() {
        // Probe the primary backing service (database). This call is intentionally
        // lightweight and non-destructive: it only validates the connection socket
        // and basic liveness using the JDBC isValid() method.
        try (Connection connection = dataSource.getConnection()) {
            // Bound the liveness check so callers don't wait too long. A short
            // timeout (10s) balances resilience for slow DB startups against probe
            // latency for health check callers.
            if (connection.isValid(10)) {
                return ResponseEntity.ok("Database is connected and API is running.");
            }
        } catch (SQLException e) {
            // Return a concise error message. Avoid returning stack traces or
            // detailed internal state to external callers for security reasons.
            return ResponseEntity.status(500).body("Database is down: " + e.getMessage());
        }

        // Fallback generic error when connection opened but did not validate
        // within the expected bounds. This path is reachable if isValid() returns
        // false for any reason.
        return ResponseEntity.status(500).body("Unknown issue with database connection.");
    }
}