Migrating to Azure ServiceBus for a Java JMS application

If you have an existing Java based application with a messaging architecture using JMS, how easily can you switch to using Azure ServiceBus? Well, it turns out that if the application is using the AMQP protocol, the switch can likely be done just via reconfiguration rather than changing the code.

Apache ActiveMQ

To make sure I didn’t start with a sample coming from the Microsoft documentation of how to do it I decided to start with the samples that comes with Apache ActiveMQ. If you download it from http://activemq.apache.org/ you will find a folder named examples where the Java version builds with Maven. In the bin folder you’ll find ways to start the ActiveMQ Broker on MacOS or Linux. Then you can run the sample code that sends and receives messages.

mvn dependency:copy-dependencies
mvn package
~/apache-activemq-5.14.3/bin/macosx/activemq console &
mvn exec:java -Dexec.mainClass="Listener"

I’ve modified the sender/publisher code abit so that I can specify the queue, message and number of messages to send on the command line.

Running the listener/receiver produces the below output

Running the sender/publisher produces this output

Switching to using Azure ServiceBus

The URI when using the ActiveMQ Broker is amqp://localhost:5672/, but it could have been shortened to amqp://localhost since 5672 is the amqp port (abit like saying http://localhost:80/). If you are using the secure protocol it would have been amqps://localhost:5671/.  The URI can contain a few other items that are passed as query parameter, and we are going to need 4 of them:

  • jms.username
  • jms.password
  • transport.connectTimeout
  • transport.idleTimeout

Azure ServiceBus uses a Shared Key for authenticating and we are going to pass that in the jms.username/jms.password parameters. The password needs to be url-encoded since it is part of the url. The way Azure ServiceBus then implements its amqp protocol is that it will need to have values high enough for the two timeouts. If you don’t set them, the connection will fail with error messages saying that JMS default timeout of 30000 ms is to low. I have modified the sample code of the Publisher.java file to have a method that creates the URI used for connecting

private static String GetConnectionURI( String[] args ) {
    String user = env("JMS_USER", "admin");
    String password = env("JMS_PASSWORD", "password");
    String host = env("JMS_HOST", "localhost");
    String protocol = "amqp";
    int port = Integer.parseInt(env("JMS_PORT", "5672"));
    if ( port == 5671 ) {
        protocol = "amqps";
    }
    String connectTimeout = env("JMS_CONNECTTIMEOUT", "");
    String idleTimeout = env("JMS_IDLETIMEOUT", "");
    String timeouts = "";
    if ( connectTimeout.length() > 0 ) {
        timeouts += "&transport.connectTimeout=" + connectTimeout;
    }
    if ( idleTimeout.length() > 0 ) {
        timeouts += "&amqp.idleTimeout=" + idleTimeout;
    }
    String ConnectionURI = protocol + "://" + host + ":" + port + "/"
            + "?jms.username=" + user
            + "&jms.password=" + password
            + timeouts;
    return ConnectionURI;
}

So, to migrate the application from using the ActiveMQ Broker to using Azure ServiceBus, I only need to set the environment variables

  • JMS_USER – set to the policy name defined on the ServiceBus namespace. In my case RootManageSharedAccessKey
  • JMS_PASSWORD – set to the Primary Key of that policy. Note! I have to url-encode the key and not paste it in directly. This can be done online with this tool https://www.w3schools.com/tags/ref_urlencode.asp
  • JMS_HOST – set to <–my-sb-namespace–>.servicebus.windows.net
    I use the whole fqdn hetre so you can change it for Azure Germany, China, US Gov, etc
  • JMS_PORT – set to 5671 since Azure ServiceBus only supports secure communications
  • JMS_CONNECTTIMEOUT – set to 60000 (60 seconds)
  • JMS_IDLETIMEOUT – set to 150000 (150 seconds, ie 3 min 30 secs)

The Java code to connect to the messaging system using JMS looks like this

JmsConnectionFactory factory = new JmsConnectionFactory( ConnectionURI );
Connection connection = factory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = null;
if (destinationName.startsWith(TOPIC_PREFIX)) {
    destination = session.createTopic(destinationName.substring(TOPIC_PREFIX.length()));
} else {
    destination = session.createQueue(destinationName);
}
MessageProducer sender = session.createProducer(destination);

Running the sending code again with these environment variables set, I get the following output (in the IntelliJ IDEA debug console).

The listener/receiver of the ServiceBus queue is a little C# program I wrote to illustrate interoperability.

Summary

The purpose of this blog post is to highlight that an existing Java application that uses JMS for it’s messaging handling do not need to be rewritten to start using ServiceBus as its backend broker. Java application can use many different techniques to find messaging resources, like JNDI, config files, etc, but whatever it is you probably can reconfig it to use Azure ServiceBus since ServiceBus supports the AMQP protocol. This gives you alot of options when rehosting a Java application to use public cloud, such as Azure.

References

ActiveMQ
http://activemq.apache.org/

How to use JMS with ServiceBus and AMQP – Microsoft documentation
https://docs.microsoft.com/en-us/azure/service-bus-messaging/service-bus-java-how-to-use-jms-api-amqp

Github sources
https://github.com/cljung/jms-amqp/