Towards Cloud-readiness: Why We Left JMS on the Ground
Cloud-Readiness of Monoliths and their Services
During the project we have to answer a lot of questions and challenges that arise when considering cloud migration. Finding answers often means analysing a monolith and finding a way to use cloud native services. For example that can be a cloud native database instead of a typical hosted one. You can read more about this topic in our blog post The Myth of Easily Changing the Database-System Underneath a Persistence Layer.
However, there are not only databases and storage services connected to this customer’s monolith. There is also the issue of communication via JMS. That means we have to find a cloud native JMS provider.
Using JMS in the Cloud
Messaging via JMS in the cloud would be easy if there were a cloud native JMS Broker. However this requirement is also a problem, since JMS is a Java specific API. That means a JMS broker, however, cannot be cloud native, since it is not language agnostic.
Now there is a new messaging-player on the block: AMQP. It is designed to be a language independent messaging protocol. There are also cloud native AMQP brokers like RabbitMQ and Azure Service Bus. But how migrate an application from JMS to AMQP ? The answer is an AMQP client which implements the JMS API. For example Apache QPID.
The Monolith
Let’s first describe our project environment: a JavaEE monolith which runs on WebSphere Liberty Profile. That monolith communicates with IBM MQ as a JMS broker, amongst other technologies. A MDB is invoked if another application sent a message into a queue.Therefor the application server’s configuration (in WLP it’s just the server.xml file) defines some Queues, JMS Connection Factories and ActivationSpecifiations. Consider the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<jmsQueue id="FancyQueueId" jndiName="jms/FancyQueue"> <properties.wmqJms baseQueueName="FamousBase"/> </jmsQueue> <jmsActivationSpec authDataRef="MQ" id="jms/activationSpec"> <properties.wmqJms channel="FamousChannel" destinationRef="FancyQueue" destinationType="javax.jms.Queue" hostName="172.1.42.1" port="1414" queueManager="FancyQueueManager"/> </jmsActivationSpec> <jmsQueueConnectionFactory id="JCA ConnectionFactory" jndiName="famousice/jcafra_retacc" /> |
Use an AMQP-Broker through JMS in the Monolith
Apache QPID was chosen as an AMQP-Broker through a JMS interface. Point of departure: a Google search that leads only to a few helpful results. A colleague and I figured out that since the QPID Resource Adapter (in order to use JCA) has been surprisingly discontinued. To save you a lot of time we have the following tips:
- It is necessary to use a generic resource adapter to define the needed resources.
- There is an Adapter for AMQP V0.91 (see MavenCentral) which you can use for RabbitMQ.
- If you want to use Azure Service Bus (which has a different AMQP version) you can use Sun’s generic adapter (see here).
The configuration could look like the following example:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<resourceAdapter id="genericra" location="${generic.rar.location}"> <customize interface="javax.jms.ConnectionFactory" suffix="cf"/> <customize interface="javax.jms.TopicConnectionFactory" suffix="tcf" /> <customize interface="javax.jms.QueueConnectionFactory" suffix="qcf" /> <customize interface="javax.jms.Queue" suffix="q" /> <properties.genericra LogLevel="FINEST" ConnectionFactoryClassName="org.apache.qpid.jms.JmsConnectionFactory" QueueConnectionFactoryClassName="org.apache.qpid.jms.JmsConnectionFactory" TopicConnectionFactoryClassName="org.apache.qpid.jms.JmsConnectionFactory" XAConnectionFactoryClassName="org.apache.qpid.jms.JmsConnectionFactory" UnifiedDestinationClassName="org.apache.qpid.jms.JmsDestination" TopicClassName="org.apache.qpid.jms.JmsTopic" QueueClassName="org.apache.qpid.jms.JmsQueue" monitoring="true" ProviderIntegrationMode="javabean" ConnectionFactoryProperties="UsernameFancyBroker,Password=PassowrtFancyBroker" /> </resourceAdapter> |
Having those definitions we can define Connection factories, Queues and Activation specifications. Very similar to the old IBM MQ configuration.
Leave JMS on the Ground
As described above, our application wants to trigger a Message Driven Bean if something comes through the Queue. To trigger a MDB, we must be running inside a CDI container. And that is exactly the problem with QPID in our project.
Both possible JCA resource adapters
- do not support the for ‘async-to-sync’ needed method
Java1Session.createConsumer(Destination destination, java.lang.String messageSelector) - do not support for MessageDrivenBeans needed method
Java1Connection.createConnectionConsumer(Destination destination, String messageSelector, ServerSessionPool sessionPool, int maxMessages)
Besides that, in general RabbitMQ does not support XA transactions.
JMS in the cloud is be possible. It is debatable if it would a good and proper messaging solution in the cloud. But due to the technical incompatibilities in our project, we have chosen to “leave JMS on the ground” instead of using it in the cloud. We found another way to do cloud native messaging with JavaEE monoliths, which we will describe a following blog post.
Recent posts






Comment article