Injecting dependencies into microservices

duration 15 minutes

Prerequisites:

Learn how to use Contexts and Dependency Injection (CDI) to manage scopes and inject dependencies into microservices.

What you’ll learn

You will learn how to use Contexts and Dependency Injection (CDI) to manage scopes and inject dependencies in a simple inventory management application.

The application that you will be working with is an inventory service, which stores the information about various JVMs that run on different systems. Whenever a request is made to the inventory service to retrieve the JVM system properties of a particular host, the inventory service communicates with the system service on that host to get these system properties. The system properties are then stored and returned.

You will use scopes to bind objects in this application to their well-defined contexts. CDI provides a variety of scopes for you to work with and while you will not use all of them in this guide, there is one for almost every scenario that you may encounter. Scopes are defined by using CDI annotations. You will also use dependency injection to inject one bean into another to make use of its functionalities. This enables you to inject the bean in its specified context without having to instantiate it yourself.

The implementation of the application and its services are provided for you in the start/src directory. The system service can be found in the start/src/main/java/io/openliberty/guides/system directory, and the inventory service can be found in the start/src/main/java/io/openliberty/guides/inventory directory. If you want to learn more about RESTful web services and how to build them, see Creating a RESTful web service for details about how to build the system service. The inventory service is built in a similar way.

What is CDI?

Contexts and Dependency Injection (CDI) defines a rich set of complementary services that improve the application structure. The most fundamental services that are provided by CDI are contexts that bind the lifecycle of stateful components to well-defined contexts, and dependency injection that is the ability to inject components into an application in a typesafe way. With CDI, the container does all the daunting work of instantiating dependencies, and controlling exactly when and how these components are instantiated and destroyed.

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-cdi-intro.git
cd guide-cdi-intro

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

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

Before you begin, make sure you have all the necessary prerequisites.

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:

cd finish
mvn liberty:run

After you see the following message, your Liberty instance is ready:

The defaultServer server is ready to run a smarter planet.

Point your browser to the http://localhost:9080/inventory/systems URL. This is the starting point of the inventory service and it displays the current contents of the inventory. As you might expect, these are empty because nothing is stored in the inventory yet. Next, point your browser to the http://localhost:9080/inventory/systems/localhost URL. You see a result in JSON format with the system properties of your local JVM. When you visit this URL, these system properties are automatically stored in the inventory. Go back to http://localhost:9080/inventory/systems and you see a new entry for localhost. For simplicity, only the OS name and username are shown here for each host. You can repeat this process for your own hostname or any other machine that is running the system service.

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

mvn liberty:stop

Handling dependencies in the application

You will use CDI to inject dependencies into the inventory manager application and learn how to manage the life cycles of your objects.

Managing scopes and contexts

Navigate to the start directory to begin.

When you run Open Liberty in dev mode, dev mode listens for file changes and automatically recompiles and deploys your updates whenever you save a new change. Run the following goal to start Open Liberty in dev mode:

mvn liberty:dev

After you see the following message, your Liberty instance is ready in dev mode:

**************************************************************
*    Liberty is running in dev mode.

Dev mode holds your command-line session to listen for file changes. Open another command-line session to continue, or open the project in your editor.

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

InventoryManager.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2017, 2022 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 2.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-2.0/
 8 *
 9 * SPDX-License-Identifier: EPL-2.0
10 *******************************************************************************/
11// end::copyright[]
12package io.openliberty.guides.inventory;
13
14import java.util.ArrayList;
15import java.util.Collections;
16import java.util.List;
17import java.util.Properties;
18import io.openliberty.guides.inventory.model.InventoryList;
19import io.openliberty.guides.inventory.model.SystemData;
20import jakarta.enterprise.context.ApplicationScoped;
21
22// tag::ApplicationScoped[]
23@ApplicationScoped
24// end::ApplicationScoped[]
25public class InventoryManager {
26
27  private List<SystemData> systems = Collections.synchronizedList(new ArrayList<>());
28
29  // tag::add[]
30  public void add(String hostname, Properties systemProps) {
31    Properties props = new Properties();
32    props.setProperty("os.name", systemProps.getProperty("os.name"));
33    props.setProperty("user.name", systemProps.getProperty("user.name"));
34
35    SystemData system = new SystemData(hostname, props);
36    if (!systems.contains(system)) {
37      systems.add(system);
38    }
39  }
40  // end::add[]
41
42  // tag::list[]
43  public InventoryList list() {
44    return new InventoryList(systems);
45  }
46  // end::list[]
47}

This bean contains two simple functions. The add() function is for adding entries to the inventory. The list() function is for listing all the entries currently stored in the inventory.

This bean must be persistent between all of the clients, which means multiple clients need to share the same instance. To achieve this by using CDI, you can simply add the @ApplicationScoped annotation onto the class.

This annotation indicates that this particular bean is to be initialized once per application. By making it application-scoped, the container ensures that the same instance of the bean is used whenever it is injected into the application.

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

InventoryResource.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2017, 2022 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 2.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-2.0/
 8 *
 9 * SPDX-License-Identifier: EPL-2.0
10 *******************************************************************************/
11// end::copyright[]
12package io.openliberty.guides.inventory;
13
14import java.util.Properties;
15import jakarta.enterprise.context.ApplicationScoped;
16import jakarta.inject.Inject;
17import jakarta.ws.rs.GET;
18import jakarta.ws.rs.Path;
19import jakarta.ws.rs.PathParam;
20import jakarta.ws.rs.Produces;
21import jakarta.ws.rs.core.MediaType;
22import jakarta.ws.rs.core.Response;
23import io.openliberty.guides.inventory.model.InventoryList;
24import io.openliberty.guides.inventory.client.SystemClient;
25
26// tag::ApplicationScoped[]
27@ApplicationScoped
28// end::ApplicationScoped[]
29// tag::endpoint[]
30@Path("/systems")
31// end::endpoint[]
32// tag::InventoryResource[]
33public class InventoryResource {
34
35  // tag::inject[]
36  @Inject
37  // end::inject[]
38  InventoryManager manager;
39
40  // tag::inject2[]
41  @Inject
42  // end::inject2[]
43  SystemClient systemClient;
44
45  @GET
46  @Path("/{hostname}")
47  @Produces(MediaType.APPLICATION_JSON)
48  public Response getPropertiesForHost(@PathParam("hostname") String hostname) {
49    // Get properties for host
50    // tag::properties[]
51    Properties props = systemClient.getProperties(hostname);
52    // end::properties[]
53    if (props == null) {
54      return Response.status(Response.Status.NOT_FOUND)
55                     .entity("{ \"error\" : \"Unknown hostname " + hostname
56                             + " or the inventory service may not be running "
57                             + "on the host machine \" }")
58                     .build();
59    }
60
61    // Add to inventory
62    // tag::managerAdd[]
63    manager.add(hostname, props);
64    // end::managerAdd[]
65    return Response.ok(props).build();
66  }
67
68  @GET
69  @Produces(MediaType.APPLICATION_JSON)
70  public InventoryList listContents() {
71    // tag::managerList[]
72    return manager.list();
73    // end::managerList[]
74  }
75}
76// tag::InventoryResource[]

The inventory resource is a RESTful service that is served at the inventory/systems endpoint.

Annotating a class with the @ApplicationScoped annotation indicates that the bean is initialized once and is shared between all requests while the application runs.

If you want this bean to be initialized once for every request, you can annotate the class with the @RequestScoped annotation instead. With the @RequestScoped annotation, the bean is instantiated when the request is received and destroyed when a response is sent back to the client. A request scope is short-lived.

Injecting a dependency

Refer to the InventoryResource class you created above.

The @Inject annotation indicates a dependency injection. You are injecting your InventoryManager and SystemClient beans into the InventoryResource class. This injects the beans in their specified context and makes all of their functionalities available without the need of instantiating them yourself. The injected bean InventoryManager can then be invoked directly through the manager.add(hostname, props) and manager.list() function calls. The injected bean SystemClient can be invoked through the systemClient.getProperties(hostname) function call.

Finally, you have a client component SystemClient that can be found in the src/main/java/io/openliberty/guides/inventory/client directory. This class communicates with the system service to retrieve the JVM system properties for a particular host that exposes them. This class also contains detailed Javadocs that you can read for reference.

Your inventory application is now completed.

InventoryResource.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2017, 2022 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 2.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-2.0/
 8 *
 9 * SPDX-License-Identifier: EPL-2.0
10 *******************************************************************************/
11// end::copyright[]
12package io.openliberty.guides.inventory;
13
14import java.util.Properties;
15import jakarta.enterprise.context.ApplicationScoped;
16import jakarta.inject.Inject;
17import jakarta.ws.rs.GET;
18import jakarta.ws.rs.Path;
19import jakarta.ws.rs.PathParam;
20import jakarta.ws.rs.Produces;
21import jakarta.ws.rs.core.MediaType;
22import jakarta.ws.rs.core.Response;
23import io.openliberty.guides.inventory.model.InventoryList;
24import io.openliberty.guides.inventory.client.SystemClient;
25
26// tag::ApplicationScoped[]
27@ApplicationScoped
28// end::ApplicationScoped[]
29// tag::endpoint[]
30@Path("/systems")
31// end::endpoint[]
32// tag::InventoryResource[]
33public class InventoryResource {
34
35  // tag::inject[]
36  @Inject
37  // end::inject[]
38  InventoryManager manager;
39
40  // tag::inject2[]
41  @Inject
42  // end::inject2[]
43  SystemClient systemClient;
44
45  @GET
46  @Path("/{hostname}")
47  @Produces(MediaType.APPLICATION_JSON)
48  public Response getPropertiesForHost(@PathParam("hostname") String hostname) {
49    // Get properties for host
50    // tag::properties[]
51    Properties props = systemClient.getProperties(hostname);
52    // end::properties[]
53    if (props == null) {
54      return Response.status(Response.Status.NOT_FOUND)
55                     .entity("{ \"error\" : \"Unknown hostname " + hostname
56                             + " or the inventory service may not be running "
57                             + "on the host machine \" }")
58                     .build();
59    }
60
61    // Add to inventory
62    // tag::managerAdd[]
63    manager.add(hostname, props);
64    // end::managerAdd[]
65    return Response.ok(props).build();
66  }
67
68  @GET
69  @Produces(MediaType.APPLICATION_JSON)
70  public InventoryList listContents() {
71    // tag::managerList[]
72    return manager.list();
73    // end::managerList[]
74  }
75}
76// tag::InventoryResource[]

SystemClient.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2017, 2023 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 2.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-2.0/
 8 *
 9 * SPDX-License-Identifier: EPL-2.0
10 *******************************************************************************/
11// end::copyright[]
12package io.openliberty.guides.inventory.client;
13
14import jakarta.enterprise.context.ApplicationScoped;
15import jakarta.inject.Inject;
16import jakarta.ws.rs.client.Client;
17import jakarta.ws.rs.client.ClientBuilder;
18import jakarta.ws.rs.client.Invocation.Builder;
19import jakarta.ws.rs.core.HttpHeaders;
20import jakarta.ws.rs.core.MediaType;
21import jakarta.ws.rs.core.Response;
22import jakarta.ws.rs.core.Response.Status;
23import java.util.Properties;
24import java.net.URI;
25import org.eclipse.microprofile.config.inject.ConfigProperty;
26
27@ApplicationScoped
28public class SystemClient {
29
30    // Constants for building URI to the system service.
31    private final String SYSTEM_PROPERTIES = "/system/properties";
32    private final String PROTOCOL = "http";
33
34    @Inject
35    @ConfigProperty(name = "system.http.port")
36    String SYS_HTTP_PORT;
37
38    // Wrapper function that gets properties
39    public Properties getProperties(String hostname) {
40        Properties properties = null;
41        Client client = ClientBuilder.newClient();
42        try {
43            Builder builder = getBuilder(hostname, client);
44            properties = getPropertiesHelper(builder);
45        } catch (Exception e) {
46            System.err.println(
47            "Exception thrown while getting properties: " + e.getMessage());
48        } finally {
49            client.close();
50        }
51        return properties;
52    }
53
54    // Method that creates the client builder
55    private Builder getBuilder(String hostname, Client client) throws Exception {
56        URI uri = new URI(
57                      PROTOCOL, null, hostname, Integer.valueOf(SYS_HTTP_PORT),
58                      SYSTEM_PROPERTIES, null, null);
59        String urlString = uri.toString();
60        Builder builder = client.target(urlString).request();
61        builder.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON);
62        return builder;
63    }
64
65    // Helper method that processes the request
66    private Properties getPropertiesHelper(Builder builder) throws Exception {
67        Response response = builder.get();
68        if (response.getStatus() == Status.OK.getStatusCode()) {
69            return response.readEntity(Properties.class);
70        } else {
71            System.err.println("Response Status is not OK.");
72            return null;
73        }
74    }
75}

Running the application

You started the Open Liberty in dev mode at the beginning of the guide, so all the changes were automatically picked up.

You can find the inventory and system services at the following URLs:

Testing the inventory application

While you can test your application manually, you should rely on automated tests because they trigger a failure whenever a code change introduces a defect. Because the application is a RESTful web service application, you can use JUnit and the RESTful web service Client API to write tests. In testing the functionality of the application, the scopes and dependencies are being tested.

Create the InventoryEndpointIT class.
src/test/java/it/io/openliberty/guides/inventory/InventoryEndpointIT.java

InventoryEndpointIT.java

  1// tag::copyright[]
  2/*******************************************************************************
  3 * Copyright (c) 2017, 2022 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 2.0
  6 * which accompanies this distribution, and is available at
  7 * http://www.eclipse.org/legal/epl-2.0/
  8 *
  9 * SPDX-License-Identifier: EPL-2.0
 10 *******************************************************************************/
 11// end::copyright[]
 12// tag::testClass[]
 13package it.io.openliberty.guides.inventory;
 14
 15import static org.junit.jupiter.api.Assertions.assertEquals;
 16import static org.junit.jupiter.api.Assertions.assertTrue;
 17
 18import jakarta.json.JsonArray;
 19import jakarta.json.JsonObject;
 20import jakarta.ws.rs.client.Client;
 21import jakarta.ws.rs.client.ClientBuilder;
 22import jakarta.ws.rs.core.MediaType;
 23import jakarta.ws.rs.core.Response;
 24
 25import org.junit.jupiter.api.AfterEach;
 26import org.junit.jupiter.api.BeforeEach;
 27import org.junit.jupiter.api.BeforeAll;
 28// tag::MethodOrderer[]
 29import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
 30// end::MethodOrderer[]
 31import org.junit.jupiter.api.Order;
 32import org.junit.jupiter.api.Test;
 33import org.junit.jupiter.api.TestMethodOrder;
 34
 35// tag::TestMethodOrder[]
 36@TestMethodOrder(OrderAnnotation.class)
 37// end::TestMethodOrder[]
 38public class InventoryEndpointIT {
 39
 40  private static String port;
 41  private static String baseUrl;
 42
 43  private Client client;
 44
 45  private final String SYSTEM_PROPERTIES = "system/properties";
 46  private final String INVENTORY_SYSTEMS = "inventory/systems";
 47
 48  // tag::BeforeAll[]
 49  @BeforeAll
 50  // end::BeforeAll[]
 51  // tag::oneTimeSetup[]
 52  public static void oneTimeSetup() {
 53    port = System.getProperty("http.port");
 54    baseUrl = "http://localhost:" + port + "/";
 55  }
 56  // end::oneTimeSetup[]
 57
 58  // tag::BeforeEach[]
 59  @BeforeEach
 60  // end::BeforeEach[]
 61  // tag::setup[]
 62  public void setup() {
 63    client = ClientBuilder.newClient();
 64  }
 65  // end::setup[]
 66
 67  // tag::AfterEach[]
 68  @AfterEach
 69  // end::AfterEach[]
 70  // tag::teardown[]
 71  public void teardown() {
 72    client.close();
 73  }
 74  // end::teardown[]
 75
 76  // tag::tests[]
 77  // tag::Test1[]
 78  @Test
 79  // end::Test1[]
 80  // tag::Order1[]
 81  @Order(1)
 82  // end::Order1[]
 83  // tag::testHostRegistration[]
 84  public void testHostRegistration() {
 85    this.visitLocalhost();
 86
 87    Response response = this.getResponse(baseUrl + INVENTORY_SYSTEMS);
 88    this.assertResponse(baseUrl, response);
 89
 90    JsonObject obj = response.readEntity(JsonObject.class);
 91
 92    JsonArray systems = obj.getJsonArray("systems");
 93
 94    boolean localhostExists = false;
 95    for (int n = 0; n < systems.size(); n++) {
 96      localhostExists = systems.getJsonObject(n)
 97                                .get("hostname").toString()
 98                                .contains("localhost");
 99      if (localhostExists) {
100          break;
101      }
102    }
103    assertTrue(localhostExists,
104              "A host was registered, but it was not localhost");
105
106    response.close();
107  }
108  // end::testHostRegistration[]
109
110  // tag::Test2[]
111  @Test
112  // end::Test2[]
113  // tag::Order2[]
114  @Order(2)
115  // end::Order2[]
116  // tag::testSystemPropertiesMatch[]
117  public void testSystemPropertiesMatch() {
118    Response invResponse = this.getResponse(baseUrl + INVENTORY_SYSTEMS);
119    Response sysResponse = this.getResponse(baseUrl + SYSTEM_PROPERTIES);
120
121    this.assertResponse(baseUrl, invResponse);
122    this.assertResponse(baseUrl, sysResponse);
123
124    JsonObject jsonFromInventory = (JsonObject) invResponse.readEntity(JsonObject.class)
125                                                           .getJsonArray("systems")
126                                                           .getJsonObject(0)
127                                                           .get("properties");
128
129    JsonObject jsonFromSystem = sysResponse.readEntity(JsonObject.class);
130
131    String osNameFromInventory = jsonFromInventory.getString("os.name");
132    String osNameFromSystem = jsonFromSystem.getString("os.name");
133    this.assertProperty("os.name", "localhost", osNameFromSystem,
134                        osNameFromInventory);
135
136    String userNameFromInventory = jsonFromInventory.getString("user.name");
137    String userNameFromSystem = jsonFromSystem.getString("user.name");
138    this.assertProperty("user.name", "localhost", userNameFromSystem,
139                        userNameFromInventory);
140
141    invResponse.close();
142    sysResponse.close();
143  }
144  // end::testSystemPropertiesMatch[]
145
146  // tag::Test3[]
147  @Test
148  // end::Test3[]
149  // tag::Order3[]
150  @Order(3)
151  // end::Order3[]
152  // tag::testUnknownHost[]
153  public void testUnknownHost() {
154    Response response = this.getResponse(baseUrl + INVENTORY_SYSTEMS);
155    this.assertResponse(baseUrl, response);
156
157    Response badResponse = client.target(baseUrl + INVENTORY_SYSTEMS + "/"
158        + "badhostname").request(MediaType.APPLICATION_JSON).get();
159
160    assertEquals(404, badResponse.getStatus(),
161        "BadResponse expected status: 404. Response code not as expected.");
162
163    String obj = badResponse.readEntity(String.class);
164
165    boolean isError = obj.contains("error");
166    assertTrue(isError,
167              "badhostname is not a valid host but it didn't raise an error");
168
169    response.close();
170    badResponse.close();
171  }
172  // end::testUnknownHost[]
173  // end::tests[]
174
175  private Response getResponse(String url) {
176    return client.target(url).request().get();
177  }
178
179  private void assertResponse(String url, Response response) {
180    assertEquals(200, response.getStatus(), "Incorrect response code from " + url);
181  }
182
183  private void assertProperty(String propertyName, String hostname,
184      String expected, String actual) {
185    assertEquals(expected, actual, "JVM system property [" + propertyName + "] "
186        + "in the system service does not match the one stored in "
187        + "the inventory service for " + hostname);
188  }
189
190  private void visitLocalhost() {
191    Response response = this.getResponse(baseUrl + SYSTEM_PROPERTIES);
192    this.assertResponse(baseUrl, response);
193    response.close();
194
195    Response targetResponse = client.target(baseUrl + INVENTORY_SYSTEMS
196        + "/localhost").request().get();
197    targetResponse.close();
198  }
199}
200// end::testClass[]

The @BeforeAll annotation is placed on a method that runs before any of the test cases. In this case, the oneTimeSetup() method retrieves the port number for the Open Liberty and builds a base URL string that is used throughout the tests.

The @BeforeEach and @AfterEach annotations are placed on methods that run before and after every test case. These methods are generally used to perform any setup and teardown tasks. In this case, the setup() method creates a JAX-RS client, which makes HTTP requests to the inventory service. The teardown() method simply destroys this client instance.

See the following descriptions of the test cases:

  • testHostRegistration() verifies that a host is correctly added to the inventory.

  • testSystemPropertiesMatch() verifies that the JVM system properties returned by the system service match the ones stored in the inventory service.

  • testUnknownHost() verifies that an unknown host or a host that does not expose their JVM system properties is correctly handled as an error.

To force these test cases to run in a particular order, annotate your InventoryEndpointIT test class with the @TestMethodOrder(OrderAnnotation.class) annotation. OrderAnnotation.class runs test methods in numerical order, according to the values specified in the @Order annotation. You can also create a custom MethodOrderer class or use built-in MethodOrderer implementations, such as OrderAnnotation.class, Alphanumeric.class, or Random.class. Label your test cases with the @Test annotation so that they automatically run when your test class runs.

Finally, the src/test/java/it/io/openliberty/guides/system/SystemEndpointIT.java file is included for you to test the basic functionality of the system service. If a test failure occurs, then you might have introduced a bug into the code.

SystemEndpointIT.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2017, 2022 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 2.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-2.0/
 8 *
 9 * SPDX-License-Identifier: EPL-2.0
10 *******************************************************************************/
11// end::copyright[]
12package it.io.openliberty.guides.system;
13
14import static org.junit.jupiter.api.Assertions.assertEquals;
15import jakarta.json.JsonObject;
16import jakarta.ws.rs.client.Client;
17import jakarta.ws.rs.client.ClientBuilder;
18import jakarta.ws.rs.client.WebTarget;
19import jakarta.ws.rs.core.Response;
20
21import org.junit.jupiter.api.Test;
22
23public class SystemEndpointIT {
24
25 @Test
26 public void testGetProperties() {
27     String port = System.getProperty("http.port");
28     String url = "http://localhost:" + port + "/";
29
30     Client client = ClientBuilder.newClient();
31
32     WebTarget target = client.target(url + "system/properties");
33     Response response = target.request().get();
34
35     assertEquals(200, response.getStatus(), "Incorrect response code from " + url);
36
37     JsonObject obj = response.readEntity(JsonObject.class);
38
39     assertEquals(System.getProperty("os.name"),
40                  obj.getString("os.name"),
41                  "The system property for the local and remote JVM should match");
42     response.close();
43     client.close();
44 }
45}

Running the tests

Because you started Open Liberty in dev mode, you can run the tests by pressing the enter/return key from the command-line session where you started dev mode.

If the tests pass, you see a similar output to the following example:

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.system.SystemEndpointIT
Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.99 sec - in it.io.openliberty.guides.system.SystemEndpointIT
Running it.io.openliberty.guides.inventory.InventoryEndpointIT
[err] Runtime exception: RESTEASY004655: Unable to invoke request: java.net.UnknownHostException: badhostname: nodename nor servname provided, or not known
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.325 sec - in it.io.openliberty.guides.inventory.InventoryEndpointIT

Results :

Tests run: 4, Failures: 0, Errors: 0, Skipped: 0

The error messages are expected and result from a request to a bad or an unknown hostname. This request is made in the testUnknownHost() test from the InventoryEndpointIT integration test.

To see whether the tests detect a failure, change the endpoint for the inventory service in the src/main/java/io/openliberty/guides/inventory/InventoryResource.java file to something else. Then, run the tests again to see that a test failure occurs.

InventoryResource.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2017, 2022 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 2.0
 6 * which accompanies this distribution, and is available at
 7 * http://www.eclipse.org/legal/epl-2.0/
 8 *
 9 * SPDX-License-Identifier: EPL-2.0
10 *******************************************************************************/
11// end::copyright[]
12package io.openliberty.guides.inventory;
13
14import java.util.Properties;
15import jakarta.enterprise.context.ApplicationScoped;
16import jakarta.inject.Inject;
17import jakarta.ws.rs.GET;
18import jakarta.ws.rs.Path;
19import jakarta.ws.rs.PathParam;
20import jakarta.ws.rs.Produces;
21import jakarta.ws.rs.core.MediaType;
22import jakarta.ws.rs.core.Response;
23import io.openliberty.guides.inventory.model.InventoryList;
24import io.openliberty.guides.inventory.client.SystemClient;
25
26// tag::ApplicationScoped[]
27@ApplicationScoped
28// end::ApplicationScoped[]
29// tag::endpoint[]
30@Path("/systems")
31// end::endpoint[]
32// tag::InventoryResource[]
33public class InventoryResource {
34
35  // tag::inject[]
36  @Inject
37  // end::inject[]
38  InventoryManager manager;
39
40  // tag::inject2[]
41  @Inject
42  // end::inject2[]
43  SystemClient systemClient;
44
45  @GET
46  @Path("/{hostname}")
47  @Produces(MediaType.APPLICATION_JSON)
48  public Response getPropertiesForHost(@PathParam("hostname") String hostname) {
49    // Get properties for host
50    // tag::properties[]
51    Properties props = systemClient.getProperties(hostname);
52    // end::properties[]
53    if (props == null) {
54      return Response.status(Response.Status.NOT_FOUND)
55                     .entity("{ \"error\" : \"Unknown hostname " + hostname
56                             + " or the inventory service may not be running "
57                             + "on the host machine \" }")
58                     .build();
59    }
60
61    // Add to inventory
62    // tag::managerAdd[]
63    manager.add(hostname, props);
64    // end::managerAdd[]
65    return Response.ok(props).build();
66  }
67
68  @GET
69  @Produces(MediaType.APPLICATION_JSON)
70  public InventoryList listContents() {
71    // tag::managerList[]
72    return manager.list();
73    // end::managerList[]
74  }
75}
76// tag::InventoryResource[]

When you are done checking out the service, exit dev mode by pressing CTRL+C in the command-line session where you ran Liberty, or by typing q and then pressing the enter/return key.

Great work! You’re done!

You just used CDI services in Open Liberty to build a simple inventory application.

Guide Attribution

Injecting dependencies into microservices by Open Liberty is licensed under CC BY-ND 4.0

Copy file contents
Copied to clipboard

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