MicroProfile Config and Health
MicroProfile is a set of specifications for cloud services that evolves alongside Jakarta EE and builds on CDI. Open Liberty supports it fully — we enabled microProfile-7.1 back in the first lesson. Let's look at the two most needed parts: Config — settings you can change without rebuilding, and Health — addresses the deployment system uses to learn whether the application is alive.
The settings file
The generator already created the file src/main/resources/META-INF/microprofile-config.properties. Put the blog settings in it:
# src/main/resources/META-INF/microprofile-config.properties
blog.title=Anna's blog
blog.page-size=10Injecting settings
// src/main/java/com/example/blog/SettingsResource.java
package com.example.blog;
import jakarta.inject.Inject;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
import java.util.Optional;
import org.eclipse.microprofile.config.inject.ConfigProperty;
@Path("/settings")
@Produces(MediaType.APPLICATION_JSON)
public class SettingsResource {
public record Settings(String title, int pageSize, String banner) {
}
@Inject
@ConfigProperty(name = "blog.title")
String title;
@Inject
@ConfigProperty(name = "blog.page-size", defaultValue = "20")
int pageSize;
@Inject
@ConfigProperty(name = "blog.banner")
Optional<String> banner;
@GET
public Settings settings() {
return new Settings(title, pageSize, banner.orElse("none"));
}
}defaultValue is used when the setting is not found anywhere, and Optional suits optional values. The string becomes an int by itself.
curl http://localhost:8080/blog/api/settings{"banner":"none","pageSize":10,"title":"Anna's blog"}
Sources and priority
| Source | Example | Priority |
|---|---|---|
| Java system properties | -Dblog.page-size=5 | Highest |
| Environment variables | BLOG_PAGE_SIZE=5 | Middle |
microprofile-config.properties | blog.page-size=10 | Lowest |
For an environment variable, dots and dashes become _, and letters become uppercase. Let's start the server with variables:
BLOG_PAGE_SIZE=5 BLOG_BANNER="Autumn sale" ./mvnw liberty:dev{"banner":"Autumn sale","pageSize":5,"title":"Anna's blog"}
This way the same build runs with different settings on a laptop and on a server.
Health checks
Deployment systems such as Kubernetes regularly ask the application whether it is alive (liveness) and ready to take requests (readiness). The readiness check for the blog is whether the database is reachable:
// src/main/java/com/example/blog/DatabaseHealthCheck.java
package com.example.blog;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Readiness;
@Readiness
@ApplicationScoped
public class DatabaseHealthCheck implements HealthCheck {
@Inject
PostRepository repository;
@Override
public HealthCheckResponse call() {
try {
long posts = repository.count();
return HealthCheckResponse.named("database")
.up()
.withData("posts", posts)
.build();
} catch (RuntimeException e) {
return HealthCheckResponse.named("database")
.down()
.withData("error", e.getMessage())
.build();
}
}
}And a simple "alive" check:
// src/main/java/com/example/blog/AliveCheck.java
package com.example.blog;
import jakarta.enterprise.context.ApplicationScoped;
import org.eclipse.microprofile.health.HealthCheck;
import org.eclipse.microprofile.health.HealthCheckResponse;
import org.eclipse.microprofile.health.Liveness;
@Liveness
@ApplicationScoped
public class AliveCheck implements HealthCheck {
@Override
public HealthCheckResponse call() {
return HealthCheckResponse.up("blog");
}
}The check addresses belong to the server, not to the application, so they have no /blog:
curl http://localhost:8080/health/ready
curl http://localhost:8080/health{"status":"UP","checks":[{"name":"database","status":"UP","data":{"posts":2}}]}
{"status":"UP","checks":[{"name":"blog","status":"UP","data":{}},{"name":"database","status":"UP","data":{"posts":2}}]}
| Address | Which checks |
|---|---|
/health/live | @Liveness |
/health/ready | @Readiness |
/health/started | @Startup |
/health | All of them |
If any check returns down(), the overall status becomes DOWN, and the response code becomes 503.
Common mistake
Injecting a required setting that is not in any source. The application does not start:
SRCFG02000: Failed to Inject @ConfigProperty for key blog.owner into com.example.blog.SettingsResource.title since the config property could not be found in any config source
Add the value to the settings file, set a defaultValue or make the field Optional.