In our introduction to local transactions article, we looked at what transactions are, how they are defined in JMS and in IBM MQ, and shared a few samples for simple usage in stand-alone Java SE applications.
In this article, we look at what you need to know to write JMS messaging applications for enterprise application servers. Application servers provide abstractions that remove some of the complexity from your app so you can concentrate on the business logic alone. The trade-off is that you have to know how to use the common services like transactions that they provide to achieve your goal.
Jakarta Enterprise Edition (Jakarta EE)
Enterprise applications are not usually stand alone; there are lots of them, doing different things, and connecting to resources through different protocols to exchange data and keep the state of all the participating resources consistent.
It makes sense for these enterprise applications to have a common environment to run in. And, once they're there, it might make sense that some of the repeated, common functionality is centralized on the platform so that new apps don't have to implement the same behaviors over and over again.
For Java, we start with Jakarta Enterprise Edition (Jakarta EE). Jakarta EE is a specification that defines a Java platform that is referred to as a Jakarta EE container, which is a server environment in which our application can run that also provides common services and APIs.
Before we jump into how to build transactional JMS enterprise applications on an app server, let’s review some basic app server concepts first:
Enterprise Java beans (EJBs). Enterprise java beans are server-side components that provide some business logic to application clients. Clients invoke the EJB methods to use a service provided by the bean. EJBs run in EJB containers which provide services like transactionality and security.
Message-driven beans (MDBs). Message-driven beans are a type of EJBs. They allow enterprise applications to process messages asynchronously. They are usually message listeners listening for messages from applications or other components. When a message arrives, the EJB container calls the message listener’s onMessage method to process the message.
Contexts and dependency injection. Contexts provide lifecycle management for stateful components like beans and dependency injection allows for injection of components into applications to be used at deployment time.
Descriptors and annotations. Deployment descriptors are .xml files that describe how applications should be deployed and what dependencies should be injected. Annotations are used in applications to add the injection in classes and methods, removing the need for XML configuration files. Annotations are more recent, but both are still in use and can be used in combination.
This is just the tip of the iceberg of what a developer should be familiar with in order to write good Jakarta EE applications that run in app servers.
Let's look at a more specific set of resources that make up an app server, based on the Jakarta EE specification.
In the following diagram, there is the Open Liberty app server working with the MQ resource adapter to allow a message to be sent to a queue on an IBM MQ queue manager. Both Open Liberty and MQ support JMS messaging. A web application sends a message to a queue on which a message-driven bean is listening. The message-driven bean is also running in the app server but is not consuming any resources until a message arrives. The MQ resource adapter enables the communication between the Open Liberty app server and IBM MQ such that a message is sent to the queue and the listener gets the message when it arrives on the queue.
Figure 1 Applications using Liberty app server resources and MQ Resource adapter to connect to a queue manager and send/receive message
In the previous article, we saw that a in a Java SE scenario, the transaction was coordinated between IBM MQ as the messaging provider and the JMS API. We added a transacted property to a JMS context (or session) before starting a method or a group of methods and ended the transaction with a commit. Depending on how critical the messages we were sending inside the transaction were, we made sure that when rolling them back that they ended up on the queue they started from. If a message was just created before being a part of the transaction, it might have been thrown away. If the message was important and it didn't come from a queue that it could go back to, we made use of the JMS API call to put it to a backout queue we named for this purpose. If we didn't name the backout queue, we relied on the queue manager to put the message on the dead letter queue.
In the app server environment, service components take over the role of managing transactions. Rather than just knowing which JMS classes to use, you need to know that at a certain point the Jakarta EE platform will take over, do things under the covers, and provide transactionality for your app. Your application just needs to behave in the right way.
This scenario is where we get to the concept of transaction demarcation. This means setting the boundaries of where the transaction begins and ends.
In Jakarta EE, there are two ways to manage transactions. For each method, you need to understand what and how much you're responsible for when including a transaction in your app.
Container-managed transaction demarcation
In container-managed transaction demarcation, the EJB container sets the transaction boundaries. In basic terms, the transaction starts before the bean method starts and commits just before the method exits. Read more about container-managed transactions and how to use the attributes to set the scope for this kind of transaction.
Bean-managed transaction demarcation
Bean managed transaction demarcation allows developers to explicitly mark the transaction boundaries. It allows for a more fine-grain control over transactions. Read this tutorial for more on bean managed transactions.
Using developer-friendly Spring
Spring promises a more developer friendly approach to using the resources that a Jakarta EE compliant framework provides.
Spring provides its own abstraction on top of the Jakarta EE specification. Spring works with plain old java objects (POJOs) and does not require you to understand how Jakarta EE works. The Spring framework hides the app server layer and gives you a different way of achieving your goals.
With the EJB container, you can give it an annotation or a descriptor and the app server orchestrates things for you, with the EJ bean you do it programmatically. In Spring, it is the same. You can rely on the container and they'll manage it for you, or you can do it yourself. You just have to learn a little about Spring annotations and classes, but the framework will do the rest for you.
If your application is built for Spring, it is still portable. Spring gives you an app server agnostic way of doing things.
Declarative transaction management is Spring's preferred way of working with transactions because it has less impact on the application code. Spring warns developers that it is useful to understand that the functionality of transactions goes beyond the @Transactional annotation and the @EnableTransactionManagement configuration. The support for transactions is provided through Spring's Aspect Oriented Programming (AOP) proxies and transactional metadata. Read more about it in this section of the Spring framework docs.
We're mentioning Spring's declarative transaction management because this is what we're using in our IBM MQ samples. In practical terms, we can demonstrate simple usage through these samples. Let’s see how transactions work in Spring.
Transactions with IBM MQ and Spring
We provide two sets of simple transacted samples.
The simple application shows a transaction with a commit and a rollback.
The requester sample is a request sample that puts a message on the queue using the Spring send and receive method and provides a temporary queue where it waits for a reply.
The responder sample is a message-driven response/listener sample that receives a message, gets the reply queue from the requester, and sends a message back.
These samples are designed to work with the IBM MQ container with the default developer configuration.
How do I get the samples? We give you a couple of options for exploring the samples.
The samples are included in this GitHub repository that has the code for integrating MQ JMS with Spring (the mq-spring-boot-starter).
Follow the instructions in the main mq-jms-spring Readme to clone the repo, build it, and run the samples locally with Gradle.
If you need step-by-step instructions, you can also clone the mq-dev-patterns repository and follow the instructions in the Spring directory Readme to use the samples from mq-jms-spring and run them with Maven.
The features of Spring that the samples are using
For a quick note on some of the annotations we're using in our samples, read a bit more below or jump straight into the samples.
Spring Boot
We use Spring Boot in our applications. Spring Boot is an extension to Spring that allows for building and running of stand-alone Spring applications without much configuration. Along with the mq-spring-boot-starter that provides the helper classes for integrating with IBM MQ, you can get started very quickly.
We use Spring's externalized configuration properties in the form of an application.properties file which Spring Boot finds and loads when the application starts. This is how we provide some of the MQ connection variables and user details for our app to access the queue manager.
We use the JmsTransactionManager because we want to retrieve the JMS Session and use the same transaction to send a reply after we get a request message.
TransactionStatus represents the status of a transaction and can be retrieved to find out the status information and programmatically request a rollback.
Summary
In this article, we looked at how JMS messaging applications work in applications servers, both Jakarta EE and Spring. We looked at what it takes for transactions to work in such environments.
While Jakarta EE compliant application servers offer a sophisticated environment for applications to make use of the platform's services such as transactionality, their complexity, packaging, and deployment strategies might create barriers for developers to get started with easily.
The Spring framework seeks to simplify access to its resources by cutting out the middle layer by hiding it behind the abstractions that POJOs can use through straight forward annotations while the framework wires everything together as needed for applications to work.
Transactions are an essential part of enterprise application programming. Regardless of whether your applications need to work with Jakarta EE servers or Spring, if the messaging payload is of value, IBM MQ is flexible and can support either.
About cookies on this siteOur websites require some cookies to function properly (required). In addition, other cookies may be used with your consent to analyze site usage, improve the user experience and for advertising.For more information, please review your cookie preferences options. By visiting our website, you agree to our processing of information as described in IBM’sprivacy statement. To provide a smooth navigation, your cookie preferences will be shared across the IBM web domains listed here.