MQTT and Spring Boot Integration - When Connection lost - spring-boot

I am trying to using Spring MQTT Integration to build a client that is subscribe to MQTT broker. The code works as expected, no issues. I am struggling configuring it so that when the connection is lost, it subscribes automatically. What is happening now, is that when it disconnects, the connection is established but no is not subscribed anymore to my topic.
What should I do to capture the event correctly, and resubscribe again when connection is lost?
Here is my configuration
import org.eclipse.paho.client.mqttv3.MqttConnectOptions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.core.MessageProducer;
import org.springframework.integration.mqtt.core.ConsumerStopAction;
import org.springframework.integration.mqtt.core.DefaultMqttPahoClientFactory;
import org.springframework.integration.mqtt.core.MqttPahoClientFactory;
import org.springframework.integration.mqtt.inbound.MqttPahoMessageDrivenChannelAdapter;
import org.springframework.integration.mqtt.outbound.MqttPahoMessageHandler;
import org.springframework.integration.mqtt.support.DefaultPahoMessageConverter;
import org.springframework.integration.mqtt.support.MqttHeaders;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.MessagingException;
#Configuration
public class MqttBeans {
Logger logger = LoggerFactory.getLogger(MqttBeans.class);
#Bean
public MqttConnectOptions mqttConnectOptions() {
MqttConnectOptions options = new MqttConnectOptions();
options.setServerURIs(new String[] { "ssl://URL:8883" });
options.setUserName("ubidot_bridge");
String pass = "PASS";
options.setPassword(pass.toCharArray());
options.setCleanSession(false);
options.setAutomaticReconnect(true);
options.setConnectionTimeout(30);
options.setKeepAliveInterval(90);
options.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1_1);
return options;
}
#Bean
public MqttPahoClientFactory mqttClientFactory(MqttConnectOptions options) {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
factory.setConnectionOptions( options );
factory.setConsumerStopAction(ConsumerStopAction.UNSUBSCRIBE_NEVER);
logger.info("Reconnected to the broker");
return factory;
}
#Bean
public MessageChannel mqttInputChannel() {
return new DirectChannel();
}
#Bean
public MqttPahoMessageDrivenChannelAdapter mqttPahoMessageDrivenChannelAdapterConfig(MqttConnectOptions options) {
MqttPahoMessageDrivenChannelAdapter adapter = new MqttPahoMessageDrivenChannelAdapter("ubidot_bridge_in",
mqttClientFactory(options), "#");
adapter.setCompletionTimeout(5000);
adapter.setConverter(new DefaultPahoMessageConverter());
adapter.setQos(2);
adapter.setOutputChannel(mqttInputChannel());
logger.info("Setting up inbound channel");
return adapter;
}
#Bean
public MessageProducer inbound(MqttPahoMessageDrivenChannelAdapter adapter) {
return adapter;
}
#Bean
#ServiceActivator(inputChannel = "mqttInputChannel")
public MessageHandler handler() {
logger.info("Setting up msg receiver handler");
return new MessageHandler() {
#Override
public void handleMessage(Message<?> message) throws MessagingException {
String topic = message.getHeaders().get(MqttHeaders.RECEIVED_TOPIC).toString();
logger.info("Msg received .. Topic: " + topic);
logger.info("Payload " + message.getPayload());
System.out.println();
}
};
}
#Bean
public MessageChannel mqttOutboundChannel() {
return new DirectChannel();
}
#Bean
#ServiceActivator(inputChannel = "mqttOutboundChannel")
public MessageHandler mqttOutbound( MqttConnectOptions options ) {
// clientId is generated using a random number
MqttPahoMessageHandler messageHandler = new MqttPahoMessageHandler("ubidot_bridge_out", mqttClientFactory(options));
messageHandler.setAsync(true);
messageHandler.setDefaultTopic("#");
messageHandler.setDefaultRetained(false);
return messageHandler;
}
}
Thank you in advance for the help
T.

Related

How to listen to multiple queues simultaneously on same queue manager?

I want to listen to multiple (2) queues on the same queue manager from my spring boot (kotlin) app. I have the following MQ connection factory but not sure how to add the listeners for my 2 queues.
#Configuration
open class MqConfig {
#Value("\${jms.mq.port}")
private var port: Int = 0
#Value("\${jms.mq.channel}")
private var channel: String? = null
#Value("\${jms.mq.host}")
private var host: String? = null
#Value("\${jms.mq.manager}")
private var queueManager: String? = null
#Value("\${jms.mqA.queue}")
private var jmsMqA: String? = null
#Value("\${jms.mqB.queue}")
private var jmsMqB: String? = null
#Bean
open fun jmsMQConnectionFactory(): MQQueueConnectionFactory {
val mqQueueConnectionFactory = MQQueueConnectionFactory()
mqQueueConnectionFactory.port = port
mqQueueConnectionFactory.channel = channel
mqQueueConnectionFactory.hostName = host
mqQueueConnectionFactory.queueManager = queueManager
mqQueueConnectionFactory.transportType = WMQConstants.WMQ_CM_CLIENT;
mqQueueConnectionFactory.sslSocketFactory = sslSocketfactory;
}
As you can see in the code above, the two queues I want to listen to are jmsMqA and jmsMqB but not sure where/how to use them.
I am new to queues and MQ, so if anyone can point me in the right direction on how to implement a listener and (possibly) publisher for those 2 queues, that will be really helpful.
You can use #JmsListener to listen the queues. Here is the link.
For example:
#Component
class QueueAListener {
#JmsListener(
destination = "\${jms.mqA.queue}",
containerFactory = "jmsMQContainerFactory"
)
fun onMessage(message: Message) {
// yours logic
}
}
For queue B you can create new component or add method to the same class(don't forget to change method name).
UPD:
Container factory:
#Bean(name = "jmsMQContainerFactory")
public JmsListenerContainerFactory<?> topicJmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setPubSubDomain(true);
factory.setConnectionFactory(jmsMQConnectionFactory());
return factory;
}
You can use 'JmsTemplate' to send messages to queues. Please, check this manual
Note: This answer is based on the sample in https://github.com/ibm-messaging/mq-dev-patterns/tree/master/Spring-JMS/src/main/java/com/ibm/mq/samples/jms/spring/level114
Your jmsMQConnectionFactory bean should look something like (this is java)
import com.ibm.mq.jms.MQConnectionFactory;
import com.ibm.mq.samples.jms.spring.globals.handlers.OurDestinationResolver;
import com.ibm.mq.samples.jms.spring.globals.handlers.OurMessageConverter;
import com.ibm.mq.spring.boot.MQConfigurationProperties;
import com.ibm.mq.spring.boot.MQConnectionFactoryFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.QosSettings;
import javax.jms.DeliveryMode;
import javax.jms.JMSException;
#Configuration
public class MQConfiguration114 {
protected final Log logger = LogFactory.getLog(getClass());
#Bean
public MQConnectionFactory mqConnectionFactory() throws JMSException {
MQConfigurationProperties properties = new MQConfigurationProperties();
// Properties will be a mix of defaults, and those found in application.properties
// under ibm.mq
// Here we can override any of the properties should we need to
MQConnectionFactoryFactory mqcff = new MQConnectionFactoryFactory(properties,null);
MQConnectionFactory mqcf = mqcff.createConnectionFactory(MQConnectionFactory.class);
return mqcf;
}
#Bean
public JmsListenerContainerFactory<?> myContainerFactory114() throws JMSException {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(mqConnectionFactory());
// ... any customisations go here
return factory;
}
// If you are going to be sending messages then
#Bean("myJmsTemplate114")
public JmsTemplate myJmsTemplate114() throws JMSException {
JmsTemplate jmsTemplate = new JmsTemplate(mqConnectionFactory());
// .. Any customisations go here
return jmsTemplate;
}
}
Your listener code would look something like:
package com.ibm.mq.samples.jms.spring.level114;
import com.ibm.mq.samples.jms.spring.globals.data.OurData;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
//#Component
public class MessageConsumer114 {
protected final Log logger = LogFactory.getLog(getClass());
#JmsListener(destination = "${queue.name1}", containerFactory = "myContainerFactory114")
public void receiveRequest(OurData message) {
logger.info("");
logger.info( this.getClass().getSimpleName());
logger.info("Received message of type: " + message.getClass().getSimpleName());
logger.info("Received message :" + message);
}
}
#JmsListener(destination = "${queue.name2}", containerFactory = "myContainerFactory114")
public void receiveRequest(OurData message) {
logger.info("");
logger.info( this.getClass().getSimpleName());
logger.info("Received message of type: " + message.getClass().getSimpleName());
logger.info("Received message :" + message);
}
}
Your sender logic would look like:
import com.ibm.mq.samples.jms.spring.globals.data.OurData;
import com.ibm.mq.samples.jms.spring.globals.handlers.OurMessageConverter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.stereotype.Service;
//#Service
public class SendMessageService114 {
protected final Log logger = LogFactory.getLog(getClass());
#Value("${queue.name1}")
public String sendQueue1;
#Value("${queue.name2}")
public String sendQueue2;
final private JmsTemplate myJmsTemplate114;
final private OurMessageConverter ourConverter = new OurMessageConverter();
SendMessageService114(JmsTemplate myJmsTemplate114) {
this.myJmsTemplate114 = myJmsTemplate114;
}
public void send1(OurData msg) {
logger.info("Sending Message");
myJmsTemplate114.convertAndSend(sendQueue1, msg);
}
public void send2(OurData msg) {
logger.info("Sending Message");
myJmsTemplate114.convertAndSend(sendQueue2, msg);
}
}

Spring Integration how to use Control Bus with JavaConfig, no DSL

I'm having a few issues with Spring Integration and the control bus. I need to turn auto-start off on an InboundChannelAdapter. However when I do this I can't get the ControlBus to start the channel adapter.
I've searched for an answer online, but most of the examples use XML configuration.
Here is the entirety of my code:
package com.example.springintegrationdemo;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.integration.annotation.InboundChannelAdapter;
import org.springframework.integration.annotation.Poller;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.config.ExpressionControlBusFactoryBean;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.file.FileReadingMessageSource;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.support.GenericMessage;
import java.io.File;
#SpringBootApplication
#EnableIntegration
public class SpringIntegrationDemoApplication {
#Bean
public MessageChannel fileChannel() {
return new DirectChannel();
}
#Bean(name = "fileMessageSource")
#InboundChannelAdapter(channel = "fileChannel", poller = #Poller(fixedDelay = "1000"),autoStartup = "false")
public MessageSource<File> fileMessageSource() {
FileReadingMessageSource fileReadingMessageSource = new FileReadingMessageSource();
fileReadingMessageSource.setDirectory(new File("lz"));
return fileReadingMessageSource;
}
#Bean
#ServiceActivator(inputChannel = "fileChannel")
public MessageHandler messageHandler() {
MessageHandler messageHandler = message -> {
File f = (File) message.getPayload();
System.out.println(f.getAbsolutePath());
};
return messageHandler;
}
#Bean
MessageChannel controlChannel() {
return new DirectChannel();
}
#Bean
#ServiceActivator(inputChannel = "controlChannel")
ExpressionControlBusFactoryBean controlBus() {
ExpressionControlBusFactoryBean expressionControlBusFactoryBean = new ExpressionControlBusFactoryBean();
return expressionControlBusFactoryBean;
}
#Bean
CommandLineRunner commandLineRunner(#Qualifier("controlChannel") MessageChannel controlChannel) {
return (String[] args)-> {
System.out.println("Starting incoming file adapter: ");
boolean sent = controlChannel.send(new GenericMessage<>("#fileMessageSource.start()"));
System.out.println("Sent control message successfully? " + sent);
while(System.in.available() == 0) {
Thread.sleep(50);
}
};
}
public static void main(String[] args) {
SpringApplication.run(SpringIntegrationDemoApplication.class, args);
}
}
The message is sent to the control bus component successfully, but the inbound channel adapter never starts.
I would appreciate any help.
Thanks,
Dave
See here: https://docs.spring.io/spring-integration/docs/current/reference/html/configuration.html#annotations_on_beans
The fileMessageSource bean name is exactly for the FileReadingMessageSource. A SourcePollingChannelAdapter created from the InboundChannelAdapter has this bean name: springIntegrationDemoApplication.fileMessageSource.inboundChannelAdapter.
The #EndpointId can help you to simplify it.
In other words: everything is OK with your config, only the problem that you don't use the proper endpoint id to start the SourcePollingChannelAdapter.

Spring-core with #RabbitListener annotation does not fire bounded method

I am trying to use rabbitmq #RabbitListener annotation in my methods so that whenever any message arrives, my method can get auto executed. Based on the official documentation provided here, I made the folowing config class
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.annotation.EnableRabbit;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitAdmin;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.rabbit.listener.MessageListenerContainer;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class RabbitConfiguration {
public static final String QUEUE_NAME="myQueue";
public static final String EXCHANGE_NAME="my_EXCHANGE";
#Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setMaxConcurrentConsumers(5);
return factory;
}
#Bean
public ConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory =
new CachingConnectionFactory("localhost");
return connectionFactory;
}
#Bean
public AmqpAdmin amqpAdmin() {
return new RabbitAdmin(connectionFactory());
}
#Bean
public RabbitTemplate rabbitTemplate() {
return new RabbitTemplate(connectionFactory());
}
#Bean
public Queue myQueue() {
return new Queue(QUEUE_NAME);
}
#Bean
MessageListenerContainer messageListenerContainer() {
SimpleMessageListenerContainer simpleMessageListenerContainer = new SimpleMessageListenerContainer();
simpleMessageListenerContainer.setConnectionFactory(connectionFactory());
simpleMessageListenerContainer.setQueues(myQueue());
simpleMessageListenerContainer.setMessageListener(new RabbitMQListner());
return simpleMessageListenerContainer;
}
#Bean
FanoutExchange exchange() {
return new FanoutExchange(EXCHANGE_NAME);
}
#Bean
Binding binding(Queue queue, FanoutExchange exchange) {
return BindingBuilder.bind(queue).to(exchange);
}
#Bean
MessagingService messagingService(){
return new MessagingService();
}
}
Then from my service class I used like this:-
#Component
public class MessagingService {
public void send(String msg){
ApplicationContext context =
new AnnotationConfigApplicationContext(RabbitConfiguration.class);
AmqpTemplate template = context.getBean(AmqpTemplate.class);
template.convertAndSend(QUEUE_NAME,"Hello from template "+msg);
}
#RabbitListener(queues=QUEUE_NAME)
private void receiveMessage(String order) {
System.out.println("Hello"+order);
}
The send method is working fine. But the receiveMessage method does not print the expected output. It should print the message as soon as it arrives in the queue. Instead, When I tried to apply #EnableRabbit annotation in my configuration file, I got java.lang.ArrayStoreException exception on runtime. The method receiveMessage does not even get executed.
You can ignore typo.
You need a method annotated with #RabbitHandler as well.
Example :
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class Consumer {
#RabbitListener(queues = "myQueue")
#RabbitHandler
public void handle(String msg) {
System.out.println("RCVD :: " + msg);
}
}

Doesn't work rollback() in SessionAwareMessageListener

Even though the message is received by the MessageListener, I don't want to remove from the Queue, I want to do some processing in onMessage method and based on the result:
I want to commit(); for Success - so the message will be completely removed from the Queue.
For Failures - don't commit - rollback(); so the message will be redelivered (some times by default) and then goes to Dead letter Queue (DLQ). That’s OK for us.
I use: SpringBoot and hornetq (spring-boot-starter-hornetq-1.4.7.RELEASE).
Settings:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.config.JmsListenerContainerFactory;
import org.springframework.jms.connection.UserCredentialsConnectionFactoryAdapter;
import org.springframework.jndi.JndiObjectFactoryBean;
import org.springframework.jndi.JndiTemplate;
import javax.jms.ConnectionFactory;
import javax.naming.Context;
import javax.naming.NamingException;
import java.util.Properties;
import static com.test.hornetq.Receiver.LOG;
import static javax.jms.Session.SESSION_TRANSACTED;
#Configuration
public class JmsConfig {
private String host;
private String port;
private String connectionFactoryJndiName;
private String jndiInit;
private String user;
private String password;
private String jmsReceiverConcurrency;
public JmsConfig(final Environment env) {
host = env.getProperty("host");
port = env.getProperty("port");
connectionFactoryJndiName = env.getProperty("connectionfactory.jndiname");
jndiInit = env.getProperty("jndiInit");
user = env.getProperty("user");
password = env.getProperty("password");
jmsReceiverConcurrency = env.getProperty("jmsReceiverConcurrency");
}
#Bean
public JndiTemplate jndiTemplate() {
final JndiTemplate jndiTemplate = new JndiTemplate();
jndiTemplate.setEnvironment(getProperties());
return jndiTemplate;
}
#Bean
public JndiObjectFactoryBean jmsConnectionFactory() throws NamingException {
final JndiObjectFactoryBean jndiObjectFactoryBean = new JndiObjectFactoryBean();
jndiObjectFactoryBean.setJndiTemplate(jndiTemplate());
jndiObjectFactoryBean.setJndiName(connectionFactoryJndiName);
jndiObjectFactoryBean.afterPropertiesSet();
return jndiObjectFactoryBean;
}
#Bean
#Primary
public ConnectionFactory connectionFactory() throws NamingException {
final UserCredentialsConnectionFactoryAdapter adapter = new UserCredentialsConnectionFactoryAdapter();
adapter.setTargetConnectionFactory((ConnectionFactory) jmsConnectionFactory().getObject());
adapter.setUsername(user);
adapter.setPassword(password);
return adapter;
}
#Bean
public JmsListenerContainerFactory<?> myJmsContainerFactory() throws NamingException {
final DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
factory.setSubscriptionDurable(false);
factory.setConcurrency(jmsReceiverConcurrency);
factory.setMaxMessagesPerTask(1);
factory.setSessionTransacted(true);
factory.setSessionAcknowledgeMode(SESSION_TRANSACTED);
factory.setErrorHandler(t -> {
LOG.error("Error in listener!", t);
});
return factory;
}
private Properties getProperties() {
final Properties jndiProps = new Properties();
jndiProps.setProperty(Context.INITIAL_CONTEXT_FACTORY, jndiInit);
jndiProps.setProperty(Context.PROVIDER_URL, "http-remoting://" + host + ":" + port);
jndiProps.setProperty(Context.SECURITY_PRINCIPAL, user);
jndiProps.setProperty(Context.SECURITY_CREDENTIALS, password);
return jndiProps;
}
}
And receiver:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import javax.jms.JMSException;
import javax.jms.MapMessage;
import javax.jms.Session;
#Component
public class Receiver {
#JmsListener(destination = "${destination.name}", containerFactory = "myJmsContainerFactory")
public void onReceive(final MapMessage message, Session session) throws JMSException {
try {
System.out.println(">>>> " + message);
session.rollback();
} catch (Exception ex) {
System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>THROW ");
throw ex;
}
}
}
But when I do rollback(); nothing happen and message doesn't comeback.
The code work. The problem was in hornetq settings in server side.
<pre-acknowledge>true</pre-acknowledge>
Extra Acknowledge Modes
Please note, that if you use pre-acknowledge mode, then you will lose transactional semantics for messages being consumed, since clearly they are being acknowledged first on the server, not when you commit the transaction. This may be stating the obvious but we like to be clear on these things to avoid confusion!

How to subcribe to Spring Boot JMS topic from other app

I have 2 applications, the first app starts a ActiveMQ broker ( https://spring.io/guides/gs/messaging-jms/ ).
At the second app I want to subcribe a topic from the first app.
How can I do this without starting a ActiveMQ Server?
Possible solution:
Server Application Project
import java.time.LocalDateTime;
import javax.jms.ConnectionFactory;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.broker.BrokerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.ui.ModelMap;
#SpringBootApplication
#EnableJms
#EnableScheduling
public class JsmServerApplication {
#Autowired
JmsTemplate jmsTemplate;
#Bean
public BrokerService broker() throws Exception {
BrokerService ret = new BrokerService();
ret.addConnector("tcp://0.0.0.0:4444"); // allow remote connections
ret.setBrokerName("primary-broker");
ret.setUseJmx(true);
return ret;
}
#Bean
public ConnectionFactory connectionFactory() {
return new ActiveMQConnectionFactory("tcp://localhost:4444");
}
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(jacksonJmsMessageConverter());
return factory;
}
#Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
public static void main(String[] args) {
SpringApplication.run(JsmServerApplication.class, args);
}
#Scheduled(cron = "*/5 * * * * ?")
public void run() {
ModelMap msg = new ModelMap("now", LocalDateTime.now().toString());
System.out.println("Sending: " + msg);
jmsTemplate.convertAndSend("messages", msg);
}
}
Client Application Project
import javax.jms.ConnectionFactory;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;
import org.springframework.ui.ModelMap;
#SpringBootApplication
#EnableJms
public class JsmClientApplication {
#Bean
public ConnectionFactory connectionFactory() {
return new ActiveMQConnectionFactory("tcp://localhost:4444");
}
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(jacksonJmsMessageConverter());
return factory;
}
#Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
#JmsListener(destination = "messages", containerFactory = "jmsListenerContainerFactory")
public void msg(ModelMap msg) {
System.out.println(msg);
}
public static void main(String[] args) {
SpringApplication.run(JsmClientApplication.class, args);
}
}
Is it a correct approch?
Solved with this:
http://javasampleapproach.com/java-integration/activemq-work-spring-jms-activemq-topic-publisher-subcribers-pattern-using-springboot
You can use the MessageConsumer to consume the data like the code below
public static void main(String[] args) throws JMSException {
// Getting JMS connection from the server
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("topic");
MessageConsumer consumer = session.createConsumer(topic);
MessageListener listner = new MessageListener() {
public void onMessage(Message message) {
try {
//do operations
} catch (JMSException e) {
}
}
};
consumer.setMessageListener(listner);
connection.close();
}
Since you are using the ActiveMQConnectionFactory, you can set the broker as below
BrokerService broker = new BrokerService();
broker.addConnector("tcp://localhost:4444);
broker.setPersistent(false);
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false");
If you do not have any restrictions in not using ActiveMQ, You can use Kafka for doing the same. Kafka provides you a highly scalable and distributed Message Bus with simple API.
https://kafka.apache.org/quickstart
I am not sure about the constraints but I just wanted to give you a feel of Kafka. However, the above code should help you in understanding the concept of subscribing and consuming messages from a topic.
See this answer for how to listen on a tcp port instead of the vm:// transport.

Resources