Adding health reports to microservices

duration 20 minutes

Prerequisites:

Explore how to report and check the health of a microservice with MicroProfile Health.

What you’ll learn

You will learn how to use MicroProfile Health to report the health status of microservices and take appropriate actions based on this report.

MicroProfile Health allows services to report their health, and it publishes the overall health status to a defined endpoint. A service reports UP if it is available and reports DOWN if it is unavailable. MicroProfile Health reports an individual service status at the endpoint and indicates the overall status as UP if all the services are UP. A service orchestrator can then use the health statuses to make decisions.

A service checks its own health by performing necessary self-checks and then reports its overall status by implementing the API provided by MicroProfile Health. A self-check can be a check on anything that the service needs, such as a dependency, a successful connection to an endpoint, a system property, a database connection, or the availability of required resources. MicroProfile offers checks for both liveness and readiness.

You will add liveness and readiness checks to the system and inventory services, which are provided for you, and implement what is necessary to report health status by using MicroProfile Health.

Getting started

The fastest way to work through this guide is to clone the Git repository and use the projects that are provided inside:

git clone https://github.com/openliberty/guide-microprofile-health.git
cd guide-microprofile-health

The start directory contains the starting project that you will build upon.

The finish directory contains the finished project that you will build.

Try what you’ll build

The finish directory in the root of this guide contains the finished application. Give it a try before you proceed.

To try out the application, first go to the finish directory and run the following Maven goal to build the application and deploy it to Open Liberty:

mvn liberty:run

After you see the following message, your application server is ready.

The defaultServer server is ready to run a smarter planet.

The system and inventory services can be found at the following URLs:

Visit the http://localhost:9080/health URL to see the overall health status of the application, as well as the aggregated data of the liveness and readiness checks. Two checks show the state of the system service, and the other two checks show the state of the inventory service. As you might expect, both services are in the UP state, and the overall health status of the application is in the UP state.

You can also access the /health/ready endpoint by visiting the http://localhost:9080/health/ready URL to view the data from the readiness health checks. Similarly, access the /health/live endpoint by visiting the http://localhost:9080/health/live URL to view the data from the liveness health checks.

After you are finished checking out the application, stop the Open Liberty server by pressing CTRL+C in the shell session where you ran the server. Alternatively, you can run the liberty:stop goal from the finish directory in another shell session:

mvn liberty:stop

Adding health checks to microservices

Navigate to the start directory to begin.

Start Open Liberty in development mode, which starts the Open Liberty server and listens for file changes:

mvn liberty:dev

After you see the following message, your application server in development mode is ready.

Press the Enter key to run tests on demand.

The development mode holds your command prompt to listen for file changes. You need to open another command prompt to continue, or simply open the project in your editor.

A health report will be generated automatically for all services that enable MicroProfile Health. The mpHealth feature has already been enabled for you in the src/main/liberty/config/server.xml file.

All services must provide an implementation of the HealthCheck interface, which will be used to verify their health. MicroProfile Health offers health checks for both readiness and liveness. A readiness check allows third-party services, such as Kubernetes, to determine whether a microservice is ready to process requests. For example, a readiness check might check dependencies, such as database connections. A liveness check allows third-party services to determine whether a microservice is running. If the liveness check fails, the application can be terminated. For example, a liveness check might fail if the application runs out of memory.

server.xml

 1<server description="Sample Liberty server">
 2
 3  <featureManager>
 4    <feature>jaxrs-2.1</feature>
 5    <feature>jsonp-1.1</feature>
 6    <feature>cdi-2.0</feature>
 7    <feature>mpConfig-1.3</feature>
 8    <feature>mpRestClient-1.3</feature>
 9    <!-- tag::mpHealth[] -->
10    <feature>mpHealth-2.0</feature>
11    <!-- end::mpHealth[] -->
12  </featureManager>
13
14  <variable name="default.http.port" defaultValue="9080"/>
15  <variable name="default.https.port" defaultValue="9443"/>
16
17  <httpEndpoint host="*" httpPort="${default.http.port}"
18    httpsPort="${default.https.port}" id="defaultHttpEndpoint"/>
19
20  <webApplication location="guide-microprofile-health.war" contextRoot="/"/>
21</server>

Adding health checks to the system service

Create the SystemReadinessCheck class.
src/main/java/io/openliberty/guides/system/SystemReadinessCheck.java

SystemReadinessCheck.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2018, 2019 IBM Corporation and others.
 4 * All rights reserved. This program and the accompanying materials
 5 * are made available under the terms of the Eclipse Public License v1.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-v10.html
 8 *
 9 * Contributors:
10 *     IBM Corporation - Initial implementation
11 *******************************************************************************/
12// end::copyright[]
13// tag::SystemReadinessCheck[]
14package io.openliberty.guides.system;
15
16import javax.enterprise.context.ApplicationScoped;
17import org.eclipse.microprofile.health.Readiness;
18import org.eclipse.microprofile.health.HealthCheck;
19import org.eclipse.microprofile.health.HealthCheckResponse;
20
21// tag::Readiness[]
22@Readiness
23// end::Readiness[]
24// tag::ApplicationScoped[]
25@ApplicationScoped
26// end::ApplicationScoped[]
27public class SystemReadinessCheck implements HealthCheck {
28  @Override
29// tag::healthCheckResponse[]
30  public HealthCheckResponse call() {
31    // tag::defaultServer[]
32    if (!System.getProperty("wlp.server.name").equals("defaultServer")) {
33    // end::defaultServer[]
34      // tag::HealthCheckResponse-DOWN[]
35      // tag::HealthCheckResponse-named[]
36      return HealthCheckResponse.named(SystemResource.class.getSimpleName() + "Readiness")
37      // end::HealthCheckResponse-named[]
38                                .withData("default server", "not available").down()
39                                .build();
40      // end::HealthCheckResponse-DOWN[]
41    }
42    // tag::HealthCheckResponse-UP[]
43    return HealthCheckResponse.named(SystemResource.class.getSimpleName() + "Readiness")
44                              .withData("default server", "available").up().build();
45    // end::HealthCheckResponse-UP[]
46  }
47// end::healthCheckResponse[]
48}
49// end::SystemReadinessCheck[]

The @Readiness annotation indicates that this particular bean is a readiness health check procedure. By pairing this annotation with the ApplicationScoped context from the Contexts and Dependency Injections API, the bean is discovered automatically when the http://localhost:9080/health endpoint receives a request.

The call() method is used to return the health status of a particular service. In this case, you are simply checking if the server name is defaultServer and returning UP if it is, and DOWN otherwise. The HealthCheckResponse.named() method is used to indicate what service the health check is done for. Overall, this is a very simple implementation of the call() method. In a real development environment, you would want to orchestrate much more meaningful health checks.

Create the SystemLivenessCheck class.
src/main/java/io/openliberty/guides/system/SystemLivenessCheck.java

SystemLivenessCheck.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2018, 2019 IBM Corporation and others.
 4 * All rights reserved. This program and the accompanying materials
 5 * are made available under the terms of the Eclipse Public License v1.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-v10.html
 8 *
 9 * Contributors:
10 *     IBM Corporation - Initial implementation
11 *******************************************************************************/
12// end::copyright[]
13// tag::SystemLivenessCheck[]
14package io.openliberty.guides.system;
15
16import java.lang.management.ManagementFactory;
17import java.lang.management.MemoryMXBean;
18
19import javax.enterprise.context.ApplicationScoped;
20import org.eclipse.microprofile.health.Liveness;
21import org.eclipse.microprofile.health.HealthCheck;
22import org.eclipse.microprofile.health.HealthCheckResponse;
23
24// tag::Liveness[]
25@Liveness
26// end::Liveness[]
27@ApplicationScoped
28public class SystemLivenessCheck implements HealthCheck {
29
30  @Override
31  public HealthCheckResponse call() {
32    MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();
33    long memUsed = memBean.getHeapMemoryUsage().getUsed();
34    long memMax = memBean.getHeapMemoryUsage().getMax();
35
36    return HealthCheckResponse.named(SystemResource.class.getSimpleName() + "Liveness")
37                              .withData("memory used", memUsed)
38                              .withData("memory max", memMax)
39                              .state(memUsed < memMax * 0.9).build();
40  }
41}
42// end::SystemLivenessCheck[]

The @Liveness annotation indicates that this is a liveness health check procedure. In this case, you are checking the heap memory usage. If more than 90% of the maximum memory is being used, a status of DOWN is returned.

Adding health checks to the inventory service

Create the InventoryReadinessCheck class.
src/main/java/io/openliberty/guides/inventory/InventoryReadinessCheck.java

InventoryReadinessCheck.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2018, 2019 IBM Corporation and others.
 4 * All rights reserved. This program and the accompanying materials
 5 * are made available under the terms of the Eclipse Public License v1.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-v10.html
 8 *
 9 * Contributors:
10 *     IBM Corporation - Initial implementation
11 *******************************************************************************/
12// end::copyright[]
13// tag::InventoryReadinessCheck[]
14package io.openliberty.guides.inventory;
15
16import javax.enterprise.context.ApplicationScoped;
17import javax.inject.Inject;
18import javax.ws.rs.client.Client;
19import javax.ws.rs.client.ClientBuilder;
20import javax.ws.rs.core.MediaType;
21import javax.ws.rs.core.Response;
22import org.eclipse.microprofile.health.Readiness;
23import org.eclipse.microprofile.health.HealthCheck;
24import org.eclipse.microprofile.health.HealthCheckResponse;
25
26@Readiness
27@ApplicationScoped
28public class InventoryReadinessCheck implements HealthCheck {
29  @Inject
30  // tag::inventoryConfig[]
31  InventoryConfig config;
32  // end::inventoryConfig[]
33
34  // tag::isHealthy[]
35  public boolean isHealthy() {
36    if (config.isInMaintenance()) {
37      return false;
38    }
39    try {
40      String url = InventoryUtils.buildUrl("http", "localhost", config.getPortNumber(),
41          "/system/properties");
42      Client client = ClientBuilder.newClient();
43      Response response = client.target(url).request(MediaType.APPLICATION_JSON).get();
44      if (response.getStatus() != 200) {
45        return false;
46      }
47      return true;
48    } catch (Exception e) {
49      return false;
50    }
51  }
52  // end::isHealthy[]
53
54  @Override
55  public HealthCheckResponse call() {
56    if (!isHealthy()) {
57      return HealthCheckResponse
58          .named(InventoryResource.class.getSimpleName() + "Readiness")
59          .withData("services", "not available").down().build();
60    }
61    return HealthCheckResponse
62        .named(InventoryResource.class.getSimpleName() + "Readiness")
63        .withData("services", "available").up().build();
64  }
65
66}
67// end::InventoryReadinessCheck[]

This time, you are checking whether or not the service is in maintenance or if it’s down. For simplicity, the custom io_openliberty_guides_inventory_inMaintenance MicroProfile Config property defined in the resources/CustomConfigSource.json file is used to indicate whether the service is in maintenance or not. This file was already created for you. To check whether the service is down, make an HTTP GET request to the system service and check the status that is returned by the response. You make a GET request to the system service rather than the inventory service because the inventory service depends on the system service. In other words, the inventory service doesn’t work if the system service is down. If the status is not 200, then the service is not running. Based on these two factors, the isHealthy() method returns whether the inventory service is healthy.

If you are curious about the injected inventoryConfig object or if you want more information on MicroProfile Config, see Configuring microservices.

Create the InventoryLivenessCheck class.
src/main/java/io/openliberty/guides/inventory/InventoryLivenessCheck.java

InventoryLivenessCheck.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2018, 2019 IBM Corporation and others.
 4 * All rights reserved. This program and the accompanying materials
 5 * are made available under the terms of the Eclipse Public License v1.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-v10.html
 8 *
 9 * Contributors:
10 *     IBM Corporation - Initial implementation
11 *******************************************************************************/
12// end::copyright[]
13// tag::InventoryLivenessCheck[]
14package io.openliberty.guides.inventory;
15
16import javax.enterprise.context.ApplicationScoped;
17
18import java.lang.management.MemoryMXBean;
19import java.lang.management.ManagementFactory;
20
21import org.eclipse.microprofile.health.Liveness;
22
23import org.eclipse.microprofile.health.HealthCheck;
24import org.eclipse.microprofile.health.HealthCheckResponse;
25
26@Liveness
27@ApplicationScoped
28public class InventoryLivenessCheck implements HealthCheck {
29
30  @Override
31  public HealthCheckResponse call() {
32      MemoryMXBean memBean = ManagementFactory.getMemoryMXBean();
33      long memUsed = memBean.getHeapMemoryUsage().getUsed();
34      long memMax = memBean.getHeapMemoryUsage().getMax();
35
36      return HealthCheckResponse.named(InventoryResource.class.getSimpleName() + "Liveness")
37                                .withData("memory used", memUsed)
38                                .withData("memory max", memMax)
39                                .state(memUsed < memMax * 0.9).build();
40  }
41}
42// end::InventoryLivenessCheck[]

As with the system liveness check, you are checking the heap memory usage. If more than 90% of the maximum memory is being used, a DOWN status is returned.

CustomConfigSource.json

1{
2    "config_ordinal":700,
3    "io_openliberty_guides_inventory_inMaintenance":false
4}

Running the application

The Open Liberty server was started in development mode at the beginning of the guide and all the changes were automatically picked up.

While the server is running, navigate to the http://localhost:9080/health URL to find the aggregated liveness and readiness health reports on the two services.

You can also navigate to the http://localhost:9080/health/ready URL to view the readiness health report, or the http://localhost:9080/health/live URL to view the liveness health report.

Put the inventory service in maintenance by setting the io_openliberty_guides_inventory_inMaintenance property to true in the resources/CustomConfigSource.json file. Because this configuration file is picked up dynamically, simply refresh the http://localhost:9080/health URL to see that the state of the inventory service changed to DOWN. The overall state of the application also changed to DOWN as a result. Go to the http://localhost:9080/inventory/systems URL to verify that the inventory service is indeed in maintenance. Set the io_openliberty_guides_inventory_inMaintenance property back to false after you are done.

CustomConfigSource.json

1{
2    "config_ordinal":700,
3    "io_openliberty_guides_inventory_inMaintenance":false
4}

Testing health checks

You will implement several test methods to validate the health of the system and inventory services.

Create the HealthIT class.
src/test/java/it/io/openliberty/guides/health/HealthIT.java

HealthIT.java

  1// tag::copyright[]
  2/*******************************************************************************
  3 * Copyright (c) 2018, 2019 IBM Corporation and others.
  4 * All rights reserved. This program and the accompanying materials
  5 * are made available under the terms of the Eclipse Public License v1.0
  6 * which accompanies this distribution, and is available at
  7 * http://www.eclipse.org/legal/epl-v10.html
  8 *
  9 * Contributors:
 10 *     IBM Corporation - Initial implementation
 11 *******************************************************************************/
 12// end::copyright[]
 13// tag::HealthIT[]
 14package it.io.openliberty.guides.health;
 15
 16import static org.junit.jupiter.api.Assertions.assertEquals;
 17
 18import java.util.HashMap;
 19
 20import javax.json.JsonArray;
 21
 22import org.junit.jupiter.api.AfterEach;
 23import org.junit.jupiter.api.BeforeEach;
 24import org.junit.jupiter.api.Test;
 25
 26public class HealthIT {
 27
 28  private JsonArray servicesStates;
 29  private static HashMap<String, String> endpointData;
 30
 31  private String HEALTH_ENDPOINT = "health";
 32  private String READINESS_ENDPOINT = "health/ready";
 33  private String LIVENES_ENDPOINT = "health/live";
 34
 35  @BeforeEach
 36  public void setup() {
 37    endpointData = new HashMap<String, String>();
 38  }
 39
 40  @Test
 41  // tag::testIfServicesAreUp[]
 42  public void testIfServicesAreUp() {
 43    endpointData.put("SystemResourceReadiness", "UP");
 44    endpointData.put("SystemResourceLiveness", "UP");
 45    endpointData.put("InventoryResourceReadiness", "UP");
 46    endpointData.put("InventoryResourceLiveness", "UP");
 47
 48    servicesStates = HealthITUtil.connectToHealthEnpoint(200, HEALTH_ENDPOINT);
 49    checkStates(endpointData, servicesStates);
 50  }
 51  // end::testIfServicesAreUp[]
 52
 53  @Test
 54  // tag::testReadiness[]
 55  public void testReadiness() {
 56    endpointData.put("SystemResourceReadiness", "UP");
 57    endpointData.put("InventoryResourceReadiness", "UP");
 58
 59    servicesStates = HealthITUtil.connectToHealthEnpoint(200, READINESS_ENDPOINT);
 60    checkStates(endpointData, servicesStates);
 61  }
 62  // end::testReadiness[]
 63
 64  @Test
 65  // tag::testLiveness[]
 66  public void testLiveness() {
 67    endpointData.put("SystemResourceLiveness", "UP");
 68    endpointData.put("InventoryResourceLiveness", "UP");
 69
 70    servicesStates = HealthITUtil.connectToHealthEnpoint(200, LIVENES_ENDPOINT);
 71    checkStates(endpointData, servicesStates);
 72  }
 73  // end::testLiveness[]
 74
 75  @Test
 76  // tag::testIfInventoryServiceIsDown[]
 77  public void testIfInventoryServiceIsDown() {
 78    endpointData.put("SystemResourceReadiness", "UP");
 79    endpointData.put("SystemResourceLiveness", "UP");
 80    endpointData.put("InventoryResourceReadiness", "UP");
 81    endpointData.put("InventoryResourceLiveness", "UP");
 82
 83    servicesStates = HealthITUtil.connectToHealthEnpoint(200, HEALTH_ENDPOINT);
 84    checkStates(endpointData, servicesStates);
 85
 86    endpointData.put("InventoryResourceReadiness", "DOWN");
 87    HealthITUtil.changeInventoryProperty(HealthITUtil.INV_MAINTENANCE_FALSE,
 88        HealthITUtil.INV_MAINTENANCE_TRUE);
 89    servicesStates = HealthITUtil.connectToHealthEnpoint(503, HEALTH_ENDPOINT);
 90    checkStates(endpointData, servicesStates);
 91  }
 92  // end::testIfInventoryServiceIsDown[]
 93
 94  private void checkStates(HashMap<String, String> testData, JsonArray servStates) {
 95    testData.forEach((service, expectedState) -> {
 96      assertEquals(expectedState, HealthITUtil.getActualState(service, servStates),
 97          "The state of " + service + " service is not matching.");
 98    });
 99  }
100
101  @AfterEach
102  public void teardown() {
103    HealthITUtil.cleanUp();
104  }
105
106}
107// end::HealthIT[]

Let’s break down the test cases:

  • The testIfServicesAreUp() test case compares the generated health report with the actual status of the services.

  • The testReadiness() test case compares the generated health report for the readiness checks with the actual status of the services.

  • The testLiveness() test case compares the generated health report for the liveness checks with the actual status of the services.

  • The testIfInventoryServiceIsDown() test case puts the inventory service in maintenance by setting the io_openliberty_guides_inventory_inMaintenance property to true and comparing the generated health report with the actual status of the services.

A few more tests were included to verify the basic functionality of the system and inventory services. They can be found under the src/test/java/it/io/openliberty/guides/inventory/InventoryEndpointIT.java and src/test/java/it/io/openliberty/guides/system/SystemEndpointIT.java files. If a test failure occurs, then you might have introduced a bug into the code. These tests run automatically as a part of the integration test suite.

CustomConfigSource.json

1{
2    "config_ordinal":700,
3    "io_openliberty_guides_inventory_inMaintenance":false
4}

InventoryEndpointIT.java

  1// tag::copyright[]
  2/*******************************************************************************
  3 * Copyright (c) 2017, 2019 IBM Corporation and others.
  4 * All rights reserved. This program and the accompanying materials
  5 * are made available under the terms of the Eclipse Public License v1.0
  6 * which accompanies this distribution, and is available at
  7 * http://www.eclipse.org/legal/epl-v10.html
  8 *
  9 * Contributors:
 10 *     IBM Corporation - Initial implementation
 11 *******************************************************************************/
 12// end::copyright[]
 13// tag::testClass[]
 14package it.io.openliberty.guides.inventory;
 15
 16import static org.junit.jupiter.api.Assertions.assertEquals;
 17import static org.junit.jupiter.api.Assertions.assertTrue;
 18
 19import javax.json.JsonArray;
 20import javax.json.JsonObject;
 21import javax.ws.rs.client.Client;
 22import javax.ws.rs.client.ClientBuilder;
 23import javax.ws.rs.core.MediaType;
 24import javax.ws.rs.core.Response;
 25
 26import org.apache.cxf.jaxrs.provider.jsrjsonp.JsrJsonpProvider;
 27import org.junit.jupiter.api.AfterEach;
 28import org.junit.jupiter.api.BeforeAll;
 29import org.junit.jupiter.api.BeforeEach;
 30import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
 31import org.junit.jupiter.api.Order;
 32import org.junit.jupiter.api.Test;
 33import org.junit.jupiter.api.TestMethodOrder;
 34
 35@TestMethodOrder(OrderAnnotation.class)
 36public class InventoryEndpointIT {
 37
 38  private static String port;
 39  private static String baseUrl;
 40
 41  private Client client;
 42
 43  private final String SYSTEM_PROPERTIES = "system/properties";
 44  private final String INVENTORY_SYSTEMS = "inventory/systems";
 45
 46  @BeforeAll
 47  public static void oneTimeSetup() {
 48    port = System.getProperty("default.http.port");
 49    baseUrl = "http://localhost:" + port + "/";
 50  }
 51
 52  @BeforeEach
 53  public void setup() {
 54    client = ClientBuilder.newClient();
 55    client.register(JsrJsonpProvider.class);
 56  }
 57
 58  @AfterEach
 59  public void teardown() {
 60    client.close();
 61  }
 62
 63  // tag::tests[]
 64  @Test
 65  @Order(1)
 66  // tag::testHostRegistration[]
 67  public void testHostRegistration() {
 68    this.visitLocalhost();
 69
 70    Response response = this.getResponse(baseUrl + INVENTORY_SYSTEMS);
 71    this.assertResponse(baseUrl, response);
 72
 73    JsonObject obj = response.readEntity(JsonObject.class);
 74
 75    JsonArray systems = obj.getJsonArray("systems");
 76
 77    boolean localhostExists = false;
 78    for (int n = 0; n < systems.size(); n++) {
 79      localhostExists = systems.getJsonObject(n).get("hostname").toString()
 80          .contains("localhost");
 81      if (localhostExists) {
 82        break;
 83      }
 84    }
 85    assertTrue(localhostExists, "A host was registered, but it was not localhost");
 86
 87    response.close();
 88  }
 89  // end::testHostRegistration[]
 90
 91  @Test
 92  @Order(2)
 93  // tag::testSystemPropertiesMatch[]
 94  public void testSystemPropertiesMatch() {
 95    Response invResponse = this.getResponse(baseUrl + INVENTORY_SYSTEMS);
 96    Response sysResponse = this.getResponse(baseUrl + SYSTEM_PROPERTIES);
 97
 98    this.assertResponse(baseUrl, invResponse);
 99    this.assertResponse(baseUrl, sysResponse);
100
101    JsonObject jsonFromInventory = (JsonObject) invResponse.readEntity(JsonObject.class)
102                                                           .getJsonArray("systems")
103                                                           .getJsonObject(0)
104                                                           .get("properties");
105
106    JsonObject jsonFromSystem = sysResponse.readEntity(JsonObject.class);
107
108    String osNameFromInventory = jsonFromInventory.getString("os.name");
109    String osNameFromSystem = jsonFromSystem.getString("os.name");
110    this.assertProperty("os.name", "localhost", osNameFromSystem,
111                        osNameFromInventory);
112
113    String userNameFromInventory = jsonFromInventory.getString("user.name");
114    String userNameFromSystem = jsonFromSystem.getString("user.name");
115    this.assertProperty("user.name", "localhost", userNameFromSystem,
116                        userNameFromInventory);
117
118    invResponse.close();
119    sysResponse.close();
120  }
121  // end::testSystemPropertiesMatch[]
122
123  @Test
124  @Order(3)
125  // tag::testUnknownHost[]
126  public void testUnknownHost() {
127    Response response = this.getResponse(baseUrl + INVENTORY_SYSTEMS);
128    this.assertResponse(baseUrl, response);
129
130    Response badResponse = client.target(baseUrl + INVENTORY_SYSTEMS + "/"
131        + "badhostname").request(MediaType.APPLICATION_JSON).get();
132
133    String obj = badResponse.readEntity(String.class);
134
135    boolean isError = obj.contains("error");
136    assertTrue(isError,
137        "badhostname is not a valid host but it didn't raise an error");
138
139    response.close();
140    badResponse.close();
141  }
142
143  // end::testUnknownHost[]
144  // end::tests[]
145  // tag::helpers[]
146  // tag::javadoc[]
147  /**
148   * <p>
149   * Returns response information from the specified URL.
150   * </p>
151   *
152   * @param url
153   *          - target URL.
154   * @return Response object with the response from the specified URL.
155   */
156  // end::javadoc[]
157  private Response getResponse(String url) {
158    return client.target(url).request().get();
159  }
160
161  // tag::javadoc[]
162  /**
163   * <p>
164   * Asserts that the given URL has the correct response code of 200.
165   * </p>
166   *
167   * @param url
168   *          - target URL.
169   * @param response
170   *          - response received from the target URL.
171   */
172  // end::javadoc[]
173  private void assertResponse(String url, Response response) {
174    assertEquals(200, response.getStatus(), "Incorrect response code from " + url);
175  }
176
177  // tag::javadoc[]
178  /**
179   * Asserts that the specified JVM system property is equivalent in both the
180   * system and inventory services.
181   *
182   * @param propertyName
183   *          - name of the system property to check.
184   * @param hostname
185   *          - name of JVM's host.
186   * @param expected
187   *          - expected name.
188   * @param actual
189   *          - actual name.
190   */
191  // end::javadoc[]
192  private void assertProperty(String propertyName, String hostname,
193      String expected, String actual) {
194    assertEquals(expected, actual, "JVM system property [" + propertyName + "] "
195        + "in the system service does not match the one stored in "
196        + "the inventory service for " + hostname);
197  }
198
199  // tag::javadoc[]
200  /**
201   * Makes a simple GET request to inventory/localhost.
202   */
203  // end::javadoc[]
204  private void visitLocalhost() {
205    Response response = this.getResponse(baseUrl + SYSTEM_PROPERTIES);
206    this.assertResponse(baseUrl, response);
207    response.close();
208
209    Response targetResponse = client.target(baseUrl + INVENTORY_SYSTEMS
210        + "/localhost").request().get();
211    targetResponse.close();
212  }
213  // end::helpers[]
214}
215// end::testClass[]

SystemEndpointIT.java

 1//tag::copyright[]
 2/*******************************************************************************
 3* Copyright (c) 2017, 2019 IBM Corporation and others.
 4* All rights reserved. This program and the accompanying materials
 5* are made available under the terms of the Eclipse Public License v1.0
 6* which accompanies this distribution, and is available at
 7* http://www.eclipse.org/legal/epl-v10.html
 8*
 9* Contributors:
10*     IBM Corporation - initial API and implementation
11*******************************************************************************/
12// end::copyright[]
13package it.io.openliberty.guides.system;
14
15import static org.junit.jupiter.api.Assertions.assertEquals;
16
17import javax.json.JsonObject;
18import javax.ws.rs.client.Client;
19import javax.ws.rs.client.ClientBuilder;
20import javax.ws.rs.client.WebTarget;
21import javax.ws.rs.core.Response;
22
23import org.apache.cxf.jaxrs.provider.jsrjsonp.JsrJsonpProvider;
24import org.junit.jupiter.api.Test;
25
26public class SystemEndpointIT {
27
28  @Test
29  public void testGetProperties() {
30    String port = System.getProperty("default.http.port");
31    String url = "http://localhost:" + port + "/";
32
33    Client client = ClientBuilder.newClient();
34    client.register(JsrJsonpProvider.class);
35
36    WebTarget target = client.target(url + "system/properties");
37    Response response = target.request().get();
38
39    assertEquals(200, response.getStatus(), "Incorrect response code from " + url);
40
41    JsonObject obj = response.readEntity(JsonObject.class);
42
43    assertEquals(System.getProperty("os.name"), obj.getString("os.name"),
44        "The system property for the local and remote JVM should match");
45
46    response.close();
47  }
48}

Running the tests

Since you started Open Liberty in development mode at the start of the guide, press the enter/return key to run the tests.

You see the following output:

[INFO] -------------------------------------------------------
[INFO]  T E S T S
[INFO] -------------------------------------------------------
[INFO] Running it.io.openliberty.guides.health.HealthIT
[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.463 s - in it.io.openliberty.guides.health.HealthIT
[INFO] Running it.io.openliberty.guides.system.SystemEndpointIT
[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.009 s - in it.io.openliberty.guides.system.SystemEndpointIT
[INFO] Running it.io.openliberty.guides.inventory.InventoryEndpointIT
[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.102 s - in it.io.openliberty.guides.inventory.InventoryEndpointIT
[INFO]
[INFO] Results:
[INFO]
[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0

To see whether the tests detect a failure, manually change the configuration of io_openliberty_guides_inventory_inMaintenance from false to true in the resources/CustomConfigSource.json file. Rerun the tests to see a test failure occur. The test failure occurs because the initial status of the inventory service is DOWN.

When you are done checking out the service, exit development mode by pressing CTRL+C in the shell session where you ran the server, or by typing q and then pressing the enter/return key.

Great work! You’re done!

You just learned how to add health checks to report the states of microservices by using MicroProfile Health in Open Liberty. Then, you wrote tests to validate the generated health report.

Feel free to try one of the related MicroProfile guides. They demonstrate additional technologies that you can learn and expand on top of what you built here.

Guide Attribution

Adding health reports to microservices by Open Liberty is licensed under CC BY-ND 4.0

Copied to clipboard
Copy code block
Copy file contents

Prerequisites:

Nice work! Where to next?

What did you think of this guide?

Extreme Dislike Dislike Like Extreme Like

What could make this guide better?

Raise an issue to share feedback

Create a pull request to contribute to this guide

Need help?

Ask a question on Stack Overflow

Like Open Liberty? Star our repo on GitHub.

Star