Starting and Managing ActiveMQ
To start the ActiveMQ broker, execute:
activemq start
To stop it:
activemq stop
Access the web console at http://localhost:8161 using credentials admin/admin.
Message Models: Queue vs Topic
Queue (Point-to-Point)
In a queue model, each message is consumed by exactly one consumer. Messages are persisted to disk (default: %ACTIVEMQ_HOME%/data/kahadb) until successfully processed. If no consumer is available when a message is sent, it remains queued. With multiple consumers, messages are distributed in a load-balanced manner — once consumed by one, they are removed from the queue.
Topic (Publish-Subscribe)
Topics broadcast messages to all active subscribers. Two subscription types exist:
- Non-durable: Subscribers must be active to receive messages. Offline subscribers miss messages.
- Durable: Subscribers retain subscriptions even when offline. Messages are stored until delivered upon reconnection.
By default, subscriptions are non-durable. Durable subscriptions behave similarly to individual persistent queues per subscriber.
Spring Boot Integration
Configuration Beans
Define JMS templates and listener containers for both queue and topic domains:
package com.example.activemq.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.core.JmsTemplate;
import javax.jms.ConnectionFactory;
@Configuration
@EnableJms
public class JmsConfig {
public static final String QUEUE_NAME = "app.queue.orders";
public static final String TOPIC_NAME = "app.topic.notifications";
@Bean("queueTemplate")
public JmsTemplate queueTemplate(ConnectionFactory connectionFactory) {
JmsTemplate template = new JmsTemplate(connectionFactory);
template.setDefaultDestinationName(QUEUE_NAME);
return template;
}
@Bean("queueListenerContainer")
public DefaultJmsListenerContainerFactory queueContainerFactory(ConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setSessionTransacted(true);
factory.setConcurrency("1-3");
factory.setRecoveryInterval(2000L);
return factory;
}
@Bean("topicTemplate")
public JmsTemplate topicTemplate(ConnectionFactory connectionFactory) {
JmsTemplate template = new JmsTemplate(connectionFactory);
template.setDefaultDestinationName(TOPIC_NAME);
template.setPubSubDomain(true);
return template;
}
@Bean("topicListenerContainer")
public DefaultJmsListenerContainerFactory topicContainerFactory(ConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setSessionTransacted(true);
factory.setConcurrency("1-3");
factory.setRecoveryInterval(2000L);
factory.setPubSubDomain(true);
return factory;
}
}
Connection Factory Configuration
Configure broker connection via application.yml:
spring:
activemq:
broker-url: tcp://localhost:61616
user: admin
password: admin
Or programmatically:
@Bean
public ConnectionFactory connectionFactory() {
return new ActiveMQConnectionFactory("admin", "admin", "tcp://localhost:61616");
}
Message Listeners
Queue lisetners — only one will receive each message:
package com.example.activemq.listener;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
@Component
public class OrderQueueListener1 {
@JmsListener(destination = "app.queue.orders", containerFactory = "queueListenerContainer")
public void processOrder(String payload) {
System.out.println("Listener 1 received: " + payload);
}
}
@Component
public class OrderQueueListener2 {
@JmsListener(destination = "app.queue.orders", containerFactory = "queueListenerContainer")
public void processOrder(String payload) {
System.out.println("Listener 2 received: " + payload);
}
}
Topic listeners — all receive every message:
@Component
public class NotificationTopicListenerA {
@JmsListener(destination = "app.topic.notifications", containerFactory = "topicListenerContainer")
public void handleNotification(String payload) {
System.out.println("Topic A: " + payload);
}
}
@Component
public class NotificationTopicListenerB {
@JmsListener(destination = "app.topic.notifications", containerFactory = "topicListenerContainer")
public void handleNotification(String payload) {
System.out.println("Topic B: " + payload);
}
}
@Component
public class NotificationTopicListenerC {
@JmsListener(destination = "app.topic.notifications", containerFactory = "topicListenerContainer")
public void handleNotification(String payload) {
System.out.println("Topic C: " + payload);
}
}
Message Producers
Expose REST endpoints to send messages:
package com.example.activemq;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalTime;
import java.util.HashMap;
import java.util.Map;
@SpringBootApplication
@RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Autowired
@Qualifier("queueTemplate")
private JmsTemplate queueTemplate;
@Autowired
@Qualifier("topicTemplate")
private JmsTemplate topicTemplate;
@GetMapping("/send/queue")
public Map<String, String> sendToQueue() {
String message = "Queue message at " + LocalTime.now();
queueTemplate.convertAndSend(message);
Map<String, String> response = new HashMap<>();
response.put("sent", message);
return response;
}
@GetMapping("/send/topic")
public Map<String, String> sendToTopic() {
String message = "Topic message at " + LocalTime.now();
topicTemplate.convertAndSend(message);
Map<String, String> response = new HashMap<>();
response.put("sent", message);
return response;
}
}
Behavior Verification
When sending to /send/queue, only one of the two queue listeners logs the message — demonstrating exclusive consumption.
When sending to /send/topic, all three topic listeners receive and log the message simultaneously — confirming broadcast delivery.