Seamless MicroProfile Health integration for GuicedEE applications using Vert.x 5 Health Checks.
Annotate your classes with standard @Liveness, @Readiness, and @Startup — health checks are discovered at startup via ClassGraph, registered with Vert.x HealthChecks, and exposed as JSON endpoints on the Vert.x Web Router automatically.
Built on Vert.x Health Checks · MicroProfile Health · Google Guice · JPMS module com.guicedee.health · Java 25+
<dependency>
<groupId>com.guicedee</groupId>
<artifactId>health</artifactId>
</dependency>Gradle (Kotlin DSL)
implementation("com.guicedee:health:2.0.2-SNAPSHOT")- MicroProfile Health annotations —
@Liveness,@Readiness,@Startup, and the legacy@Healthare all supported - Automatic discovery —
HealthPreStartupscans forHealthCheckimplementations via ClassGraph and registers them with the appropriate Vert.xHealthChecksinstance - Four dedicated endpoints — aggregated
/health, plus/health/live,/health/ready, and/health/started - Configurable paths — use
@HealthOptionson any class (or package) to override default endpoint paths - Environment variable overrides —
HEALTH_ENABLED,HEALTH_PATH,HEALTH_LIVENESS_PATH,HEALTH_READINESS_PATH,HEALTH_STARTUP_PATHoverride annotation values - Guice-managed checks — health check instances are obtained from the Guice injector, so
@Injectworks inside them - Manual registration — inject the
HealthChecksinstance and register Vert.x-native checks directly - MicroProfile SPI — custom
HealthCheckResponseProviderandHealthCheckResponseBuilderprovided out of the box - Lifecycle-aware — integrated with
IGuicePreStartup(scan),IGuicePostStartup(register), andIGuicePreDestroy(cleanup) - Timeout protection — each registered check has a 2-second timeout to prevent hanging health endpoints
Step 1 — Implement a health check with a MicroProfile annotation:
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Liveness;
@Liveness
public class DatabaseLiveness implements HealthCheck {
@Override
public HealthCheckResponse call() {
return HealthCheckResponse.named("DatabaseLiveness")
.up()
.withData("connection", "stable")
.build();
}
}Step 2 — Bootstrap GuicedEE (health endpoints are registered automatically):
IGuiceContext.registerModuleForScanning.add("my.app");
IGuiceContext.instance();
// GET /health → aggregated status
// GET /health/live → liveness checks only
// GET /health/ready → readiness checks only
// GET /health/started → startup checks onlyNo JPMS provides declaration is needed for health check classes — they are discovered via classpath scanning. The health module itself is registered automatically.
flowchart TD
n1["IGuiceContext.instance()"]
n2["IGuicePreStartup hooks"]
n1 --> n2
n3["HealthPreStartup.onStartup()<br/>sortOrder = MIN_VALUE + 60"]
n2 --> n3
n4["Create 4 HealthChecks instances<br/>health, liveness, readiness, startup"]
n3 --> n4
n5["Scan for HealthCheck implementations via ClassGraph"]
n3 --> n5
n6["Guice injector created"]
n1 --> n6
n7["HealthModule.configure()"]
n6 --> n7
n8["bind(HealthChecks.class).toInstance(healthChecks)"]
n7 --> n8
n9["IGuicePostStartup hooks"]
n1 --> n9
n10["HealthPreStartup.postLoad()"]
n9 --> n10
n11["Instantiate each discovered HealthCheck via Guice"]
n10 --> n11
n12["Inspect @Liveness, @Readiness, @Startup annotations"]
n10 --> n12
n13["Register with corresponding HealthChecks instance(s)"]
n10 --> n13
n14["Un-annotated checks register with the aggregated instance only"]
n10 --> n14
n15["VertxWebServerPostStartup<br/>from web module"]
n9 --> n15
n16["HealthRouterConfigurator.builder()<br/>sortOrder = MIN_VALUE + 60"]
n15 --> n16
n17["Read @HealthOptions / env vars for paths"]
n16 --> n17
n18["Mount HealthCheckHandler on each endpoint"]
n16 --> n18
Liveness checks determine if the application is running correctly. A failing liveness check typically means the application should be restarted.
@Liveness
public class ProcessLiveness implements HealthCheck {
@Override
public HealthCheckResponse call() {
return HealthCheckResponse.named("ProcessLiveness")
.up()
.build();
}
}Readiness checks determine if the application is ready to receive traffic. A failing readiness check means the application should be temporarily removed from the load balancer.
@Readiness
public class ServiceReadiness implements HealthCheck {
@Override
public HealthCheckResponse call() {
boolean ready = checkExternalDependencies();
return HealthCheckResponse.named("ServiceReadiness")
.status(ready)
.build();
}
}Startup checks verify if the application has finished its initialization. Failing startup checks prevent liveness probes from running during long startup sequences.
@Startup
public class InitializerStartup implements HealthCheck {
@Override
public HealthCheckResponse call() {
return HealthCheckResponse.named("InitializerStartup")
.up()
.build();
}
}A HealthCheck implementation without any annotation is registered with the aggregated /health endpoint only.
public class GenericCheck implements HealthCheck {
@Override
public HealthCheckResponse call() {
return HealthCheckResponse.named("GenericCheck")
.up()
.withData("version", "1.0")
.build();
}
}A check can carry more than one annotation — it will be registered with each corresponding instance and the aggregated instance:
@Liveness
@Readiness
public class CriticalServiceCheck implements HealthCheck {
@Override
public HealthCheckResponse call() {
return HealthCheckResponse.named("CriticalService")
.up()
.build();
}
}Place @HealthOptions on any class or package to customize endpoints:
@HealthOptions(
enabled = true,
path = "/health",
livenessPath = "/health/live",
readinessPath = "/health/ready",
startupPath = "/health/started"
)
public class MyAppConfig {
}| Attribute | Default | Description |
|---|---|---|
enabled |
true |
Enables or disables health check endpoints |
path |
/health |
Aggregated health status endpoint |
livenessPath |
/health/live |
Liveness checks endpoint |
readinessPath |
/health/ready |
Readiness checks endpoint |
startupPath |
/health/started |
Startup checks endpoint |
Every @HealthOptions attribute can be overridden with a system property or environment variable:
| Variable | Overrides | Example |
|---|---|---|
HEALTH_ENABLED |
enabled |
false |
HEALTH_PATH |
path |
/api/health |
HEALTH_LIVENESS_PATH |
livenessPath |
/api/health/live |
HEALTH_READINESS_PATH |
readinessPath |
/api/health/ready |
HEALTH_STARTUP_PATH |
startupPath |
/api/health/started |
Environment variables take precedence over annotation values.
If you need to register health checks manually using the Vert.x API, inject the HealthChecks instance:
import io.vertx.ext.healthchecks.HealthChecks;
import io.vertx.ext.healthchecks.Status;
import com.google.inject.Inject;
public class MyService {
@Inject
public MyService(HealthChecks hc) {
hc.register("my-check", 2000, promise -> promise.complete(Status.OK()));
}
}import io.vertx.ext.healthchecks.HealthChecks;
import io.vertx.ext.healthchecks.Status;
import com.google.inject.Inject;
public class MyOtherService {
@Inject
private HealthChecks hc;
public void registerChecks() {
hc.register("my-field-check", 2000, promise ->
promise.complete(Status.OK()));
}
}The aggregated HealthChecks is what Guice binds. For direct access to the liveness, readiness, or startup instances, use the static accessors:
HealthChecks liveness = HealthPreStartup.getLivenessChecks();
HealthChecks readiness = HealthPreStartup.getReadinessChecks();
HealthChecks startup = HealthPreStartup.getStartupChecks();Health endpoints return standard Vert.x health check JSON:
{
"status": "UP",
"checks": [
{
"id": "com.example.DatabaseLiveness",
"status": "UP",
"data": {
"connection": "stable"
}
},
{
"id": "com.example.ServiceReadiness",
"status": "UP"
}
]
}| HTTP Status | Meaning |
|---|---|
200 OK |
All checks are UP |
503 Service Unavailable |
One or more checks are DOWN |
When no HealthCheck implementations are found on the classpath, four placeholder checks are registered automatically (guicedee-health, guicedee-liveness, guicedee-readiness, guicedee-startup) — all returning Status.OK — so the endpoints always respond.
flowchart LR
com_guicedee_health["com.guicedee.health"]
com_guicedee_health --> com_guicedee_guicedinjection["com.guicedee.guicedinjection<br/>GuicedEE runtime — scanning, Guice, lifecycle"]
com_guicedee_health --> com_guicedee_vertx_web["com.guicedee.vertx.web<br/>Vert.x Web — Router, route mounting"]
com_guicedee_health --> io_vertx_healthcheck["io.vertx.healthcheck<br/>Vert.x Health Checks — HealthChecks, HealthCheckHandler"]
com_guicedee_health --> io_vertx_web["io.vertx.web<br/>Vert.x Web — Router integration"]
com_guicedee_health --> io_vertx_core["io.vertx.core<br/>Vert.x core"]
com_guicedee_health --> com_guicedee_modules_services_health["com.guicedee.modules.services.health<br/>MicroProfile Health API — annotations, SPI"]
Module name: com.guicedee.health
The module:
- exports
com.guicedee.healthandcom.guicedee.health.implementations - provides
IGuiceModulewithHealthModule - provides
IGuicePreStartupwithHealthPreStartup - provides
IGuicePostStartupwithHealthPreStartup - provides
VertxRouterConfiguratorwithHealthRouterConfigurator - provides
HealthCheckResponseProviderwithGuicedHealthCheckResponseProvider
| Class | Role |
|---|---|
HealthOptions |
Annotation — configures endpoint paths and enable/disable |
HealthPreStartup |
IGuicePreStartup + IGuicePostStartup + IGuicePreDestroy — scans, registers, and manages health check lifecycle |
HealthModule |
IGuiceModule — binds the HealthChecks instance into Guice |
HealthRouterConfigurator |
VertxRouterConfigurator — mounts HealthCheckHandler on the Vert.x Router |
GuicedHealthCheckResponseProvider |
MicroProfile HealthCheckResponseProvider SPI — creates response builders |
Issues and pull requests are welcome — please add tests for new health check integrations.