In this tutorial, we’ll create applications that use the AMQP open messaging protocol which IBM MQ supports with QPid AMQP JMS APIs. We’ll run these applications as standard Java applications, as Quarkus applications, and finally as GraalVM applications.
Before we get started, let’s explain a bit about the technologies we’ll be using:
- Quarkus & GraalVM
- IBM MQ Qpid samples
Using AMQP, an open standard messaging protocol
AMQP is a non-proprietary messaging protocol that provides an open standard wire protocol for messaging. Apache Qpid is an open source project that provides messaging tools for AMQP as core libraries. They don’t claim it, but think of it as a development kit for AMQP-based apps. For Java JMS 2.0, Apache Qpid provides a .jar library. Speaking of JMS 2.0 , it’s a Java API for messaging.
You may already have JMS applications that use the IBM MQ JMS libraries, com.ibm.mq.allclient. While this library supports JMS 2.0 and offers support for the full spectrum of messaging features available in IBM MQ, it is proprietary. Some projects prefer to adopt open standards, including over the wire protocols like AMQP.
IBM MQ provides support for AMQP APIs through an AMQP channel that accepts connections from AMQP client applications. Using IBM MQ, Apache Qpid JMS applications can do publish/subscribe messaging and point-to-point messaging. Messaging is not just confined to AMQP client applications, as intercommunication with client applications based on other IBM MQ API stacks is possible.
Using Quarkus and GraalVM, for our Java apps
Now that we’ve got that messaging jargon out of the way, we need to continue on explaining some of the Java jargon that you’ll need to understand for this tutorial. In this tutorial, we use both Quarkus and GraalVM.
Quarkus is a native Java framework that creates a Java platform for Kubernetes, microservices, and serverless environments. Quarkus’ native execution delivers runtime efficiencies such as fast start up, low memory utilization, and smaller application and container footprints for Java applications.
GraalVM provides a universal virtual machine for running applications across a range of programming languages including Java. GraalVM allows programs, including Java code, to be compiled into a native executable. The JVM is compiled into the native executable and provides features such as memory management and thread scheduling for the application. So, with GraalVM, your Java apps become leaner, faster, and no longer need a run anywhere JVM. They become better adapted to becoming containerised and for running in the cloud. JVMs provide a run anywhere capability that is an unneeded overhead in a controlled container. This allows the entire Java stack to be optimised when the Java application is compiled by Quarkus for execution as a GraalVM application.
However, not all Java apps will compile and run on GraalVM because not all applications or libraries are compatible with this level of optimization. Particularly, code that uses the intricacies of Java for their own optimization can not use GraalVM. This becomes a significant barrier to Java applications that want to migrate to the cloud. Using a library stack for JMS that runs on GraalVM opens up a path for messaging applications.
Hang on. You might be wondering where Open Liberty fits in to all this. Essentially, if you are looking to deploy new functions and microservices, then Quarkus is a good choice. GraalVM’s native executable capabilities give the ideal runtime characteristics for microservices and serverless functions. Read more about choosing the right Java runtime for the job in this article.
Using the IBM MQ Qpid samples to use AMQP
In this tutorial, we will use samples from our mq-dev-patterns repository, which we created as a home for getting started with messaging in a range of programming languages. The samples are ready to run most of the JMS API classes that you are thinking of trying out in your applications, whether the API feature is supported by IBM MQ over AMQP or not.
The Qpid JMS samples, which are available starting in IBM MQ 9.2.1, support AMQP 1.0 beyond publish/subscribe messaging. There was already a Qpid JMS Client stack for Quarkus, so we set about configuring that stack to use IBM MQ, and built a sample that explored the JMS 2.0 API, blind as to whether each feature we exercised was actually supported in IBM MQ 9.2.1.
To verify API functionality using the Qpid JMS 2.0 stack on Quarkus and GraalVM, we needed to exercise the same set of API features outside of Quarkus, in other words in a regular Java stack. So, we created samples that exercised the JMS API. We say samples, but it’s really one sample with two different veneers, but we’ll come to that. The samples exercise features that are supported as well as features that are not supported by IBM MQ over the AMQP Channel. The samples can run as regular stand-alone applications, as well as on the Quarkus stack as compiled native applications.
To complete this tutorial, you’ll need to install:
- Git, Maven, and a Java JDK. I use SDKMAN to install and manage versions of Java and Maven on my machine.
You’ll also need a Red Hat Registry ID, because you will be building a customized version of the IBM MQ docker container.
Completing this tutorial should take about 30 minutes.
- Set up the AMQP channel in IBM MQ
- Run the Qpid AMQP JMS client apps as standard Java apps
- Run the apps as compiled executables on GraalVM
- Explore the JMS API through the sample client apps
Step 1. Set up the AMQP channel in IBM MQ
For AMQP client applications to successfully communicate with IBM MQ, the IBM MQ AMQP service needs to be running. To enable the AMQP service, you need to clone and customise the MQ Container.
Clone the MQ Container repository by running this command:
git clone https://github.com/ibm-messaging/mq-container
CD to the cloned repository,
Enable AMQP in the MQ container by editing the
install-mq.shfile, and changing the following AMQP line to:
Set up AMQP authority, channel, and service properties by adding the contents of the
add-dev.mqsc.tplfile to the bottom of the
/incubating/mqadvanced-server-dev/10-dev.mqsc.tplfile in your cloned repository.
Build a developer Docker image.
a. Log in to the Red Hat Registry:
docker login registry.redhat.io
b. Build a development server image (for example, for IBM MQ 18.104.22.168):
MQ_VERSION=22.214.171.124 make build-devserver
c. Check the image ID:
docker image ls
You should see output similar to this output:
Start the customized MQ container:
docker run --env LICENSE=accept --env MQ_QMGR_NAME=QM1 --env MQ_APP_PASSWORD=passw0rd --publish 1414:1414 --publish 9443:9443 --publish 5672:5672 --detach ibm-mqadvanced-server-dev:126.96.36.199-amd64
Note the port 5672, which is the AMQP port.
Step 2. Run the Qpid AMQP JMS client apps as standard Java apps
You should now have a running instance of IBM MQ with the AMQP service running. Now, we’ll use the Qpid sample from the mq-dev-patterns repository and run it as a regular JMS application.
Clone the mq-dev-patterns repository:
git clone https://github.com/ibm-messaging/mq-dev-patterns
CD to the cloned repository, and then to the
amqp-qpid/qpid-standarddirectory where the Qpid samples reside.
Configure the sample for IBM MQ.
src/main/resources/jndi.properties file is already configured for a customized MQ container. This file contains place holders for the
connection url, queues, topics names, and MQ user username and password that the application uses. If you followed the instructions to create a customised MQ container, then you can leave most of these settings as they are. Otherwise edit them to suitable settings for your MQ Server.
The setting for
queue.myReplyQueueLookup is commented out. This way the application will use temporary queues when requested to run in request / response mode. If you want to use a permanent queue for replies then uncomment the line
queue.myReplyQueueLookup = DEV.QUEUE.2.
Build the AMQP JMS client application. The provided maven pom.xml file builds an uber jar file that contains everything that is needed to run the application. Build the application by running the maven command.
mvn clean package
Run the application using the default mode settings:
java -jar target/mq-dev-patterns-qpid-0.1.0.jar
This will put 5 messages onto the queue. You can retrieve the messages by running the command:
java -jar target/mq-dev-patterns-qpid-0.1.0.jar get
Let’s be clear here. These are Qpid AMQP client apps that use the Qpid JMS API stack, and nothing is IBM propriety. The configuration
jndi.properties files is the only link to IBM MQ.
Step 3. Run the apps as compiled executables on GraalVM
Now that you’ve built and run the Qpid sample client app as a regular Java application, you’re ready to run the same application (with a different veneer): as compiled native code on Quarkus.
Regular Java apps can have their own main method, but in Quarkus, apps are run inside a Jakarta EE container, with application endpoints indicated by the @ApplicationScoped annotation. The single configurable app allows us to investigate features of the API without having to rebuild the applications.
This is why in the end there are only two samples. Or rather two sample veneers. One for regular java with its own main method, and one for Quarkus annotated with
@ApplicationScoped which allows the Quarkus framework to find and start it. Beyond that, the code is shared and hence the same. The application determines, based on options passed in on the command line, which JMS API features to exercise.
CD to your cloned mq-dev-patterns repository and then to the Qpid samples for Quarkus,
Configure the sample for IBM MQ.
Quarkus doesn’t support JNDI, but does provide an alternative mechanism for application configuration. The repository contains an application.properties that is already configured for a customised MQ container. If you want to make changes then edit the
src/main/resources/jndi.propertiesfile. This file contains place holders for the connection url, queues, topics names and MQ user username and password, that the application uses. It also contains a default set of command line arguments under the setting
amqp-mqtest.appargs. If you followed the instructions to create a customized MQ container, then you can leave most of these settings as they are. Otherwise, edit them to suitable settings for your MQ Server.
Build the AMQP client app. The provided maven files builds a GraalVM compiled executable. Build the application by running the command:
./mvnq package -Pnative
Run the app. The application can be started using default mode settings by running the command:
This will put 5 messages onto the queue. You can retrieve the messages by running the command:
-Damqp-mqtest.appargscommand line setting overrides the value in the
Although the source code is Java, the executable is a binary, and Java is not being used to execute it. Although it takes longer to build than the regular Java veneer, it executes quicker.
Step 4. Explore the JMS API through the sample clients
You should now have built and running versions of both the regular Java Qpid Client and the Quarkus binary version.
You are now ready to start to exercise the JMS API through these samples. Unless you make your own code changes, you will not need to rebuild the applications as their JMS modes are command line configurable. The applications determine, based on options passed in on the command line, which JMS API features to exercise.
The options are mostly cumulative which can lead to interesting combinations. Mostly cumulative as there are some exclusive option sets where the last option specified takes precedence.
The mode options also trigger aspects of the JMS API that are not supported by IBM MQ 9.2.1 over the AMQP channel. For example, browse over AMQP isn’t supported by IBM MQ 9.2.1, but that doesn’t stop us from trying the API to verify the behaviour. If you are using IBM MQ 9.2.2 you will find that browse over AMQP is supported.
Commands to run the client apps
To put message on queues using peer-to-peer messaging of high priority messages, use this command to run the GraalVM binary:
Use this command to run the equivalent for the regular Java veneer:
java -jar /target/mq-dev-patterns-qpid-0.1.0.jar put,queue,high
Subsequent examples will show only the options in the form
We suggest that you used both veneers to interact with each other as you explore the JMS API.
Put / Get / Browse
At the most basic level we have the exclusive set of options put, get and browse. They all default to using peer-to-peer messaging over queues. If you want to use publish/subscribe over topics then add
topic as an option. The mode options
queue are another exclusive set.
For peer-to-peer For put use the options:
For get use the options:
For publish/subscribe For publish use the options:
For subscribe use the options:
The JMS API allows us to send 5 types of messages, Text, Stream, Map, Object and Bytes. The samples are configured to send and receive all of these types. By default the put and publish modes send 3
StreamMessage, and a
The sending of
BytesMessage are optional. When we created the examples, they also sent, by default, but we made them optional as they showed interesting behaviour.
- To add a BytesMessage, and ObjectMessage as part of the message cycle use these options:
Which leads us to the reason we made sending an
ObjectMessage a configurable option.
ObjectMessage is the only IBM MQ 9.2.1 supported feature over AMQP, attempted by the sample, that fails in the GraalVM environment. The GraalVM app running in put mode throws an
UnsupportedFeatureError exception in the JMS API while attempting to create the ObjectMessage. Interestingly, the GraalVM app running in get mode is able to retrieve and parse an
ObjectMessage successfully, if one is on the queue or topic. We have an issue on the qpid quarkus extension that indicates that there might be a fix in GraalVM 21.0.0.
If you want to try request/response then add reply to the list of options in put mode.
For a regular request/response, use:
This adds a reply to queue (either temporary or permanent) to the message, which a get will see and reply to. The put application waits some time for the response.
You can add a reply to queue to a published message by specifying this:
This demonstrates a polling pattern as all subscribers will see the reply to queue and will send a reply.
The applications can be set to send higher or lower priority messages. Higher priority messages will be taken off queues and topics before lower priority messages.
To send low priority messages, use:
To send high priority messages, use:
More options and combinations
The command line options for the samples allow you to control the priority of messages, run the gets asynchronously or synchronously, try session modes like client acknowledge durable subscriptions, request/response, message persistence and custom properties.
As well as features not supported in IBM MQ 9.2.1 over the AMQP channel such as use of a selector, transactions, message expiry, and message delay.
For a full list of mode settings see the sample documentation.
Summary and next steps
In this tutorial, you set up and ran working sample client applications based on the Qpid AMQP JMS stack that are able to use messaging with IBM MQ over the AMQP channel. These applications run as regular Java apps and as compiled native Quarkus apps that are ready to deploy into containers and into the cloud. These applications are command line configurable so you can explore JMS capability and GraalVM compatibility without having to rebuild them.
For more IBM MQ samples across several languages, explore our samples and patterns repository.