Comparing Messaging standards: JMS vs AMQP

In this article we will discuss about messaging standards that you can use decouple sender and receiver of messages by means of a Messaging Broker. In this first article we will focus on the differences between JMS and AMQP.

Overview of JMS

Most of Java EE developers know that JMS is a the standard Java API for communicating with Message Oriented Middleware (MOM). As a matter of fact, JMS is a part of the Java JEE, and allows two different Java applications to communicate; those applications could be using JMS clients and yet still be able to communicate, yet kept being decoupled. It’s considered to be robust and mature.

Since JMS is part of Java EE, its typical usage is for client and servers that are running in a JVM. What if client and server are using a different language or platform ? Let’s discuss this in the next section.

Overview of AMQP

A common solution is to switch to a different protocol, such as AMQP (which stands for ‘Advanced Message Queuing Protocol’). AMQP is an open standard application layer protocol for delivering messages. It can go P-2-P (One-2-One), publish/subscribe, and some more, in a manner of reliability and secured way.

At a bird eye’s view are the main features of AMQP:

  • Platform independent wire level messaging protocol
  • Consumer driven messaging
  • Interoperable across multiple languages and platforms
  • It is the wire level protocol
  • Can achieve high performance
  • Supports long lived messaging
  • Supports classic message queues, round-robin, store and forward
  • Has support for transactions (across message queues) and distributed transactions (XA, X/Open, MS DTC)
  • Has support for SASL and TLS
  • Allows to control the message flow with Meta-data

So the main difference is that JMS is an API which runs on top of a Java EE/ Jakarta EE compliant application servers. On the other hand, AMQP is not an API but a binary wire protocol that allows the interoperability between different vendors and platforms.

Therefore, if your Broker supports both the JMS standard and AMQP protocol, you can use a pure Java JMS client to communicate with the AMQP protocol.

How to communicate with WildFly / EAP 7 over AMQP

WildFly and JBoss EAP 7 ship with an embedded ActiveMQ (Artemis). The embedded broker, however, only supports the core (JMS) protocol. Therefore, you cannot use the embedded broker to communicate over the AMQP, MQTT or STOMP protocol.

The way to go is to use a standalone ActiveMQ (Artemis) or Red Hat Active MQ 7.

A sample JMS Client using AMQP Protocol

Let’s see a practical example of it. A messaging server which supports AMQP is Apache Artemis MQ (and its predecessor ActiveMQ) and its Enterprise version called Red Hat AMQ.

Firslty, in order to build a sample JMS client/server application featuring the AMQP protocol, you need to use the org.apache.qpid.jms.JmsConnectionFactory Constructor as follows:

package org.apache.activemq.artemis.jms.example;

import javax.jms.Connection;
import javax.jms.ConnectionFactory;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.Queue;
import javax.jms.Session;
import javax.jms.TextMessage;

import org.apache.qpid.jms.JmsConnectionFactory;

public class AMQPQueueExample {

   public static void main(String[] args) throws Exception {
      Connection connection = null;
      ConnectionFactory connectionFactory = new JmsConnectionFactory("amqp://localhost:5672");

      try {

         // Step 1. Create an amqp qpid 1.0 connection
         connection = connectionFactory.createConnection();

         // Step 2. Create a session
         Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

         // Step 3. Create a sender
         Queue queue = session.createQueue("jms.queue.exampleQueue");
         MessageProducer sender = session.createProducer(queue);

         // Step 4. send a few simple message
         sender.send(session.createTextMessage("Hello world "));

         connection.start();

         // Step 5. create a moving receiver, this means the message will be removed from the queue
         MessageConsumer consumer = session.createConsumer(queue);

         // Step 7. receive the simple message
         TextMessage m = (TextMessage) consumer.receive(5000);
         System.out.println("message = " + m.getText());

      }
      finally {
         if (connection != null) {
            // Step 9. close the connection
            connection.close();
         }
      }
   }
}

Furthermore, please note that the example above is just an Hello World demo. In a real-world application would typically use a long-lived MessageProducer to send multiple messages using it over time. Opening and closing a Connection, Session, and MessageProducer per message not an efficient pattern.

Next, on the configuration side, make sure you have in your ArtemisMQ configuration an acceptor for the AMQP protocol on the port 5672:

 <acceptor name="artemis">tcp://0.0.0.0:61616?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;amqpMinLargeMessageSize=102400;protocols=CORE,AMQP,STOMP,HORNETQ,MQTT,OPENWIRE;useEpoll=true;amqpCredits=1000;amqpLowCredits=300;amqpDuplicateDetection=true</acceptor>

 <!-- AMQP Acceptor.  Listens on default AMQP port for AMQP traffic.-->
 <acceptor name="amqp">tcp://0.0.0.0:5672?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=AMQP;useEpoll=true;amqpCredits=1000;amqpLowCredits=300;amqpMinLargeMessageSize=102400;amqpDuplicateDetection=true</acceptor>

 <!-- STOMP Acceptor. -->
 <acceptor name="stomp">tcp://0.0.0.0:61613?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=STOMP;useEpoll=true</acceptor>

 <!-- HornetQ Compatibility Acceptor.  Enables HornetQ Core and STOMP for legacy HornetQ clients. -->
 <acceptor name="hornetq">tcp://0.0.0.0:5445?anycastPrefix=jms.queue.;multicastPrefix=jms.topic.;protocols=HORNETQ,STOMP;useEpoll=true</acceptor>

 <!-- MQTT Acceptor -->
 <acceptor name="mqtt">tcp://0.0.0.0:1883?tcpSendBufferSize=1048576;tcpReceiveBufferSize=1048576;protocols=MQTT;useEpoll=true</acceptor>

Sending messages using AMQP Protocol with a non-Java client

Let’s take the case where a Ruby client wants to send a message to an AMQP broker, or viceversa. Or vice versa. Ruby client can’t talk JMS, so we need a message broker that can bridge the two platforms, Java and Ruby.

As AMQP provides a standard messaging protocol that stands across all platforms it doesn’t matter which AMQP client is used. In the following example, taken from ArtemisMQ examples (https://github.com/A-MQ/activemq-artemis/tree/master/examples) a Ruby client using the Proton library sends a message to the testQueue running on the ArtemisMQ server:

require 'qpid_proton'

address = "127.0.0.1:5672/testQueue"

messenger = Qpid::Proton::Messenger.new
messenger.start

msg = Qpid::Proton::Message.new
msg.address = address
msg.subject = "The time is #{Time.new}"
msg.body = "Hello world!"
msg.correlation_id = "554545"

begin
    messenger.put(msg)
rescue Qpid::Proton::ProtonError => error
    puts "ERROR: #{error.message}"
    exit
end

begin
    messenger.send
rescue Qpid::Proton::ProtonError => error
    puts "ERROR: #{error.message}"
    puts error.backtrace.join("\n")
    exit
end

puts "SENT: " + msg.body

messenger.stop

That’s all. We have covered the main differences between JMS and AMQP and produced a sample Java and Ruby application to send and receive messages.

Found the article helpful? if so please follow us on Socials