Note
|
This repository contains the guide documentation source. To view the guide in published form, view it on the Open Liberty website. |
Learn how to produce and consume messages in Java microservices by using Jakarta Messaging with Liberty Messaging Server, Liberty Messaging Server Client, and IBM MQ.
You will learn how to communicate Java services by using Jakarta Messaging with Liberty Messaging Server, Liberty Messaging Server Client, and IBM MQ. You will use a Jakarta Messaging system to handle the asynchronous messages that are sent and received between the microservices as streams of events.
You’ll also explore the configuration and use of Liberty Messaging Server, along with examples of message production and consumption with Liberty Messaging Server Client. Additionally, you will discover how to use IBM MQ for Jakarta Messaging.
In this guide, you will use Jakarta Messaging APIs to build the application and implement a messaging solution that enables communication between different parts of Java microservices. Jakarta Messaging makes it easy to write and configure your application to send, receive, and process the events efficiently.
What is Jakarta Messaging?
Jakarta Messaging provides an easy way to asynchronously send, receive, and process messages that are received as continuous streams of events. By integrating Jakarta Messaging with Open Liberty, you can easily configure and manage message producers and consumers within your Java microservices. You simply use the Jakarta Messaging API to annotate methods in your application beans, and Open Liberty handles the communication infrastructure, ensuring messages are reliably exchanged. This integration allows your services to connect easily with external messaging systems, such as IBM MQ.
The application in this guide consists of two microservices, system
and inventory
. Every 15 seconds, the system
microservice calculates and publishes an event that contains its current average system load. The inventory
microservice subscribes to that information so that it can keep an updated list of all the systems and their current system loads. The current inventory of systems can be accessed via the /systems
REST endpoint. You’ll create the system
and inventory
microservices using Java Message System.
If you’re focusing on sections other than the Using IBM MQ optional section, you can move forward without running Docker and skip this section.
You need to install Docker if it is not already installed. For installation instructions, refer to the official Docker documentation. You will build and run the application in Docker containers.
Make sure to start your Docker daemon before you proceed.
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.
cd finish
mvn -pl models clean install
Start the inventory
service 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
service by using the following command:
mvn -pl system liberty:run
After you see the following message, your Liberty instances are ready:
The defaultServer server is ready to run a smarter planet.
Visit the http://localhost:9085/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:9085/inventory/systems URL to access the inventory
microservice. You see the CPU systemLoad property for all the systems:
{
"hostname": <your hostname>,
"systemLoad": 2.25927734375
}
You can revisit the http://localhost:9085/inventory/systems URL after a while, and you will notice the CPU systemLoad
property for the systems changed.
You can also use curl
command to retrieve the hostname and systemLoad information from the inventory/systems
server endpoint in another command line session:
curl http://localhost:9085/inventory/systems
After you are finished checking out the application, stop the Liberty instance 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
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 -pl system 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.
The system
microservice is the producer of the messages that are published to the Jakarta messaging system as a stream of events. Every 15 seconds, the system
microservice publishes an event that contains its calculation of the average system load (CPU usage) for the last minute.
Create theSystemService
class.system/src/main/java/io/openliberty/guides/system/SystemService.java
SystemService.java
link:finish/system/src/main/java/io/openliberty/guides/system/SystemService.java[role=include]
The SystemService
class contains the sendSystemLoad()
method that calculates the average system load, creates a SystemLoad
object, and publishes the object as a message by using the send()
API to the jms/InventoryQueue
queue running in the Jakarta messaging system. 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.
Replace the system'sserver.xml
configuration file.system/src/main/liberty/config/server.xml
system/server.xml
link:finish/system/src/main/liberty/config/server.xml[role=include]
The messaging-3.1
and messagingClient-3.0
features enable the applications running on Liberty to provide Jakarta Messaging services. Add a connectionManager
element to handle connections for the messaging server running on the inventory
service. Define the jmsQueueConnectionFactory
element to use the InventoryCM
and set up the required remoteServerAddress
properties. Use the jmsQueue
element to define the inventory queue.
If you want to learn more about configuration for the jmsQueue
element and jmsQueueConnectionFactory
element, see the JMS Queue and JMS Queue Connection Factory documents respectively.
Run the following goal to start Open Liberty in dev mode:
mvn -pl inventory liberty:dev
After you see the following message, your Liberty instance is ready in dev mode:
************************************************************** * Liberty is running in dev mode.
The inventory
microservice records in its inventory the average system load information that it received from potentially multiple instances of the system
service.
Create theInventoryQueueListener
class.inventory/src/main/java/io/openliberty/guides/inventory/InventoryQueueListener.java
InventoryQueueListener.java
link:finish/inventory/src/main/java/io/openliberty/guides/inventory/InventoryQueueListener.java[role=include]
The inventory
microservice receives the messages from the system
microservice. Implement the InventoryQueueListener
class with the MessageListener
interface and annotate with @MessageDriven
for listening the jms/InventoryQueue
queue. Override 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 SystemLoad
object.
Replace the inventory'sserver.xml
configuration file.inventory/src/main/liberty/config/server.xml
inventory/server.xml
link:finish/inventory/src/main/liberty/config/server.xml[role=include]
Add the wasJmsEndpoint
element to ensure that the application server is properly configured to listen and manage incoming JMS connections from any hosts. Set up the messagingEngine
configuration to ensure that the application server can manage incoming message queues more effectively, assigning a reliable and persistent destination for the InventoryQueue
. Configure a jmsQueueConnectionFactory
element to use the InventoryCM
and set properties for the JMS implementation. Define a jmsQueue
element for the inventory system and a jmsActivationSpec
element to configure properties including the destination queue reference, destination type, and maximum concurrency.
You can find the inventory
and system
services at the following URLs:
You can also use curl
command to retrieve the hostname and systemLoad information from the inventory/systems
server endpoint in another command line session:
curl http://localhost:9085/inventory/systems
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 theInventoryEndpointIT
class.src/test/java/it/io/openliberty/guides/inventory/InventoryEndpointIT.java
InventoryEndpointIT.java
link:finish/inventory/src/test/java/it/io/openliberty/guides/inventory/InventoryEndpointIT.java[role=include]
See the following descriptions of the test cases:
-
testNonEmpty()
verifies that the hostname and the load average for each system in the inventory are not empty. -
testValue()
verifies that the hostname and system load average returned by the system service match the ones stored in the inventory service 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.
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
service.
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
[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: 3, 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.
When you are done checking out the service, stop the Liberty instance by pressing CTRL+C
in each command-line session where you ran the system
and inventory
services.
The application has been built and tested. In this section, you’ll learn how to configure Liberty to use IBM MQ as the messaging server.
Start IBM MQ by running the following command on the command-line session:
docker pull icr.io/ibm-messaging/mq:latest
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:latest
If you’re a Mac user, check out this website for the How to build Mac IBM MQ container image guide on building IBM MQ container image.
After building the container image, you can find the image version:
docker image | grep mq
If it builds successfully, you will see an image similar to the ibm-mqadvanced-server-dev:9.4.0.0-arm64
.
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
If the IBM MQ container
runs successfully, you can access https://localhost:9443/ibmmq/console.
Replace thepom.xml
file of the system service.system/pom.xml
system/pom.xml
link:ibmmq/system/pom.xml[role=include]
Add the liberty.var.ibmmq-*
properties for the IBM MQ container. You can change to different values when you deploy the application on production environment without modifying the Liberty server.xml
configuration file.
Replace thepom.xml
file of the inventory service.inventory/pom.xml
inventory/pom.xml
link:ibmmq/inventory/pom.xml[role=include]
Add the liberty.var.ibmmq-*
properties for the IBM MQ container. You can change to different values when you deploy the application on production environment without modifying the Liberty server.xml
configuration file.
Replace theserver.xml
file of the system service.system/src/main/liberty/config/server.xml
system/server.xml
link:ibmmq/system/src/main/liberty/config/server.xml[role=include]
Replace the properties.wasJms
configuration by the properties.wmqjmsra
configuration. All property values are defined in the pom.xml
file that you have replaced in the step. Also, modify jmsQueue
property to set the baseQueueName
value with the ${ibmmq-inventory-queue-name}
variable. Add resourceAdapter
element to provide the RAR file that provides the IBM MQ classes for Java and JMS.
Replace theserver.xml
file fo the inventory service.inventory/src/main/liberty/config/server.xml
inventory/server.xml
link:ibmmq/inventory/src/main/liberty/config/server.xml[role=include]
Refine the jmsQueue
and jmsActivationSpec
configurations with the variables for IBM MQ settings. Add resourceAdapter
element as you did for the system
service in above.
Start the inventory
service by running the following command in dev mode:
mvn -pl inventory liberty:dev
Next, open another command-line session, navigate to the finish
directory, and start the system
service by using the following command:
mvn -pl system liberty:dev
After 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
service by the http://localhost:9085/inventory/systems URL.
Go to the command shell running inventory
dev mode and press enter/return
to run the tests. If the tests pass, you’ll see similar output to the following example:
-------------------------------------------------------
T E S T S
-------------------------------------------------------
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: 3, Failures: 0, Errors: 0, Skipped: 0
After you are finished checking out the application, stop the Liberty instances by pressing CTRL+C
in each command-line sessions where you ran system
and inventory
services.
Run the following commands to stop the running IBM MQ container and clean up the qm1data
volume:
docker stop QM1
docker volume remove qm1data
You just developed a reactive Java application using Jakarta Messaging and Open Liberty.