I want to use ActiveMQ within Spring Boot app as embedded server. To setup ActiveMQ I used following tutorial: Spring Boot. Messaging with JMS. My app will be the broker and the consumer. There are multiple threads creating messages like this:
#Autowired
private JmsTemplate jmsTemplate;
.......
MessageCreator messageCreator = session -> session.createObjectMessage(transactionNotificationData);
jmsTemplate.setSessionAcknowledgeMode(Session.CLIENT_ACKNOWLEDGE);
jmsTemplate.send(QUEUE, messageCreator);
I have another class with following method:
#JmsListener(destination = QUEUE)
public void receive(Message message) throws IOException {
brokerService.getPersistenceAdapter();
try {
if (message instanceof ObjectMessage) {
ObjectMessage objMessage = (ObjectMessage) message;
NotificationData notification = (NotificationData) objMessage.getObject();
LOG.info("Received <" + notification.notification + ">");
...... do some stuff ........
// message.acknowledge();
}
} catch (JMSException e) {
e.printStackTrace();
}
During the tests I can see the messages are produced and consumed.
As you can see message.acknowledge() is commented. So I expect the message will be redelivered after rerun of my app. However it doesn't happen.
Message Acknowledgement is automatically handled by the container and it executes after the onMessage() is successfully executed,(receive() in your case),
so even when you comment message.acknowledge(); , container on its own sends the acknowledgement
you can have a look at following link for more reference
Acknowledgement from Consumer in ActiveMQ
Hope this helps!
Good luck!
Related
I have embedded ActiveMQ broker configured in Spring with websocket support (using STOMP).
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketMqConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableStompBrokerRelay("/topic");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/messaging")
.setAllowedOrigins("*")
.withSockJS();
}
#Bean(initMethod = "start", destroyMethod = "stop")
public BrokerService brokerService() throws Exception {
PersistenceAdapter persistenceAdapter = getPersistenceAdapter();
BrokerService brokerService = new BrokerService();
brokerService.setPersistent(true);
brokerService.setDeleteAllMessagesOnStartup(true);
brokerService.setUseJmx(false);
brokerService.setBrokerName("broker");
brokerService.addConnector("stomp://localhost:61613");
return borkerService;
}
In my JavaScript client I subscribe to topic:
var successHandler = function() {
stompClient.subscribe('/topic/test', function(not) {
pushNotification(not);
}, {'id': clientId, 'activemq.subscriptionName': clientId});
};
var socket = new SockJS('/messaging');
var stompClient = Stomp.over(socket);
stompClient.connect({'client-id': clientId}, successHandler, failureHandler);
And I am using backend service to feed this topic:
#Autowired
private SimpMessagingTemplate messagingTemplate;
messagingTemplate.convertAndSend("/topic/test", event);
And here are my questions:
When I send message to topic, but client haven't been subscribed yet, why messages are not persisted (I suppose that after client subscribe, he should be notified about missed messages)?
If client disconnect from topic, every message is persisted, is there any mean to restrict number of persisted messages, time or size of KahaDB's log files?
Messages sent to a Topic are not persisted unless the client has created a durable Topic subscription previously and the message is sent with the persistent flag set. To create a durable subscription add the headers as specified in the ActiveMQ STOMP documentation.
Once you start using durable Topic subscriptions then yes message can accumulate in the KahaDB store at which point you can configure the store usage limits to control size.
I am using JMS via IBM MQ. My sender code:
#Autowired
private JmsTemplate jmsTemplate;
public void sendPHRq(String msg) {
jmsTemplate.send(AntiFraudRq, session -> {
Message message = session.createTextMessage(msg);
LOGGER.info("1" + message.getJMSCorrelationID());
LOGGER.info("2" + message.getJMSMessageID());
return message;
});
}
But in my log I can see only such record:
1null
2null
How can I get my messageid? Because I'm listening reply queue with another listeners, and shouldn't take their messages.
You can get JMSMessageID after message is sent.
It will be generated by MQ JMS right before sending the message.
Hello I am writing some kind of simple testing scenario where I execute the following source code:
Here is my send() method:
public void send() throws JMSException {
Session session = null;
MessageProducer producer = null;
try {
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue("TEST.FOO");
producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
byte[] uselessData = new byte[1024];
BytesMessage message = session.createBytesMessage();
message.writeBytes(uselessData);
producer.send(message);
} finally {
producer.close();
session.close();
}
}
Here is my receive() method:
public void receive() throws JMSException {
Session session = null;
MessageConsumer consumer = null;
try {
session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue("TEST.FOO");
consumer = session.createConsumer(destination);
Message hugeMessage = consumer.receiveNoWait();
if (hugeMessage == null) {
System.out.println("Message was not received");
unsucsesfullCount++;
} else {
if (hugeMessage instanceof BytesMessage) {
System.out.println("Message received");
}
}
} finally {
consumer.close();
session.close();
}
}
I execute:
send();
receive();
The message value after receiveNoWait() is always null.
My question here is does the receiveNoWait() guarantee message delivery when there are messages in the broker? The send() is executed successfully so there is at least one message in the destination.
I've searched in the specification but there is not really clear definition if a message, which is available at the broker side should be explicitly received by receiveNoWait() at client side.
Also I want to ask, if receiveNoWait() does not have message available, should it trigger some refresh consumer process in the broker, so the next receiveNoWait() will receive the message?
The example code I provided run on ActiveMQ but my question is more conceptual than provider specific, because I have the same observation for other JMS providers.
No, the specification does not guarantee that any call to receiveNoWait will return a message, it might and then again it might not. When using receiveNoWait you must always check for a null return and act accordingly.
In the case of ActiveMQ the client will return a message if the broker has dispatched one to it and it is immediately available in the consumer prefetch buffer otherwise it just returns null.
Other implementations my indeed send a poll request to the broker, Qpid JMS for instance uses an AMQP link drain request to ask that the broker send it any messages that are available for dispatch and the broker will either send them or signal that the link is drained and there are no messages ready.
In short it's completely up to the client and broker how they implement receiveNoWait but no matter what you always need to account for the chance that you won't get a message returned to you from that method.
Have Apache Camel simple message route from folder to ActiveMQ topic:
//Create context to create endpoint, routes, processor within context scope
CamelContext context = new DefaultCamelContext();
//Create endpoint route
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception
{
from("file:data/outbox").to("activemq:topic:Vadim_Topic");
//from("activemq:topic:TEST").to.to("file:data/outbox");
}
});
context.start();
Thread.sleep(5000);
context.stop();
}
And JMS implementation if Topic Consumer:
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
try {
Connection connection = connectionFactory.createConnection();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
//connection.setClientID("12345");
connection.start();
Topic topic = session.createTopic("Vadim_Topic");
MessageConsumer messageConsumer = session.createConsumer(topic);
MessageListener messageListener = new MessageListener() {
public void onMessage(Message message) {
TextMessage textMessage = (TextMessage) message;
try {
System.out.println("Received message: " + textMessage.getText());
} catch (JMSException e) {
e.printStackTrace();
}
}
};
messageConsumer.setMessageListener(messageListener);
} catch (Exception e) {
e.printStackTrace();
}
Can't understand why is my consumer can't recieve messages sent by Camel route??
I guess thet problem is then I need to subscribe my JMS Consumer on messages sent by Camel?
How can I do this if this is the case?
Camel not only allows you to send messages to a topic, it can also very easily read messages from a topic and send it to one of your POJOs.
A route that reads from your topic and sends the messages to a POJO would look like this:
from("activemq:topic:Vadim_Topic").bean(ExampleBean.class);
Camel will figure out which method to call on the POJO depending on the type of message it received, and the available method signatures. See this page for details on using POJO's in camel routes: https://camel.apache.org/bean.html
I'm using Spring JMS and ActiveMQ to send message from a sender to multiple listeners using ActiveMQ Topic (publish/subscribe). So far all listeners can receive message from the sender. But I want to add a functionality that when a particular listener, say listener1, gets the message, listener1 will send a receipt confirmation to the sender. I followed the comment in my old post and created a TemporaryQueue in the sender and used ReplyTo in the sender and receiver to get the confirmation message from the listener to the sender.
My sender class is:
public class CustomerStatusSender {
private JmsTemplate jmsTemplate;
private Topic topic;
public void setJmsTemplate(JmsTemplate jmsTemplate) {
this.jmsTemplate = jmsTemplate;
}
public void setTopic(Topic topic) {
this.topic = topic;
}
public void simpleSend(final String customerStatusMessage) {
jmsTemplate.send(topic, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
TextMessage message = session.createTextMessage("hello world");
message.setStringProperty("content", customerStatusMessage);
message.setIntProperty("count", 10);
//send acknowledge request to a listener via a tempQueue
Destination tempQueue = session.createTemporaryQueue();
message.setJMSCorrelationID("replyMessage");
message.setJMSReplyTo(tempQueue);
return message;
}
});
}
}
The sender creates a TemporaryQueue for the listener to send back the confirmation message. Then in one of the listeners, I have the following code to send the confirmation message back to the sender:
public class CustomerStatusListener implements SessionAwareMessageListener<Message> {
public void onMessage(Message message, Session session) {
if (message instanceof TextMessage) {
try {
System.out.println("Subscriber 1 got you! The message is: "
+ message.getStringProperty("content"));
//create a receipt confirmation message and send it back to the sender
Message response = session.createMessage();
response.setJMSCorrelationID(message.getJMSCorrelationID());
response.setBooleanProperty("Ack", true);
TemporaryQueue tempQueue = (TemporaryQueue) message.getJMSReplyTo();
MessageProducer producer = session.createProducer(tempQueue);
producer.send(tempQueue, response);
} catch (JMSException ex) {
throw new RuntimeException(ex);
}
} else {
throw new IllegalArgumentException(
"Message must be of type TextMessage");
}
}
}
However, I found that the following line in the Listener class throws an error:
TemporaryQueue tempQueue = (TemporaryQueue) message.getJMSReplyTo();
MessageProducer producer = session.createProducer(tempQueue);
The exception error says:
The destination temp-queue://ID:xyz-1385491-1:2:1 does not exist.
So what's wrong here? I assume that tempQueue created by the sender is available for the listener in the same JMS session. Why the tempQueue object after calling message.getJMSReplyTo() does not return a valid TemporaryQueue?
The other question is: How do I receive the confirmation message in the sender? Should I implements MessageListener interface in the sender in order to receive the confirmation from the listener? Or should I just call receive() method to receive it synchronously?
Thanks for any suggestions!
If you are using spring-jms, why not just use a MessageListenerAdapter as your listener? - he will take care of the replyTo stuff and your listener can be a simple POJO.
In any case, you don't need to cast it to a Tempoarary queue; it's just a destination as far as the listener is concerned.
Checkout the MessageListenerAdapter` javadocs.
Also, you need to create a consumer on the temp queue on the sending side, to receive the reply. If the sending connection is closed, the temp queue will go away.
I ended up using a separate JMS Queue to send the acknowledge message from the listener-1 to the sender. For some reason, the temporaryQueue created by ActiveMQ is not available during the JMS session.