Migrate existing Java Message Service (JMS) 2.0 applications from Apache ActiveMQ to Azure Service Bus

This article discusses how to modify an existing Java Message Service (JMS) 2.0 application that interacts with a JMS Broker to interact with Azure Service Bus instead. In particular, the article covers migrating from Apache ActiveMQ or Amazon MQ.

Azure Service Bus supports Java 2 Platform, Enterprise Edition and Spring workloads that use the JMS 2.0 API over Advanced Message Queueing Protocol (AMQP).

Before you start

Differences between Azure Service Bus and Apache ActiveMQ

Azure Service Bus and Apache ActiveMQ are both message brokers, functioning as JMS providers for client applications to send messages to and receive messages from. They both enable the point-to-point semantics with queues, and publish-subscribe semantics with topics and subscriptions.

Even so, there are some differences between the two, as the following table shows:

Category ActiveMQ Azure Service Bus
Application tiering Clustered monolith Two-tier
(gateway + back end)
Protocol support
  • AMQP
  • STOMP
  • OpenWire
AMQP
Provisioning mode
  • Infrastructure as a service (IaaS), on-premises
  • Amazon MQ (managed platform as a service)
Managed platform as a service (PaaS)
Message size Customer configurable 100 MB (Premium tier)
High availability Customer managed Platform managed
Disaster recovery Customer managed Platform managed

Current supported and unsupported features

The following table lists the Java Message Service (JMS) features that Azure Service Bus currently supports. It also shows features that are unsupported.

Feature API Status
Queues
  • JMSContext.createQueue( String queueName)
Supported
Topics
  • JMSContext.createTopic( String topicName)
Supported
Temporary queues
  • JMSContext.createTemporaryQueue()
Supported
Temporary topics
  • JMSContext.createTemporaryTopic()
Supported
Message Producer /
JMSProducer
  • JMSContext.createProducer()
Supported
Queue browsers
  • JMSContext.createBrowser(Queue queue)
  • JMSContext.createBrowser(Queue queue, String messageSelector)
Supported
Message Consumer/
JMSConsumer
  • JMSContext.createConsumer( Destination destination)
  • JMSContext.createConsumer( Destination destination, String messageSelector)
  • JMSContext.createConsumer( Destination destination, String messageSelector, boolean noLocal)

noLocal is currently not supported
Supported
Shared durable subscriptions
  • JMSContext.createSharedDurableConsumer(Topic topic, String name)
  • JMSContext.createSharedDurableConsumer(Topic topic, String name, String messageSelector)
Supported
Unshared durable subscriptions
  • JMSContext.createDurableConsumer(Topic topic, String name)
  • createDurableConsumer(Topic topic, String name, String messageSelector, boolean noLocal)

noLocal is currently not supported and should be set to false
Supported
Shared non-durable subscriptions
  • JMSContext.createSharedConsumer(Topic topic, String sharedSubscriptionName)
  • JMSContext.createSharedConsumer(Topic topic, String sharedSubscriptionName, String messageSelector)
Supported
Unshared non-durable subscriptions
  • JMSContext.createConsumer(Destination destination)
  • JMSContext.createConsumer( Destination destination, String messageSelector)
  • JMSContext.createConsumer( Destination destination, String messageSelector, boolean noLocal)

noLocal is currently not supported and should be set to false
Supported
Message selectors depends on the consumer created Supported
Delivery Delay (scheduled messages)
  • JMSProducer.setDeliveryDelay( long deliveryDelay)
Supported
Message created
  • JMSContext.createMessage()
  • JMSContext.createBytesMessage()
  • JMSContext.createMapMessage()
  • JMSContext.createObjectMessage( Serializable object)
  • JMSContext.createStreamMessage()
  • JMSContext.createTextMessage()
  • JMSContext.createTextMessage( String text)
Supported
Cross entity transactions
  • Connection.createSession(true, Session.SESSION_TRANSACTED)
Supported
Distributed transactions Not supported

Considerations

The two-tiered nature of Azure Service Bus affords various business continuity capabilities (high availability and disaster recovery). However, there are some considerations when you're using JMS features.

Service upgrades

In case of service bus upgrades and restarts, temporary queues or topics are deleted. If your application is sensitive to data loss on temporary queues or topics, don't use temporary queues or topics. Use durable queues, topics, and subscriptions instead.

Data migration

As part of migrating and modifying your client applications to interact with Azure Service Bus, the data held in ActiveMQ isn't migrated to Service Bus. You might need a custom application to drain the ActiveMQ queues, topics, and subscriptions, and then replay the messages to the queues, topics, and subscriptions of Service Bus.

Authentication and authorization

Azure role-based access control (Azure RBAC), backed by Microsoft Entra ID, is the preferred authentication mechanism for Service Bus. To enable role-based access control, please follow the steps in the Azure Service Bus JMS 2.0 developer guide.

Pre-migration

Version check

You use the following components and versions while you're writing the JMS applications:

Component Version
Java Message Service (JMS) API 1.1 or greater
AMQP protocol 1.0

Ensure that AMQP ports are open

Service Bus supports communication over the AMQP protocol. For this purpose, enable communication over ports 5671 (AMQP) and 443 (TCP). Depending on where the client applications are hosted, you might need a support ticket to allow communication over these ports.

Important

Service Bus supports only AMQP 1.0 protocol.

Set up enterprise configurations

Service Bus enables various enterprise security and high availability features. For more information, see:

Monitoring, alerts and tracing

For each Service Bus namespace, you publish metrics onto Azure Monitor. You can use these metrics for alerting and dynamic scaling of resources allocated to the namespace.

For more information about the different metrics and how to set up alerts on them, see Service Bus metrics in Azure Monitor. You can also find out more about client side tracing for data operations and operational/diagnostic logging for management operations.

Metrics - New Relic

You can correlate which metrics from ActiveMQ map to which metrics in Azure Service Bus. See the following from the New Relic website:

Note

Currently, New Relic doesn't have direct, seamless integration with ActiveMQ, but they do have metrics available for Amazon MQ. Because Amazon MQ is derived from ActiveMQ, the following table maps the New Relic metrics from Amazon MQ to Azure Service Bus.

Metric grouping Amazon MQ/ActiveMQ metric Azure Service Bus metric
Broker CpuUtilization CPUXNS
Broker MemoryUsage WSXNS
Broker CurrentConnectionsCount activeConnections
Broker EstablishedConnectionsCount activeConnections + connectionsClosed
Broker InactiveDurableTopicSubscribersCount Use subscription metrics
Broker TotalMessageCount Use queue/topic/subscription level activeMessages
Queue/Topic EnqueueCount incomingMessages
Queue/Topic DequeueCount outgoingMessages
Queue QueueSize sizeBytes

Migration

To migrate your existing JMS 2.0 application to interact with Service Bus, follow the steps in the next several sections.

Export the topology from ActiveMQ and create the entities in Service Bus (optional)

To ensure that client applications can seamlessly connect with Service Bus, migrate the topology (including queues, topics, and subscriptions) from Apache ActiveMQ to Service Bus.

Note

For JMS applications, you create queues, topics, and subscriptions as a runtime operation. Most JMS providers (message brokers) give you the ability to create these at runtime. That's why this export step is considered optional. To ensure that your application has the permissions to create the topology at runtime, use the connection string with SAS Manage permissions.

To do this:

  1. Use the ActiveMQ command line tools to export the topology.
  2. Re-create the same topology by using an Azure Resource Manager template.
  3. Run the Azure Resource Manager template.

Import the maven dependency for Service Bus JMS implementation

To ensure seamless connectivity with Service Bus, add the azure-servicebus-jms package as a dependency to the Maven pom.xml file, as follows:

<dependencies>
...
    <dependency>
        <groupId>com.microsoft.azure</groupId>
        <artifactId>azure-servicebus-jms</artifactId>
    </dependency>
...
</dependencies>

Application server configuration changes

This part is customized to the application server that is hosting your client applications connecting to ActiveMQ.

Spring applications

Update the application.properties file

If you're using a Spring boot application to connect to ActiveMQ, you want to remove the ActiveMQ-specific properties from the application.properties file.

spring.activemq.broker-url=<ACTIVEMQ BROKER URL>
spring.activemq.user=<ACTIVEMQ USERNAME>
spring.activemq.password=<ACTIVEMQ PASSWORD>

Then, add the Service Bus-specific properties to the application.properties file.

azure.servicebus.connection-string=Endpoint=myEndpoint;SharedAccessKeyName=mySharedAccessKeyName;SharedAccessKey=mySharedAccessKey
Replace ActiveMQConnectionFactory with ServiceBusJmsConnectionFactory

The next step is to replace the instance of ActiveMQConnectionFactory with the ServiceBusJmsConnectionFactory.

Note

The actual code changes are specific to the application and how dependencies are managed, but the following sample provides the guidance on what should be changed.

Previously, you might have been instantiating an object of ActiveMQConnectionFactory, as follows:


String BROKER_URL = "<URL of the hosted ActiveMQ broker>";
ConnectionFactory factory = new ActiveMQConnectionFactory(BROKER_URL);

Connection connection = factory.createConnection();
connection.start();

Now, you're changing this to instantiate an object of ServiceBusJmsConnectionFactory, as follows:


ServiceBusJmsConnectionFactorySettings settings = new ServiceBusJmsConnectionFactorySettings();
String SERVICE_BUS_CONNECTION_STRING = "<Service Bus Connection string>";

ConnectionFactory factory = new ServiceBusJmsConnectionFactory(SERVICE_BUS_CONNECTION_STRING, settings);

Connection connection = factory.createConnection();
connection.start();

Post-migration

Now that you have modified the application to start sending and receiving messages from Service Bus, you should verify that it works as you expect. When that's done, you can proceed to further refine and modernize your application stack.

Next steps

To learn more about Service Bus messaging and JMS, see: