Cannot publish Topic-message to two subscriber simlutaneously using ActiveMQ - spring-boot

I have referred to the SpringBoot application publish and read from ActiveMQ topic to, publish a Topic using ActiveMQ. I have created two receiver micro-services which reads message from the topic.I have also created rest endpoint to publish the Topic.However, I have to execute this rest end point two times to publish the message for two receivers 1). The first execution of rest endpoint will send message to Receiver1
2). The second execution of rest endpoint will send message to Receiver2
Hence 2 receivers could not read from the Topic simultaneously.
Here is my code.
PublisherApplication.java
package com.springboot;
//import statements
#SpringBootApplication
#EnableDiscoveryClient
#EnableJms
public class PublisherApplication {
#Bean
public JmsListenerContainerFactory<?> topicListenerFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
// This provides all boot's default to this factory, including the message converter
configurer.configure(factory, connectionFactory);
//setPubSubDomain identifies Topic in ActiveMQ
factory.setPubSubDomain(true);
return factory;
}
public static void main(String[] args) {
SpringApplication.run(PublisherApplication.class, args);
}
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder.build();
}
}
PublishMessage.java
[Rest-end point to publish Topic]
package com.springboot.controller;
//import statements
#RestController
#RequestMapping(path = "/schoolDashboard/topic")
class PublishMessage {
public static final String MAILBOX_TOPIC = "mailbox.topic";
#Autowired
private JmsTemplate jmsTemplate;
#GetMapping(path = "/sendEmail")
public void sendStudentById() throws Exception{
System.out.println("Anindya-TopicSendMessage.java :: Publishing Email sent....");
jmsTemplate.convertAndSend(MAILBOX_TOPIC, "Topic - Email Sent");
}
}
ReceiverApplication01
[Note - Receiver01 is first microservice]
package com.springboot;
//import statements
#SpringBootApplication
#EnableJms
public class ReceiverApplication01 {
#Bean
public JmsListenerContainerFactory<?> topicListenerFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
// This provides all boot's default to this factory, including the message converter
configurer.configure(factory, connectionFactory);
//setPubSubDomain identifies Topic in ActiveMQ
factory.setPubSubDomain(true);
return factory;
}
public static void main(String[] args) {
SpringApplication.run(ReceiverApplication01.class, args);
}
}
TopicMesssgeReceiver01.java
[Receiver01 Read message from Topic]
package com.springboot.message;
//import statement
#Component
public class TopicMesssgeReceiver01 {
private final SimpleMessageConverter converter = new SimpleMessageConverter();
public static final String MAILBOX_TOPIC = "mailbox.topic";
#JmsListener(destination = MAILBOX_TOPIC, containerFactory = "topicListenerFactory")
public void receiveMessage(final Message message) throws JMSException{
System.out.println("Receiver01 <" + String.valueOf(this.converter.fromMessage(message)) + ">");
}
}
ReceiverApplication02
[Note:-Receiver02 is second microservice]
package com.springboot;
//import statement
#SpringBootApplication
#EnableJms
public class ReaderApplication02 {
#Bean
public JmsListenerContainerFactory<?> topicListenerFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setPubSubDomain(true);
configurer.configure(factory, connectionFactory);
return factory;
}
public static void main(String[] args) {
SpringApplication.run(ReaderApplication02.class, args);
}
}
TopicMesssgeReceiver02
[Receiver02 Read message from Topic]
package com.springboot.message;
//import statement
#Component
public class TopicMesssgeReceiver02 {
private final SimpleMessageConverter converter = new SimpleMessageConverter();
public static final String MAILBOX_TOPIC = "mailbox.topic";
#JmsListener(destination = MAILBOX_TOPIC, containerFactory = "topicListenerFactory")
public void receiveMessage(final Message message) throws Exception{
System.out.println("Receiver02 <" + String.valueOf(this.converter.fromMessage(message)) + ">");
}
}

Thanks Naveen!! Finally, I am able to do it.
We have to set only setPubSubDomain(true); and spring-boot will take care all boiler-plate code. Now, two receiver Microservices can read message from Topic simultaneously Following are the code changes
PublishMessage.java
[Rest-end point to publish Topic]
package com.springboot.controller;
//import statements
#RestController
#RequestMapping(path = "/schoolDashboard/topic")
class PublishMessage {
public static final String MAILBOX_TOPIC = "mailbox.topic";
#Autowired
private JmsTemplate jmsTemplate;
#GetMapping(path = "/sendEmail")
public void sendStudentById() throws Exception{
System.out.println("Publisher :: Message sent...");
/* Added this statement. setPubSubDomain(true) identifies Topic in ActiveMQ */
jmsTemplate.setPubSubDomain(true);
jmsTemplate.convertAndSend(MAILBOX_TOPIC, "Topic - Email Sent");
}
}
ReceiverApplication02
[Note:-Receiver02 is second microservice]
package com.springboot;
//import statement
#SpringBootApplication
#EnableJms
public class ReaderApplication02 {
#Bean
public JmsListenerContainerFactory<?> topicListenerFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory);
/* setPubSubDomain(true) should be placed after
* configuration of the specified jms listener container factory*/
factory.setPubSubDomain(true);
return factory;
}
public static void main(String[] args) {
SpringApplication.run(ReaderApplication02.class, args);
}
}

Related

Redelivery Policy Is Not Working in Activemq Spring Boot

I have used below configuration
#SpringBootApplication
#EnableScheduling
public class NotificationApplication {
#Value("${jms.broker.endpoint}")
private String brokerUrl;
#Autowired
private Environment env;
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(NotificationApplication.class, args);
RecordReader abc = ctx.getBean(RecordReader.class);
abc.readNotifications();
}
#Bean
public ActiveMQConnectionFactory activeMQConnectionFactory() {
ActiveMQConnectionFactory factory = new ActiveMQConnectionFactory(brokerUrl);
RedeliveryPolicy redeliveryPolicy = new RedeliveryPolicy();
redeliveryPolicy.setMaximumRedeliveries(env.getProperty("jms.redelivery.maximum", Integer.class));
factory.setRedeliveryPolicy(redeliveryPolicy);
factory.setTrustedPackages(Arrays.asList("com.lms.notification"));
return factory;
}
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory() throws Throwable {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(activeMQConnectionFactory());
factory.setMessageConverter(jacksonJmsMessageConverter());
factory.setConcurrency(env.getProperty("jms.connections.concurrent"));
return factory;
}
#Bean
public JmsTemplate jmsTemplate() {
JmsTemplate template = new JmsTemplate();
template.setConnectionFactory(activeMQConnectionFactory());
template.setMessageConverter(jacksonJmsMessageConverter());
return template;
}
#Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
}
The issue is the Redelivery policy is not working as I have defined in activeMQConnectionFactory bean. Means I have set maximum redelivery 1, but is not being redelivered in case of exception in listener. Also in case of exception in listener it should go to the default DLQ, which is also not happening. But if I comment the jmsListenerContainerFactory bean all works fine.
I am not able to identify why this is happening. Can any one look into this what wrong I am doing?
I am using Activemq 5.16.1
Thanks

IBM-MQ Create MQ Destination

Using spring boot and IBM MQ i need to send message to MQ.
In My Spring boot application i have registered MQQueueConnectionFactory as below.
#SpringBootApplication
#EnableJms
public class MainApplication {
public static void main(String[] args) {
new SpringApplicationBuilder(MainApplication.class).web(WebApplicationType.NONE).run(args);
logger.info("init completed...");
}
#Bean
public MQQueueConnectionFactory queueConnectionFactory() {
MQQueueConnectionFactory queueConnectionFactory = new MQQueueConnectionFactory();
try {
queueConnectionFactory.setTransportType(WMQConstants.WMQ_CM_CLIENT);
queueConnectionFactory.setHostName(host);
queueConnectionFactory.setChannel(channel);
queueConnectionFactory.setPort(port);
queueConnectionFactory.setQueueManager(queueManager);
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
return queueConnectionFactory;
}
}
And i have destination resolver as below.
#Component
public class IBMWebSphereMqDestinationResolver extends
DynamicDestinationResolver implements DestinationResolver {
#Override
public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain) throws JMSException {
Destination destination = super.resolveDestinationName(session, destinationName, pubSubDomain);
if (destination instanceof MQDestination) {
MQDestination mqDestination = (MQDestination) destination;
}
return destination;
}
}
I am using JmsTemplate to send message to MQ.
#Service
public class MqServiceImpl implements MqService {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
private JmsTemplate jmsTemplate;
#Autowired
private MQDestination destination;
#Handler
#Override
public void sendMessage(String textMessage) {
logger.info("textMessage {} ", textMessage);
logger.info("destination {} ", destination);
jmsTemplate.convertAndSend(destination, textMessage);
}
}
However when i try to start the application i am getting MQDestination' that could not be found.
service.impl.MqServiceImpl required a bean of type 'com.ibm.mq.jms.MQDestination' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.ibm.mq.jms.MQDestination' in your configuration.
Shouldn't spring try to call DestinationResolver ?
If not how do i register destination for IBM MQ?
You are defining an DestinationResolver bean, but injecting an MQDestination bean. This is why you get the error. You should inject the DestinationResolver and call setDestinationResolver on the JmsTemplate.
But the JmsTemplate resolves the destinations dynamically. This works as well:
public void sendMessage(String textMessage) {
String destination = "MY.QUEUE";
jmsTemplate.convertAndSend(destination, textMessage);
}

Message conversion after retrieving it from activemq topic

I wanted to implement messaging between microservices using ActiveMQ as a broker, but after setting everyting as should be, one thing got me stuck. Before I describe the problem, here is I am approaching messaging:
Producer config:
#Slf4j
#Configuration
#EnableJms
public class JmsConfig {
public static final String EBAY_TOPIC = "ebay.topic";
#Bean
public ActiveMQTopic destinationTopic() {
return new ActiveMQTopic(EBAY_TOPIC);
}
#Bean
public JmsListenerContainerFactory<?> connectionFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory);
factory.setPubSubDomain(true);
return factory;
}
#Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
#Bean
public JmsTemplate jmsTemplate(ConnectionFactory connectionFactory, MessageConverter messageConverter) {
JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
jmsTemplate.setDefaultDestinationName(EBAY_TOPIC);
jmsTemplate.setConnectionFactory(connectionFactory);
jmsTemplate.setMessageConverter(messageConverter);
jmsTemplate.setPubSubDomain(true);
return jmsTemplate;
}
}
Producer sending message:
#RequiredArgsConstructor
#Slf4j
#RestController
#RequestMapping("/message")
public class IntercommunicationController {
private final JmsTemplate jmsTemplate;
#PostMapping("/send")
public void sendMessageToOtherService(#RequestBody Message message) {
jmsTemplate.convertAndSend(JmsConfig.EBAY_TOPIC, message);
}
}
Receiver config:
#Configuration
#EnableJms
public class JmsConfig {
public static final String EBAY_TOPIC = "ebay.topic";
#Bean
public JmsListenerContainerFactory<?> connectionFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer,
ErrorHandler errorHandler) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory);
factory.setErrorHandler(errorHandler);
factory.setPubSubDomain(true);
return factory;
}
#Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
}
Receiver implementation:
#Slf4j
#Component
public class MessageListener {
#JmsListener(destination = JmsConfig.EBAY_TOPIC, containerFactory = "connectionFactory")
public void receiveMessage(#Payload Message receivedMessage) {
log.info("Got message saying {}", receivedMessage);
}
}
POJO which is being sent:
#NoArgsConstructor
#Data
public class Message {
String content;
String from;
}
Problem:
I'm getting error once I try to POST object through json and send it on topic. Once message get to the topic, the consumer can't handle deserialization of the message as in TypeIdPropertyName is different package path of the object which I'm sending to the one I'm trying to receive, they are 1:1 in both applications, but I'm getting:
2018-03-20 16:32:08.601 ERROR 1568 --- [enerContainer-1] c.g.g.s.c.config.MessageErrorHandler : Listener method 'public void com.gft.graduate2018.sabb.client.listener.MessageListener.receiveMessage(client.domain.Message)' threw exception; nested exception is org.springframework.jms.support.converter.MessageConversionException: Failed to resolve type id [backend.model.dtos.Message]; nested exception is java.lang.ClassNotFoundException: backend.model.dtos.Message
What's the proper way of addressing that problem ? I could end up writing custom parser from string to object but probably that would not be the best solution. Hopefully there is someone who dealt with it before and can help resolve that problem :)
See...
/**
* Specify mappings from type ids to Java classes, if desired.
* This allows for synthetic ids in the type id message property,
* instead of transferring Java class names.
* <p>Default is no custom mappings, i.e. transferring raw Java class names.
* #param typeIdMappings a Map with type id values as keys and Java classes as values
*/
public void setTypeIdMappings(Map<String, Class<?>> typeIdMappings) {
...
}
...on the converter.
Set the mappings on both sides, so the __type header just contains a token that's mapped from the class name on the sending side and to the class name on the receiving side.

Springboot RabbitMq Consumer consuming only at first time.please help me to get the configurations right and how can configure multiple listners

Springboot RabbitMq Consumer consuming only at first time.please help me to get the configurations right and how can configure multiple listners.I am developing an Automated Mapping solution for which i Execute various jobs .and in a need to put those jobs in queue
Here's my application class:Application.java
#SpringBootApplication
#ComponentScan(basePackages = { "com.fractal.sago", "com.fractal.grpc" })
public class Application extends SpringBootServletInitializer {
public static void main(String[] args) throws Exception {
SpringApplication.run(Application.class, args);
}
RabbitMqConfig class:
#Configuration
public class RabbitMqConfig {
public final static String JOB_QUEUE_NAME = "jobQueue";
public final static String JOB_EXCHANGE_NAME = "jobExchange";
#Bean
Queue jobQueue() {
return new Queue(JOB_QUEUE_NAME, true);
}
#Bean
DirectExchange jobExchange() {
return new DirectExchange(JOB_EXCHANGE_NAME);
}
#Bean
Binding jobBinding(DirectExchange directExchange) {
return BindingBuilder.bind(jobQueue()).to(jobExchange()).with(jobQueue().getName());
}
#Bean
SimpleMessageListenerContainer jobQueueContainer(ConnectionFactory connectionFactory,
MessageListenerAdapter joblistenerAdapter) {
SimpleMessageListenerContainer jobQueueContainer = new SimpleMessageListenerContainer();
jobQueueContainer.setConnectionFactory(connectionFactory);
jobQueueContainer.setQueueNames(JOB_QUEUE_NAME);
jobQueueContainer.setMessageListener(joblistenerAdapter);
return jobQueueContainer;
}
#Bean
MessageListenerAdapter joblistenerAdapter(JobQueueConsumer messageReceiver) {
MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(messageReceiver, "receiveMessage");
messageListenerAdapter.setMessageConverter(producerJackson2MessageConverter());
return messageListenerAdapter;
}
#Bean
public Jackson2JsonMessageConverter producerJackson2MessageConverter() {
return new Jackson2JsonMessageConverter();
}
}
producer : JobProducer
#Component
public class JobQueueProducer {
#Autowired
RabbitTemplate rabbitTemplate;
public void sendMessage(String message) {
Message messageToSend = MessageBuilder.withBody(message.getBytes())
.setDeliveryMode(MessageDeliveryMode.PERSISTENT).build();
rabbitTemplate.convertAndSend(RabbitMqConfig.JOB_EXCHANGE_NAME, RabbitMqConfig.JOB_QUEUE_NAME, messageToSend);
//rabbitTemplate.convertAndSend(RabbitMqConfig.JOB_QUEUE_NAME, messageToSend);
}
}
Consumer :JobQueueConsumer
enter code here
#Component
public class JobQueueConsumer implements MessageListener {
#Autowired
SagoAlgo sagoAlgo;
#Autowired
CCDMappingService ccdMappingService;
#RabbitListener(queues = { RabbitMqConfig.JOB_QUEUE_NAME })
public void receiveMessage(Message message) throws SQLException {
System.out.println("Received Message: " + new String(message.getBody()));
Integer jobId = Integer.parseInt(new String(message.getBody()));
System.out.println(jobId);
CCDMappingVO ccdVo =ccdMappingService.fetchCCDWithCategoriesById(jobId);
sagoAlgo.execAlgo(ccdVo); //my Algo to be executed
}
//when I implement MessageListener default method to be executed
public void onMessage(Message message) {
// System.out.println("Received Message: " + new
// String(message.getBody()));
}
}

spring boot configure multiple ActiveMQ instances

I have requirement to move messages from queues on one ActiveMQ instance to another ActiveMQ instance. Is there a way to connect to two different ActiveMQ instances using spring boot configuration?
Do I need to create multiple connectionFactories? If so then how does JmsTemplate know which ActiveMQ instance to connect to?
#Bean
public ConnectionFactory connectionFactory() {
return new ActiveMQConnectionFactory(JMS_BROKER_URL);
}
Any help and code examples would be useful.
Thanks in advance.
GM
Additionally to the response of #Chris
You have to create different BrokerService instances using differents ports and create different ConnectionFactory to connect to each broker and create different JmsTemplate using these different factories to send messages to differents brokers.
For example :
import javax.jms.ConnectionFactory;
import javax.jms.QueueConnectionFactory;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.broker.BrokerService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jms.DefaultJmsListenerContainerFactoryConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.core.JmsTemplate;
#Configuration
public class ActiveMQConfigurationForJmsCamelRouteConsumeAndForward {
public static final String LOCAL_Q = "localQ";
public static final String REMOTE_Q = "remoteQ";
#Bean
public BrokerService broker() throws Exception {
final BrokerService broker = new BrokerService();
broker.addConnector("tcp://localhost:5671");
broker.setBrokerName("broker");
broker.setUseJmx(false);
return broker;
}
#Bean
public BrokerService broker2() throws Exception {
final BrokerService broker = new BrokerService();
broker.addConnector("tcp://localhost:5672");
broker.setBrokerName("broker2");
broker.setUseJmx(false);
return broker;
}
#Bean
#Primary
public ConnectionFactory jmsConnectionFactory() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:5671");
return connectionFactory;
}
#Bean
public QueueConnectionFactory jmsConnectionFactory2() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:5672");
return connectionFactory;
}
#Bean
#Primary
public JmsTemplate jmsTemplate() {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(jmsConnectionFactory());
jmsTemplate.setDefaultDestinationName(LOCAL_Q);
return jmsTemplate;
}
#Bean
public JmsTemplate jmsTemplate2() {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setConnectionFactory(jmsConnectionFactory2());
jmsTemplate.setDefaultDestinationName(REMOTE_Q);
return jmsTemplate;
}
#Bean
public JmsListenerContainerFactory<?> jmsListenerContainerFactory(ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory);
return factory;
}
#Bean
public JmsListenerContainerFactory<?> jmsListenerContainerFactory2(
#Qualifier("jmsConnectionFactory2") ConnectionFactory connectionFactory,
DefaultJmsListenerContainerFactoryConfigurer configurer) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
configurer.configure(factory, connectionFactory);
return factory;
}
}
To move messages from one AMQ instance to another instance you can use JmsBridgeConnectors :
Note that by the example below you cannot have multiple consumers on the queue from which you want to forward the messages because Camel or JmsBridgeConnectors consume the message and forward it. If you want a only copy of the message to be forwarded you have some solutions :
1- Convert your queue to a topic, manage the messages for offline consumers by a durable subscriptions or retroactive consumers.
2- convert your queue to a composite queue and use DestinationsInterceptors to copy messages to another queue.
3- use NetworkConnector for Networkof brokers
#Bean
public BrokerService broker() throws Exception {
final BrokerService broker = new BrokerService();
broker.addConnector("tcp://localhost:5671");
SimpleJmsQueueConnector simpleJmsQueueConnector = new SimpleJmsQueueConnector();
OutboundQueueBridge bridge = new OutboundQueueBridge();
bridge.setLocalQueueName(LOCAL_Q);
bridge.setOutboundQueueName(REMOTE_Q);
OutboundQueueBridge[] outboundQueueBridges = new OutboundQueueBridge[] { bridge };
simpleJmsQueueConnector.getReconnectionPolicy().setMaxSendRetries(ReconnectionPolicy.INFINITE);
simpleJmsQueueConnector.setOutboundQueueBridges(outboundQueueBridges);
simpleJmsQueueConnector.setLocalQueueConnectionFactory((QueueConnectionFactory) jmsConnectionFactory());
simpleJmsQueueConnector.setOutboundQueueConnectionFactory(jmsConnectionFactory2());
JmsConnector[] jmsConnectors = new JmsConnector[] { simpleJmsQueueConnector };
broker.setJmsBridgeConnectors(jmsConnectors);
broker.setBrokerName("broker");
broker.setUseJmx(false);
return broker;
}
Or by using Camel like this below :
#Bean
public CamelContext camelContext() throws Exception {
CamelContext context = new DefaultCamelContext();
context.addComponent("inboundQueue", ActiveMQComponent.activeMQComponent("tcp://localhost:5671"));
context.addComponent("outboundQueue", ActiveMQComponent.activeMQComponent("tcp://localhost:5672"));
context.addRoutes(new RouteBuilder() {
public void configure() {
from("inboundQueue:queue:" + LOCAL_Q).to("outboundQueue:queue:" + REMOTE_Q);
}
});
context.start();
return context;
}
your Producer must be like this to use differents JmsTemplates :
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Component;
#Component
public class Producer implements CommandLineRunner {
#Autowired
private JmsTemplate jmsTemplate;
#Autowired
#Qualifier("jmsTemplate2")
private JmsTemplate jmsTemplate2;
#Override
public void run(String... args) throws Exception {
send("Sample message");
}
public void send(String msg) {
this.jmsTemplate.convertAndSend(ActiveMQConfigurationForJmsCamelRouteConsumeAndForward.LOCAL_Q, msg);
this.jmsTemplate2.convertAndSend(ActiveMQConfigurationForJmsCamelRouteConsumeAndForward.REMOTE_Q, msg);
}
}
and Consumer :
import javax.jms.Session;
import org.apache.activemq.ActiveMQSession;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
#Component
public class Consumer {
#JmsListener(destination = ActiveMQConfigurationForJmsCamelRouteConsumeAndForward.REMOTE_Q, containerFactory = "jmsListenerContainerFactory2")
public void receiveQueue(Session session, String text) {
System.out.println(((ActiveMQSession) session).getConnection().getBrokerInfo());
System.out.println(text);
}
}
You would need to instantiate multiple JmsTemplate instances as Beans in your application and then use a combination of #Qualifier and #Primary annotations to indicate which JmsTemplate instance should go where.
For example
#Bean("queue1")
#Primary
public JmsTemplate getQueue1(#Qualifier("connectionFactory1")ConnectionFactory factory...){
...
}
#Bean("queue2")
#Primary
public JmsTemplate getQueue2(#Qualifier("connectionFactory2")ConnectionFactory factory...){
...
}
...
#Autowired
#Qualifier("queue1")
private JmsTemplate queue1;
...
See here for more info.
You can use the Spring Boot default for the queue consumer
#JmsListener(destination = “queue.name")
public void consumer(String message) {
// consume the message
}
And for the producer you can create another JmsTemplate #Bean
#Bean
public JmsTemplate jmsTemplate() {
return new JmsTemplate(new ActiveMQConnectionFactory("tcp://localhost:5671"));
}
This way you can register as many brokers/listeners as you wish dynamically:
import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.JmsListenerConfigurer;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerEndpointRegistrar;
import org.springframework.jms.config.SimpleJmsListenerEndpoint;
import javax.jms.Message;
import javax.jms.MessageListener;
#Configuration
public class CustomJmsConfigurer implements JmsListenerConfigurer {
#Override
public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
ActiveMQConnectionFactory amqConnectionFactory = new ActiveMQConnectionFactory();
amqConnectionFactory.setBrokerURL("brokerUrl");
amqConnectionFactory.setUserName("user");
amqConnectionFactory.setPassword("password");
amqConnectionFactory.setExclusiveConsumer(true);
DefaultJmsListenerContainerFactory containerFactory = new DefaultJmsListenerContainerFactory();
containerFactory.setConnectionFactory(amqConnectionFactory);
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
endpoint.setId("someIdentifier");
endpoint.setDestination("queueName");
endpoint.setMessageListener(new MessageListener() {
#Override
public void onMessage(Message message) {
// Do your stuff
}
});
registrar.registerEndpoint(endpoint, containerFactory);
}
}

Resources