Merge pull request #921 from hapifhir/health-check

Standalone Java Health Check & Hibernate Dialect Override Fix
This commit is contained in:
c-schuler
2026-03-10 09:18:34 -06:00
committed by GitHub
5 changed files with 72 additions and 2 deletions

View File

@@ -15,6 +15,9 @@ FROM build-hapi AS build-distroless
RUN mvn package -DskipTests spring-boot:repackage -Pboot RUN mvn package -DskipTests spring-boot:repackage -Pboot
RUN mkdir /app && cp /tmp/hapi-fhir-jpaserver-starter/target/ROOT.war /app/main.war RUN mkdir /app && cp /tmp/hapi-fhir-jpaserver-starter/target/ROOT.war /app/main.war
COPY src/main/java/HealthCheck.java /app/HealthCheck.java
RUN javac /app/HealthCheck.java
########### Use the official Tomcat image as base image for the Tomcat variant ########### Use the official Tomcat image as base image for the Tomcat variant
########### it can be built using eg. `docker build --target tomcat .` ########### it can be built using eg. `docker build --target tomcat .`

View File

@@ -475,6 +475,20 @@ jpa:
# Then comment all hibernate.search.backend.* # Then comment all hibernate.search.backend.*
``` ```
## Docker Health Check
The distroless Docker image includes a built-in health check that verifies the FHIR server is operational by calling the `/fhir/metadata` endpoint and confirming a valid `CapabilityStatement` is returned. It uses a standalone Java class with no external dependencies, making it compatible with the distroless base image which has no shell or utilities like `curl`.
To run the health check inside a running container:
```
docker exec hapi-fhir-jpaserver-start java -cp /app HealthCheck
```
An exit code of `0` indicates the server is healthy. An exit code of `1` indicates a failure, with diagnostic details written to stderr.
To enable periodic health checks, uncomment the `healthcheck` block in `docker-compose.yml`.
## Running hapi-fhir-jpaserver directly from IntelliJ as Spring Boot ## Running hapi-fhir-jpaserver directly from IntelliJ as Spring Boot
Make sure you run with the maven profile called ```boot``` and NOT also ```jetty```. Then you are ready to press debug the project directly without any extra Application Servers. Make sure you run with the maven profile called ```boot``` and NOT also ```jetty```. Then you are ready to press debug the project directly without any extra Application Servers.

View File

@@ -8,9 +8,17 @@ services:
SPRING_DATASOURCE_USERNAME: "admin" SPRING_DATASOURCE_USERNAME: "admin"
SPRING_DATASOURCE_PASSWORD: "admin" SPRING_DATASOURCE_PASSWORD: "admin"
SPRING_DATASOURCE_DRIVER_CLASS_NAME: "org.postgresql.Driver" SPRING_DATASOURCE_DRIVER_CLASS_NAME: "org.postgresql.Driver"
SPRING_JPA_PROPERTIES_HIBERNATE_DIALECT: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect HIBERNATE_DIALECT: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect
ports: ports:
- "8080:8080" - "8080:8080"
# Uncomment to enable periodic health checks.
# Can also be run manually: docker exec hapi-fhir-jpaserver-start java -cp /app HealthCheck
# healthcheck:
# test: ["CMD", "java", "-cp", "/app", "HealthCheck"]
# interval: 30s
# timeout: 10s
# start_period: 60s
# retries: 3
depends_on: depends_on:
hapi-fhir-postgres: hapi-fhir-postgres:
condition: service_healthy condition: service_healthy

View File

@@ -0,0 +1,45 @@
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.regex.Pattern;
public class HealthCheck {
public static void main(String[] args) {
try {
var port = System.getenv().getOrDefault("SERVER_PORT", "8080");
var url = new URL("http://localhost:" + port + "/fhir/metadata");
var conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/fhir+json");
conn.setConnectTimeout(10000);
conn.setReadTimeout(10000);
var status = conn.getResponseCode();
if (status != 200) {
System.err.println("Health check failed: HTTP " + status);
System.exit(1);
}
var body = new StringBuilder();
try (var reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
String line;
while ((line = reader.readLine()) != null) {
body.append(line);
}
}
var pattern = Pattern.compile("\"resourceType\"\\s*:\\s*\"CapabilityStatement\"");
if (pattern.matcher(body.toString()).find()) {
System.exit(0);
} else {
System.err.println("Health check failed: CapabilityStatement not found in response");
System.exit(1);
}
} catch (Exception e) {
System.err.println("Health check failed: " + e.getMessage());
System.exit(1);
}
}
}

View File

@@ -120,7 +120,7 @@ spring:
# Hibernate dialect is auto-detected except for H2/Postgres. # Hibernate dialect is auto-detected except for H2/Postgres.
# If using H2: ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect # If using H2: ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect
# If using Postgres: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect # If using Postgres: ca.uhn.fhir.jpa.model.dialect.HapiFhirPostgresDialect
dialect: ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect dialect: ${HIBERNATE_DIALECT:ca.uhn.fhir.jpa.model.dialect.HapiFhirH2Dialect}
# --- Optional Hibernate DDL & tuning (commented out from source) --- # --- Optional Hibernate DDL & tuning (commented out from source) ---
hbm2ddl: hbm2ddl: