Producing and consuming messages in Java microservices

duration 30 minutes
New

Prerequisites:

Learn how to produce and consume messages to communicate between Java microservices in a standard way by using the Jakarta Messaging API with the embedded Liberty Messaging Server or an external messaging server, IBM MQ.

What you’ll learn

You’ll learn how to communicate between Java web services when one service is producing a continuous stream of asynchronous messages or events to be consumed by other services, rather than just sending and receiving individual requests for data. You will also learn how to use a messaging server and client to manage the production and consumption of the messages by the services.

In this guide, you will first use the embedded Liberty Messaging Server to manage messages, then you will optionally switch to using an external messaging server to manage the messages, in this case, IBM MQ. You might use an external messaging server if it is critical that none of the messages is lost if there is a system overload or outage; for example during a bank transfer in a banking application.

You will learn how to write your Java application using the Jakarta Messaging API which provides a standard way to produce and consume messages in Java application, regardless of which messaging server your application will ultimately use.

The application in this guide consists of two microservices, system and inventory. Every 15 seconds, the system microservice computes and publishes a message that contains the system’s current CPU and memory load usage. The inventory microservice subscribes to that information at the /systems REST endpoint so that it can keep an updated list of all the systems and their current system loads.

You’ll create the system and inventory microservices using the Jakarta Messaging API to produce and consume the messages using the embedded Liberty Messaging Server.

Application architecture where system and inventory services use the Jakarta Messaging to communicate.

You will then, optionally, reconfigure the application, without changing the application’s Java code, to use an external IBM MQ messaging server instead.

Additional prerequisites

To complete the optional Using IBM MQ as the messaging server section in this guide, install Docker if it is not already installed. You will use an IBM MQ container as the external messaging server. For installation instructions, refer to the official Docker documentation.

Make sure to start your Docker daemon before you proceed.

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-jms-intro.git
cd guide-jms-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 and install the models module. The models module contains the SystemLoad data class for both the system and inventory microservices to use.

cd finish
mvn -pl models clean install

Start the inventory microservice by running the following command:

mvn -pl inventory liberty:run

Next, open another command-line session, navigate to the finish directory, and start the system microservice by using the following command:

mvn -pl system liberty:run

When you see the following message, your Liberty instances are ready:

The defaultServer server is ready to run a smarter planet.

Visit the http://localhost:9081/health URL to confirm that the inventory microservice is up and running.

When both the liveness and readiness health checks are up, go to the http://localhost:9081/inventory/systems URL to access the inventory microservice. You see the systemLoad property for all the systems:

{
   "hostname": <your hostname>,
   "systemLoad": 6.037155240703536E-9
}

You can revisit the http://localhost:9081/inventory/systems URL after a while, and you will notice the systemLoad property for the systems changed.

You can also use curl command to retrieve the hostname and systemLoad information from the /inventory/systems REST endpoint in another command line session:

curl http://localhost:9081/inventory/systems

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

mvn -pl inventory liberty:stop
mvn -pl system liberty:stop

Creating the consumer in the inventory microservice

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 the inventory microservice in dev mode:

mvn -pl inventory liberty:dev

When 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.

The inventory microservice records in its inventory the recent system load information that it received from potentially multiple instances of the system microservice.

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

InventoryQueueListener.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2024 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 io.openliberty.guides.models.SystemLoad;
15import jakarta.ejb.ActivationConfigProperty;
16import jakarta.ejb.MessageDriven;
17import jakarta.inject.Inject;
18import jakarta.jms.JMSException;
19import jakarta.jms.Message;
20import jakarta.jms.MessageListener;
21import jakarta.jms.TextMessage;
22
23import java.util.logging.Logger;
24
25// tag::messageDriven[]
26@MessageDriven(activationConfig = {
27    @ActivationConfigProperty(
28        propertyName = "destinationLookup", propertyValue = "jms/InventoryQueue"),
29    @ActivationConfigProperty(
30        propertyName = "destinationType", propertyValue = "jakarta.jms.Queue")
31})
32// end::messageDriven[]
33// tag::InventoryQueueListener[]
34public class InventoryQueueListener implements MessageListener {
35
36    private static Logger logger =
37            Logger.getLogger(InventoryQueueListener.class.getName());
38
39    // tag::InventoryManager[]
40    @Inject
41    private InventoryManager manager;
42    //end::InventoryManager[]
43
44    // tag::onMessage[]
45    public void onMessage(Message message) {
46        try {
47            if (message instanceof TextMessage) {
48                TextMessage textMessage = (TextMessage) message;
49                String json = textMessage.getText();
50                // tag::systemLoad[]
51                SystemLoad systemLoad = SystemLoad.fromJson(json);
52                // end::systemLoad[]
53
54                String hostname = systemLoad.hostname;
55                Double recentLoad = systemLoad.recentLoad;
56                // tag::InventoryManagerUpdate[]
57                if (manager.getSystem(hostname).isPresent()) {
58                    manager.updateCpuStatus(hostname, recentLoad);
59                    logger.info("Host " + hostname + " was updated: " + recentLoad);
60                } else {
61                    manager.addSystem(hostname, recentLoad);
62                    logger.info("Host " + hostname + " was added: " + recentLoad);
63                }
64                // end::InventoryManagerUpdate[]
65            } else {
66                logger.warning(
67                    "Unsupported Message Type: " + message.getClass().getName());
68            }
69        } catch (JMSException e) {
70            e.printStackTrace();
71        }
72    }
73    // end::onMessage[]
74
75}
76// end::InventoryQueueListener[]

The inventory microservice receives the messages from the system microservice. Implement the InventoryQueueListener class with the MessageListener interface and annotate with @MessageDriven to monitor the jms/InventoryQueue message queue. Implement the onMessage() method that processes the incoming messages, updates the inventory by using the InventoryManager bean, and logs the action. Use the SystemLoad.fromJson() method to convert the JSON message string to the SystemLoad object.

Next, configure the inventory microservice with an embedded messaging server and the Messaging Server Client feature.

Replace the inventory's server.xml configuration file.
inventory/src/main/liberty/config/server.xml

inventory/server.xml

 1<server description="Inventory Service">
 2
 3  <featureManager>
 4    <feature>restfulWS-3.1</feature>
 5    <feature>cdi-4.0</feature>
 6    <feature>jsonb-3.0</feature>
 7    <feature>mpHealth-4.0</feature>
 8    <feature>mpConfig-3.1</feature>
 9    <!-- tag::messaging[] -->
10    <feature>messaging-3.1</feature>
11    <!-- end::messaging[] -->
12    <!-- tag::messagingServer[] -->
13    <feature>messagingServer-3.0</feature>
14    <!-- end::messagingServer[] -->
15    <!-- tag::messagingClient[] -->
16    <feature>messagingClient-3.0</feature>
17    <!-- end::messagingClient[] -->
18    <feature>enterpriseBeansLite-4.0</feature>
19    <feature>mdb-4.0</feature>
20  </featureManager>
21
22  <variable name="http.port" defaultValue="9081"/>
23  <variable name="https.port" defaultValue="9444"/>
24
25  <httpEndpoint id="defaultHttpEndpoint" host="*"
26                httpPort="${http.port}" httpsPort="${https.port}" />
27
28  <!-- tag::wasJmsEndpoint[] -->
29  <wasJmsEndpoint id="InboundJmsCommsEndpoint"
30                  host="*"
31                  wasJmsPort="7277"
32                  wasJmsSSLPort="9101"/>
33  <!-- end::wasJmsEndpoint[] -->
34
35  <!-- tag::connectionManager[] -->
36  <connectionManager id="InventoryCM" maxPoolSize="400" minPoolSize="1"/>
37  <!-- end::connectionManager[] -->
38
39  <!-- tag::messagingEngine[] -->
40  <messagingEngine id="InventoryME">
41    <queue id="InventoryQueue"
42           maxQueueDepth="5000"/>
43  </messagingEngine>
44  <!-- end::messagingEngine[] -->
45
46  <!-- tag::jmsConnectionFactory[] -->
47  <jmsConnectionFactory connectionManagerRef="InventoryCM"
48                        jndiName="InventoryConnectionFactory">
49    <properties.wasJms/>
50  </jmsConnectionFactory>
51  <!-- end::jmsConnectionFactory[] -->
52
53  <!-- tag::jmsQueue[] -->
54  <jmsQueue id="InventoryQueue" jndiName="jms/InventoryQueue">
55    <properties.wasJms queueName="InventoryQueue"/>
56  </jmsQueue>
57  <!-- end::jmsQueue[] -->
58
59  <!-- tag::jmsActivationSpec[] -->
60  <jmsActivationSpec id="guide-jms-intro-inventory/InventoryQueueListener">
61    <properties.wasJms maxConcurrency="200"/>
62  </jmsActivationSpec>
63  <!-- end::jmsActivationSpec[] -->
64
65  <logging consoleLogLevel="INFO"/>
66
67  <webApplication location="guide-jms-intro-inventory.war" contextRoot="/"/>
68
69</server>

The messagingServer feature enables a Liberty runtime to host an embedded messaging server to manage messaging destinations. The messagingClient feature enables applications to connect to a Liberty messaging server and access the messaging destinations hosted on that server through the Jakarta Messaging API that is enabled by the messaging feature.

Add the wasJmsEndpoint element to configure the Liberty runtime to monitor and manage incoming JMS connections from any hosts. Set up the messagingEngine configuration to ensure that the Liberty runtime can manage incoming message queues more effectively, assigning a reliable and persistent destination for the InventoryQueue. Configure a jmsConnectionFactory element to use the InventoryCM connection manager and set properties for the JMS implementation. Define a jmsQueue element for the InventoryQueue message queue with its JNDI name and a jmsActivationSpec element to configure properties, including the queue listener class name and maximum concurrency.

To learn more about configuration for the jmsQueue element and jmsConnectionFactory element, see the JMS Queue and JMS Connection Factory documentation.

Creating the message producer in the system service

Open another command-line session, navigate to the start directory, and run the following goal to start the system microservice in dev mode:

mvn -pl system liberty:dev

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

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

The system microservice is the producer of the messages that are published to the messaging server as a stream of events. Every 15 seconds, the system microservice triggers an event that calculates the recent CPU usage for the last minute.

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

SystemService.java

 1// tag::copyright[]
 2/*******************************************************************************
 3 * Copyright (c) 2024 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.system;
13
14import java.lang.management.ManagementFactory;
15import java.net.InetAddress;
16import java.net.UnknownHostException;
17import java.util.logging.Logger;
18
19import com.sun.management.OperatingSystemMXBean;
20
21import io.openliberty.guides.models.SystemLoad;
22import jakarta.annotation.Resource;
23import jakarta.ejb.Schedule;
24import jakarta.ejb.Singleton;
25import jakarta.inject.Inject;
26import jakarta.jms.JMSConnectionFactory;
27import jakarta.jms.JMSContext;
28import jakarta.jms.Queue;
29
30@Singleton
31public class SystemService {
32
33    private static final OperatingSystemMXBean OS_MEAN =
34            (OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
35    private static String hostname = null;
36
37    private static Logger logger = Logger.getLogger(SystemService.class.getName());
38
39    @Inject
40    @JMSConnectionFactory("InventoryConnectionFactory")
41    private JMSContext context;
42
43    //tag::jms/InventoryQueue[]
44    @Resource(lookup = "jms/InventoryQueue")
45    private Queue queue;
46    //end::jms/InventoryQueue[]
47
48    private static String getHostname() {
49        if (hostname == null) {
50            try {
51                return InetAddress.getLocalHost().getHostName();
52            } catch (UnknownHostException e) {
53                return System.getenv("HOSTNAME");
54            }
55        }
56        return hostname;
57    }
58
59    // tag::schedule[]
60    @Schedule(second = "*/15", minute = "*", hour = "*", persistent = false)
61    // end::schedule[]
62    // tag::sendSystemLoad[]
63    public void sendSystemLoad() {
64        //tag::SystemLoad[]
65        SystemLoad systemLoad = new SystemLoad(getHostname(),
66                                    Double.valueOf(OS_MEAN.getCpuLoad()));
67        //end::SystemLoad[]
68        //tag::createProducer[]
69        context.createProducer().send(queue, systemLoad.toString());
70        //end::createProducer[]
71        logger.info(systemLoad.toString());
72    }
73    // end::sendSystemLoad[]
74}

The SystemService class contains the sendSystemLoad() method that calculates the recent system load, creates a SystemLoad object, and publishes the object as a message to the jms/InventoryQueue message queue running in the messaging server by using the send() method. The @Schedule annotation on the sendSystemLoad() method sets the frequency at which the system service publishes the calculation to the event stream, ensuring it runs every 15 seconds.

Next, configure the system microservice to access the message queue.

Replace the system's server.xml configuration file.
system/src/main/liberty/config/server.xml

system/server.xml

 1<server description="System Service">
 2
 3  <featureManager>
 4    <feature>cdi-4.0</feature>
 5    <feature>jsonb-3.0</feature>
 6    <feature>mpHealth-4.0</feature>
 7    <feature>mpConfig-3.1</feature>
 8    <!-- tag::messaging[] -->
 9    <feature>messaging-3.1</feature>
10    <!-- end::messaging[] -->
11    <!-- tag::messagingClient[] -->
12    <feature>messagingClient-3.0</feature>
13    <!-- end::messagingClient[] -->
14    <feature>enterpriseBeansLite-4.0</feature>
15    <feature>mdb-4.0</feature>
16  </featureManager>
17
18  <variable name="http.port" defaultValue="9082"/>
19  <variable name="https.port" defaultValue="9445"/>
20  <!-- tag::jms[] -->
21  <variable name="inventory.jms.host" defaultValue="localhost"/>
22  <variable name="inventory.jms.port" defaultValue="7277"/>
23  <!-- end::jms[] -->
24
25  <httpEndpoint id="defaultHttpEndpoint" host="*"
26                httpPort="${http.port}" httpsPort="${https.port}"/>
27
28  <!-- tag::connectionManager[] -->
29  <connectionManager id="InventoryCM" maxPoolSize="400" minPoolSize="1"/>
30  <!-- end::connectionManager[] -->
31
32  <!-- tag::jmsConnectionFactory[] -->
33  <jmsConnectionFactory
34    connectionManagerRef="InventoryCM"
35    jndiName="InventoryConnectionFactory">
36    <!-- tag::propertiesWasJms[] -->
37    <properties.wasJms
38      remoteServerAddress="${inventory.jms.host}:${inventory.jms.port}:BootstrapBasicMessaging"/>
39    <!-- end::propertiesWasJms[] -->
40  </jmsConnectionFactory>
41  <!-- end::jmsConnectionFactory[] -->
42
43  <!-- tag::jmsQueue[] -->
44  <jmsQueue id="InventoryQueue" jndiName="jms/InventoryQueue">
45    <properties.wasJms queueName="InventoryQueue"/>
46  </jmsQueue>
47  <!-- end::jmsQueue[] -->
48
49  <logging consoleLogLevel="INFO"/>
50
51  <webApplication location="guide-jms-intro-system.war" contextRoot="/"/>
52
53</server>

The messaging and messagingClient features enable the Liberty runtime to provide the required messaging services. Add a connectionManager element to handle connections for the messaging server running on the inventory microservice. Define the jmsConnectionFactory element to use the InventoryCM connection manager and set up the required remoteServerAddress properties. Use the jmsQueue element to define the inventory message queue.

In your dev mode console for the system microservice, type r and press enter/return key to restart the Liberty instance so that Liberty reads the configuration changes. When you see the following message, your Liberty instance is ready in dev mode:

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

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 microservice at the following URLs:

You can also use curl command to retrieve the hostname and systemLoad information from the /inventory/systems REST endpoint in another command line session:

curl http://localhost:9081/inventory/systems

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.
inventory/src/test/java/it/io/openliberty/guides/inventory/InventoryEndpointIT.java

InventoryEndpointIT.java

  1// tag::copyright[]
  2/*******************************************************************************
  3 * Copyright (c) 2024 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 jakarta.json.JsonArray;
 16import jakarta.json.JsonObject;
 17import jakarta.ws.rs.client.Client;
 18import jakarta.ws.rs.client.ClientBuilder;
 19import jakarta.ws.rs.core.MediaType;
 20import jakarta.ws.rs.core.Response;
 21
 22import org.junit.jupiter.api.Test;
 23import org.junit.jupiter.api.AfterEach;
 24import org.junit.jupiter.api.BeforeEach;
 25import org.junit.jupiter.api.MethodOrderer;
 26import org.junit.jupiter.api.Order;
 27
 28import org.junit.jupiter.api.BeforeAll;
 29import org.junit.jupiter.api.TestMethodOrder;
 30
 31import static org.junit.jupiter.api.Assertions.assertFalse;
 32import static org.junit.jupiter.api.Assertions.assertTrue;
 33import static org.junit.jupiter.api.Assertions.assertNotNull;
 34import static org.junit.jupiter.api.Assertions.assertEquals;
 35
 36
 37// tag::TestMethodOrder[]
 38@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
 39// end::TestMethodOrder[]
 40public class InventoryEndpointIT {
 41
 42    private static String port;
 43    private static String baseUrl;
 44    private static String hostname;
 45
 46    private Client client;
 47
 48    private final String INVENTORY_SYSTEMS = "inventory/systems";
 49
 50    // tag::BeforeAll[]
 51    @BeforeAll
 52    // end::BeforeAll[]
 53    // tag::oneTimeSetup[]
 54    public static void oneTimeSetup() {
 55        port = System.getProperty("http.port");
 56        baseUrl = "http://localhost:" + port + "/";
 57    }
 58    // end::oneTimeSetup[]
 59
 60    // tag::BeforeEach[]
 61    @BeforeEach
 62    // end::BeforeEach[]
 63    // tag::setup[]
 64    public void setup() {
 65        client = ClientBuilder.newClient();
 66    }
 67    // end::setup[]
 68
 69    // tag::AfterEach[]
 70    @AfterEach
 71    // end::AfterEach[]
 72    // tag::teardown[]
 73    public void teardown() {
 74        client.close();
 75    }
 76    // end::teardown[]
 77
 78
 79    // tag::tests[]
 80    // tag::testGetSystems[]
 81    @Test
 82    @Order(1)
 83    public void testGetSystems() {
 84        Response response = this.getResponse(baseUrl + INVENTORY_SYSTEMS);
 85        this.assertResponse(baseUrl, response);
 86
 87        JsonArray systems = response.readEntity(JsonArray.class);
 88
 89        boolean hostnameExists = false;
 90        boolean recentLoadExists = false;
 91        for (int n = 0; n < systems.size(); n++) {
 92            hostnameExists = systems.getJsonObject(n)
 93                                    .get("hostname").toString().isEmpty();
 94            recentLoadExists = systems.getJsonObject(n)
 95                                      .get("systemLoad").toString().isEmpty();
 96
 97            assertFalse(hostnameExists, "A host was registered, but it was empty");
 98            assertFalse(recentLoadExists,
 99                "A recent system load was registered, but it was empty");
100            if (!hostnameExists && !recentLoadExists) {
101                String host = systems.getJsonObject(n).get("hostname").toString();
102                hostname = host.substring(1, host.length() - 1);
103                break;
104            }
105        }
106        assertNotNull(hostname, "Hostname should be set by the first test. (1)");
107        response.close();
108    }
109    // end::testGetSystems[]
110
111    // tag::testGetSystemsWithHost[]
112    @Test
113    @Order(2)
114    public void testGetSystemsWithHost() {
115        assertNotNull(hostname, "Hostname should be set by the first test. (2)");
116
117        Response response =
118            this.getResponse(baseUrl + INVENTORY_SYSTEMS + "/" + hostname);
119        this.assertResponse(baseUrl, response);
120
121        JsonObject system = response.readEntity(JsonObject.class);
122
123        String responseHostname = system.getString("hostname");
124        Boolean recentLoadExists = system.get("systemLoad").toString().isEmpty();
125
126        assertEquals(hostname, responseHostname,
127            "Hostname should match the one from the TestNonEmpty");
128        assertFalse(recentLoadExists, "A recent system load should not be empty");
129
130        response.close();
131    }
132    // end::testGetSystemsWithHost[]
133
134    // tag::testUnknownHost[]
135    @Test
136    @Order(3)
137    public void testUnknownHost() {
138        Response badResponse =
139            client.target(baseUrl + INVENTORY_SYSTEMS + "/" + "badhostname")
140                  .request(MediaType.APPLICATION_JSON).get();
141
142        assertEquals(404, badResponse.getStatus(),
143            "BadResponse expected status: 404. Response code not as expected.");
144
145        String stringObj = badResponse.readEntity(String.class);
146        assertTrue(stringObj.contains("hostname does not exist."),
147            "badhostname is not a valid host but it didn't raise an error");
148
149        badResponse.close();
150    }
151    // end::testUnknownHost[]
152    // end::tests[]
153
154    private Response getResponse(String url) {
155        return client.target(url).request().get();
156    }
157
158    private void assertResponse(String url, Response response) {
159        assertEquals(200, response.getStatus(), "Incorrect response code from " + url);
160    }
161
162}

See the following descriptions of the test cases:

  • testGetSystems() verifies that the hostname and the system load for each system in the inventory are not empty.

  • testGetSystemsWithHost() verifies that the hostname and system load returned by the system microservice match the ones stored in the inventory microservice and ensures they are not empty.

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

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 for the inventory microservice.

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

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.inventory.InventoryEndpointIT
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.325 sec - in it.io.openliberty.guides.inventory.InventoryEndpointIT

Results :

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

When you are done checking out the application, stop the Liberty instances by pressing CTRL+C in each command-line session where you ran the system and inventory microservices.

Using IBM MQ as the messaging server - Optional

The application has been built and tested. In this section, you’ll learn how to configure Liberty to use IBM MQ container as the messaging server instead of the embedded Liberty Messaging Server.

Start IBM MQ by running the following command on the command-line session:

docker pull icr.io/ibm-messaging/mq:9.4.0.0-r3

docker volume create qm1data

docker run ^
--env LICENSE=accept ^
--env MQ_QMGR_NAME=QM1 ^
--volume qm1data:/mnt/mqm ^
--publish 1414:1414 --publish 9443:9443 ^
--detach ^
--env MQ_APP_PASSWORD=passw0rd ^
--env MQ_ADMIN_PASSWORD=passw0rd ^
--rm ^
--platform linux/amd64 ^
--name QM1 ^
icr.io/ibm-messaging/mq:9.4.0.0-r3

If you’re an Intel-based Mac user, start IBM MQ by running the following command on the command-line session:

docker pull icr.io/ibm-messaging/mq:9.4.0.0-r3

docker volume create qm1data

docker run \
--env LICENSE=accept \
--env MQ_QMGR_NAME=QM1 \
--volume qm1data:/mnt/mqm \
--publish 1414:1414 --publish 9443:9443 \
--detach \
--env MQ_APP_PASSWORD=passw0rd \
--env MQ_ADMIN_PASSWORD=passw0rd \
--rm \
--platform linux/amd64 \
--name QM1 \
icr.io/ibm-messaging/mq:9.4.0.0-r3

If you’re an ARM-based Mac user, check out the How to build Mac IBM MQ container image blog in the IBM TechXchange Community website for building IBM MQ container image.

Navigate to an empty directory for building the IBM MQ Docker container image and run the following commands:

git clone https://github.com/ibm-messaging/mq-container.git -b 9.4.0.0-r3
cd mq-container
make build-devserver COMMAND=docker

After building the container image, you can find the image version:

docker images | grep mq

When the container image is built, you see an image similar to the ibm-mqadvanced-server-dev:9.4.0.0-arm64. Now, you can start IBM MQ by running the following command on the command-line session:

docker volume create qm1data

docker run \
--env LICENSE=accept \
--env MQ_QMGR_NAME=QM1 \
--volume docker:/mnt/mqm \
--publish 1414:1414 --publish 9443:9443 \
--detach \
--env MQ_APP_PASSWORD=passw0rd \
--env MQ_ADMIN_PASSWORD=passw0rd \
--name QM1 ibm-mqadvanced-server-dev:9.4.0.0-arm64

Start IBM MQ by running the following command on the command-line session:

docker pull icr.io/ibm-messaging/mq:9.4.0.0-r3

docker volume create qm1data

docker run \
--env LICENSE=accept \
--env MQ_QMGR_NAME=QM1 \
--volume qm1data:/mnt/mqm \
--publish 1414:1414 --publish 9443:9443 \
--detach \
--env MQ_APP_PASSWORD=passw0rd \
--env MQ_ADMIN_PASSWORD=passw0rd \
--rm \
--platform linux/amd64 \
--name QM1 \
icr.io/ibm-messaging/mq:9.4.0.0-r3

When the IBM MQ container is running, you can access the https://localhost:9443/ibmmq/console URL.

Replace the pom.xml file of the inventory service.
inventory/pom.xml

inventory/pom.xml

  1<?xml version='1.0' encoding='utf-8'?>
  2<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  3    <modelVersion>4.0.0</modelVersion>
  4
  5    <groupId>io.openliberty.guides</groupId>
  6    <artifactId>guide-jms-intro-inventory</artifactId>
  7    <version>1.0-SNAPSHOT</version>
  8    <packaging>war</packaging>
  9
 10    <properties>
 11        <maven.compiler.source>17</maven.compiler.source>
 12        <maven.compiler.target>17</maven.compiler.target>
 13        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 14        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 15        <!-- Liberty configuration -->
 16        <liberty.var.http.port>9081</liberty.var.http.port>
 17        <liberty.var.https.port>9444</liberty.var.https.port>
 18        <!-- IBM MQ -->
 19        <!-- tag::liberty[] -->
 20        <liberty.var.ibmmq-hostname>localhost</liberty.var.ibmmq-hostname>
 21        <liberty.var.ibmmq-port>1414</liberty.var.ibmmq-port>
 22        <liberty.var.ibmmq-channel>DEV.APP.SVRCONN</liberty.var.ibmmq-channel>
 23        <liberty.var.ibmmq-queue-manager>QM1</liberty.var.ibmmq-queue-manager>
 24        <liberty.var.ibmmq-username>app</liberty.var.ibmmq-username>
 25        <liberty.var.ibmmq-password>passw0rd</liberty.var.ibmmq-password>
 26        <liberty.var.ibmmq-inventory-queue-name>DEV.QUEUE.1</liberty.var.ibmmq-inventory-queue-name>
 27        <!-- end::liberty[] -->
 28    </properties>
 29    
 30    <dependencies>
 31        <!-- Provided dependencies -->
 32        <dependency>
 33            <groupId>jakarta.platform</groupId>
 34            <artifactId>jakarta.jakartaee-api</artifactId>
 35            <version>10.0.0</version>
 36            <scope>provided</scope>
 37        </dependency>
 38        <dependency>
 39            <groupId>org.eclipse.microprofile</groupId>
 40            <artifactId>microprofile</artifactId>
 41            <version>6.1</version>
 42            <type>pom</type>
 43            <scope>provided</scope>
 44        </dependency>
 45        
 46        <!--  Required dependencies -->
 47        <dependency>
 48           <groupId>io.openliberty.guides</groupId>
 49           <artifactId>guide-jms-intro-models</artifactId>
 50           <version>1.0-SNAPSHOT</version>
 51        </dependency>
 52        <!-- For tests -->
 53        <dependency>
 54            <groupId>org.junit.jupiter</groupId>
 55            <artifactId>junit-jupiter</artifactId>
 56            <version>5.11.2</version>
 57            <scope>test</scope>
 58        </dependency>
 59        <dependency>
 60            <groupId>org.jboss.resteasy</groupId>
 61            <artifactId>resteasy-client</artifactId>
 62            <version>6.2.10.Final</version>
 63            <scope>test</scope>
 64        </dependency>
 65        <dependency>
 66            <groupId>org.jboss.resteasy</groupId>
 67            <artifactId>resteasy-json-binding-provider</artifactId>
 68            <version>6.2.10.Final</version>
 69            <scope>test</scope>
 70        </dependency>
 71    </dependencies>
 72
 73    <build>
 74        <finalName>${project.artifactId}</finalName>
 75        <plugins>
 76            <plugin>
 77                <groupId>org.apache.maven.plugins</groupId>
 78                <artifactId>maven-war-plugin</artifactId>
 79                <version>3.4.0</version>
 80                <configuration>
 81                    <packagingExcludes>pom.xml</packagingExcludes>
 82                </configuration>
 83            </plugin>
 84
 85            <!-- Liberty plugin -->
 86            <plugin>
 87                <groupId>io.openliberty.tools</groupId>
 88                <artifactId>liberty-maven-plugin</artifactId>
 89                <version>3.11.1</version>
 90            </plugin>
 91
 92            <!-- Plugin to run unit tests -->
 93            <plugin>
 94                <groupId>org.apache.maven.plugins</groupId>
 95                <artifactId>maven-surefire-plugin</artifactId>
 96                <version>3.5.1</version>
 97            </plugin>
 98
 99            <!-- Plugin to run integration tests -->
100            <plugin>
101                <groupId>org.apache.maven.plugins</groupId>
102                <artifactId>maven-failsafe-plugin</artifactId>
103                <version>3.5.1</version>
104                <configuration>
105                    <systemPropertyVariables>
106                        <http.port>${liberty.var.http.port}</http.port>
107                        <https.port>${liberty.var.https.port}</https.port>
108                    </systemPropertyVariables>
109                </configuration>
110                <executions>
111                    <execution>
112                        <goals>
113                            <goal>integration-test</goal>
114                            <goal>verify</goal>
115                        </goals>
116                    </execution>
117                </executions>
118            </plugin>
119        </plugins>
120    </build>
121</project>

Add the liberty.var.ibmmq-* properties for the IBM MQ container. You can change to different values when you deploy the application on a production environment without modifying the Liberty server.xml configuration file.

Replace the server.xml file of the inventory service.
inventory/src/main/liberty/config/server.xml

inventory/server.xml

 1<server description="Inventory Service">
 2
 3  <featureManager>
 4    <feature>restfulWS-3.1</feature>
 5    <feature>cdi-4.0</feature>
 6    <feature>jsonb-3.0</feature>
 7    <feature>mpHealth-4.0</feature>
 8    <feature>mpConfig-3.1</feature>
 9    <feature>messaging-3.1</feature>
10    <feature>messagingClient-3.0</feature>
11    <feature>messagingServer-3.0</feature>
12    <feature>enterpriseBeansLite-4.0</feature>
13    <feature>mdb-4.0</feature>
14  </featureManager>
15
16  <variable name="http.port" defaultValue="9081"/>
17  <variable name="https.port" defaultValue="9444"/>
18
19  <httpEndpoint id="defaultHttpEndpoint" host="*"
20                httpPort="${http.port}" httpsPort="${https.port}"/>
21
22  <!-- tag::wasJmsEndpoint[] -->
23  <wasJmsEndpoint id="InboundJmsCommsEndpoint"
24                  host="*"
25                  wasJmsPort="7277"
26                  wasJmsSSLPort="9101"/>
27  <!-- end::wasJmsEndpoint[]-->
28
29  <!-- tag::jmsQueue[] -->
30  <jmsQueue id="InventoryQueue" jndiName="jms/InventoryQueue">
31    <properties.wmqjmsra baseQueueName="${ibmmq-inventory-queue-name}"/>
32  </jmsQueue>
33  <!-- end::jmsQueue[] -->
34
35  <!-- tag::jmsActivationSpec[] -->
36  <jmsActivationSpec id="guide-jms-intro-inventory/InventoryQueueListener">
37    <properties.wmqjmsra
38      hostName="${ibmmq-hostname}"
39      port="${ibmmq-port}"
40      channel="${ibmmq-channel}"
41      queueManager="${ibmmq-queue-manager}"
42      userName="${ibmmq-username}"
43      password="${ibmmq-password}"
44      transportType="CLIENT"/>
45  </jmsActivationSpec>
46  <!-- end::jmsActivationSpec[] -->
47
48  <!-- tag::resourceAdapter[] -->
49  <resourceAdapter id="wmqjmsra"
50    location="https://repo.maven.apache.org/maven2/com/ibm/mq/wmq.jakarta.jmsra/9.4.0.0/wmq.jakarta.jmsra-9.4.0.0.rar"/>
51  <!-- end::resourceAdapter[] -->
52    
53  <logging consoleLogLevel="INFO"/>
54
55  <webApplication location="guide-jms-intro-inventory.war" contextRoot="/"/>
56
57</server>

Refine the jmsQueue and jmsActivationSpec configurations with the variables for IBM MQ settings. Add the resourceAdapter element to define the RAR file that provides the IBM MQ classes for Java and JMS. Note that the messagingEngine and jmsConnectionFactory configurations are removed from the configuration because they are no longer required.

Replace the pom.xml file of the system service.
system/pom.xml

system/pom.xml

  1<?xml version='1.0' encoding='utf-8'?>
  2<project xmlns="http://maven.apache.org/POM/4.0.0"
  3    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5    <modelVersion>4.0.0</modelVersion>
  6
  7    <groupId>io.openliberty.guides</groupId>
  8    <artifactId>guide-jms-intro-system</artifactId>
  9    <version>1.0-SNAPSHOT</version>
 10    <packaging>war</packaging>
 11
 12    <properties>
 13        <maven.compiler.source>17</maven.compiler.source>
 14        <maven.compiler.target>17</maven.compiler.target>
 15        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 16        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
 17        <!-- Liberty configuration -->
 18        <liberty.var.http.port>9082</liberty.var.http.port>
 19        <liberty.var.https.port>9445</liberty.var.https.port>
 20        <liberty.var.inventory.jms.host>localhost</liberty.var.inventory.jms.host>
 21        <liberty.var.inventory.jms.port>7277</liberty.var.inventory.jms.port>
 22        <!-- IBM MQ -->
 23        <!-- tag::liberty[] -->
 24        <liberty.var.ibmmq-hostname>localhost</liberty.var.ibmmq-hostname>
 25        <liberty.var.ibmmq-port>1414</liberty.var.ibmmq-port>
 26        <liberty.var.ibmmq-channel>DEV.APP.SVRCONN</liberty.var.ibmmq-channel>
 27        <liberty.var.ibmmq-queue-manager>QM1</liberty.var.ibmmq-queue-manager>
 28        <liberty.var.ibmmq-username>app</liberty.var.ibmmq-username>
 29        <liberty.var.ibmmq-password>passw0rd</liberty.var.ibmmq-password>
 30        <liberty.var.ibmmq-inventory-queue-name>DEV.QUEUE.1</liberty.var.ibmmq-inventory-queue-name>
 31        <!-- end::liberty[] -->
 32    </properties>
 33
 34    <dependencies>
 35        <!-- Provided dependencies -->
 36        <dependency>
 37            <groupId>jakarta.platform</groupId>
 38            <artifactId>jakarta.jakartaee-api</artifactId>
 39            <version>10.0.0</version>
 40            <scope>provided</scope>
 41        </dependency>
 42        <dependency>
 43            <groupId>org.eclipse.microprofile</groupId>
 44            <artifactId>microprofile</artifactId>
 45            <version>6.1</version>
 46            <type>pom</type>
 47            <scope>provided</scope>
 48        </dependency>
 49        <!-- Required dependencies -->
 50        <dependency>
 51            <groupId>io.openliberty.guides</groupId>
 52            <artifactId>guide-jms-intro-models</artifactId>
 53            <version>1.0-SNAPSHOT</version>
 54        </dependency>
 55        <dependency>
 56            <groupId>org.slf4j</groupId>
 57            <artifactId>slf4j-api</artifactId>
 58            <version>2.0.16</version>
 59        </dependency>
 60        <dependency>
 61            <groupId>org.slf4j</groupId>
 62            <artifactId>slf4j-simple</artifactId>
 63            <version>2.0.16</version>
 64        </dependency>
 65        <!-- For tests -->
 66        <dependency>
 67            <groupId>org.junit.jupiter</groupId>
 68            <artifactId>junit-jupiter</artifactId>
 69            <version>5.11.2</version>
 70            <scope>test</scope>
 71        </dependency>
 72    </dependencies>
 73
 74    <build>
 75        <finalName>${project.artifactId}</finalName>
 76        <plugins>
 77            <plugin>
 78                <groupId>org.apache.maven.plugins</groupId>
 79                <artifactId>maven-war-plugin</artifactId>
 80                <version>3.4.0</version>
 81                <configuration>
 82                    <packagingExcludes>pom.xml</packagingExcludes>
 83                </configuration>
 84            </plugin>
 85
 86            <!-- Liberty plugin -->
 87            <plugin>
 88                <groupId>io.openliberty.tools</groupId>
 89                <artifactId>liberty-maven-plugin</artifactId>
 90                <version>3.11.1</version>
 91            </plugin>
 92
 93            <!-- Plugin to run unit tests -->
 94            <plugin>
 95                <groupId>org.apache.maven.plugins</groupId>
 96                <artifactId>maven-surefire-plugin</artifactId>
 97                <version>3.5.1</version>
 98            </plugin>
 99
100            <!-- Plugin to run integration tests -->
101            <plugin>
102                <groupId>org.apache.maven.plugins</groupId>
103                <artifactId>maven-failsafe-plugin</artifactId>
104                <version>3.5.1</version>
105                <executions>
106                    <execution>
107                        <id>integration-test</id>
108                        <goals>
109                            <goal>integration-test</goal>
110                        </goals>
111                        <configuration>
112                            <trimStackTrace>false</trimStackTrace>
113                        </configuration>
114                    </execution>
115                    <execution>
116                        <id>verify</id>
117                        <goals>
118                            <goal>verify</goal>
119                        </goals>
120                    </execution>
121                </executions>
122            </plugin>
123        </plugins>
124    </build>
125</project>

Add the liberty.var.ibmmq-* properties for the IBM MQ container as you did for the inventory microservice previously.

Replace the server.xml file of the system service.
system/src/main/liberty/config/server.xml

system/server.xml

 1<server description="System Service">
 2
 3  <featureManager>
 4    <feature>cdi-4.0</feature>
 5    <feature>jsonb-3.0</feature>
 6    <feature>mpHealth-4.0</feature>
 7    <feature>mpConfig-3.1</feature>
 8    <feature>messaging-3.1</feature>
 9    <feature>messagingClient-3.0</feature>
10    <feature>enterpriseBeansLite-4.0</feature>
11    <feature>mdb-4.0</feature>
12  </featureManager>
13
14  <variable name="http.port" defaultValue="9082"/>
15  <variable name="https.port" defaultValue="9445"/>
16  <!-- tag::jms[] -->
17  <variable name="inventory.jms.host" defaultValue="localhost"/>
18  <variable name="inventory.jms.port" defaultValue="7277"/>
19  <!-- end::jms[] -->
20  
21  <httpEndpoint id="defaultHttpEndpoint" host="*"
22                httpPort="${http.port}" httpsPort="${https.port}" />
23
24  <!-- tag::connectionManager[] -->
25  <connectionManager id="InventoryCM" maxPoolSize="400" minPoolSize="1"/>
26  <!-- end::connectionManager[] -->
27
28  <!-- tag::jmsConnectionFactory[] -->
29  <jmsConnectionFactory
30    connectionManagerRef="InventoryCM"
31    jndiName="InventoryConnectionFactory">
32  <!-- tag::properties.wmqjmsra[] -->
33    <properties.wmqjmsra
34      hostName="${ibmmq-hostname}"
35      port="${ibmmq-port}"
36      channel="${ibmmq-channel}"
37      queueManager="${ibmmq-queue-manager}"
38      userName="${ibmmq-username}"
39      password="${ibmmq-password}"
40      transportType="CLIENT" />
41  <!-- end::properties.wmqjmsra[] -->
42  </jmsConnectionFactory>
43  <!-- end::jmsConnectionFactory[] -->
44
45  <!-- tag::jmsQueue[] -->
46  <jmsQueue id="InventoryQueue" jndiName="jms/InventoryQueue">
47    <!-- tag::baseQueueName[] -->
48    <properties.wmqjmsra baseQueueName="${ibmmq-inventory-queue-name}"/>
49    <!-- end::baseQueueName[] -->
50  </jmsQueue>
51  <!-- end::jmsQueue[] -->
52
53  <!-- tag::resourceAdapter[] -->
54  <resourceAdapter id="wmqjmsra"
55    location="https://repo.maven.apache.org/maven2/com/ibm/mq/wmq.jakarta.jmsra/9.4.0.0/wmq.jakarta.jmsra-9.4.0.0.rar"/>
56  <!-- end::resourceAdapter[] -->
57
58  <logging consoleLogLevel="INFO"/>
59
60  <webApplication location="guide-jms-intro-system.war" contextRoot="/"/>
61
62</server>

Replace the properties.wasJms configuration by the properties.wmqjmsra configuration. All property values are defined in the pom.xml file that you replaced. Also, modify the jmsQueue property to set the baseQueueName value with the ${ibmmq-inventory-queue-name} variable. Add the resourceAdapter element like you did for the inventory microservice.

Start the inventory microservice by running the following command in dev mode:

mvn -pl inventory liberty:dev

Next, open another command-line session, navigate to the start directory, and start the system microservice by using the following command:

mvn -pl system liberty:dev

When you see the following message, your Liberty instances are ready in dev mode:

The defaultServer server is ready to run a smarter planet.

You can access the inventory microservice by the http://localhost:9081/inventory/systems URL.

In the command shell where inventory dev mode is running, press enter/return to run the tests. If the tests pass, you’ll see output that is similar to the following example:

-------------------------------------------------------
 T E S T S
-------------------------------------------------------
Running it.io.openliberty.guides.inventory.InventoryEndpointIT
Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.325 sec - in it.io.openliberty.guides.inventory.InventoryEndpointIT

Results :

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

After you are finished checking out the application, stop the Liberty instances by pressing CTRL+C in the command-line sessions where you ran the system and inventory microservices.

Run the following commands to stop the running IBM MQ container and clean up the qm1data volume:

docker stop QM1
docker rm QM1
docker volume remove qm1data

Great work! You’re done!

You just developed a Java cloud-native application that uses Jakarta Messaging to produce and consume messages in Open Liberty.

Guide Attribution

Producing and consuming messages in Java microservices by {guide-author} 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