Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

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=10

Injecting 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

SourceExamplePriority
Java system properties-Dblog.page-size=5Highest
Environment variablesBLOG_PAGE_SIZE=5Middle
microprofile-config.propertiesblog.page-size=10Lowest

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}}]}
AddressWhich checks
/health/live@Liveness
/health/ready@Readiness
/health/started@Startup
/healthAll 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.

Thanks for reading!

Donate to the site — we work hard for you.