Capture MQ down event from Spring JMS - java

Does Default Message Listener Container of Spring have any method like ErrorHandler where I can Capture MQ down Event.
I can get following logs from spring but need to report when MQ is down.
o.s.j.l.DefaultMessageListenerContainer : Setup of JMS message listener invoker failed for destination
o.s.j.l.DefaultMessageListenerContainer : Successfully refreshed JMS Connection
How can I achieve this?

Finally I solved my issue by overrding refreshConnectionUntilSuccessful of DefaultMessageListenerContainer as below:
public class MessageListenerContainer extends DefaultMessageListenerContainer {
#Override protected void refreshConnectionUntilSuccessful() {
super.refreshConnectionUntilSuccessful();
// Your own implementation goes here like sending an email
logger.error(MessageListenerContainer.class, new Exception("MQ CONNECTION LOST"));
}}

Related

"No decoder for session id" in logs for Spring web sockets and Stomp

I have a tomcat cluster with 2 instances and apache proxy in one server. Application use Spring framework 4.3.10 with web sockets, apache-activemq-5.15.0 as stomp broker:
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/wshandler" allowed-origins="*">
</websocket:stomp-endpoint>
<websocket:stomp-broker-relay prefix="/topic,/queue"
relay-host="localhost" relay-port="62356"
heartbeat-send-interval="10000" heartbeat-receive-interval="10000"/>
<websocket:client-inbound-channel>
<websocket:interceptors>
<bean class="somepath.TopicSubscriptionInterceptor"/>
</websocket:interceptors>
</websocket:client-inbound-channel>
</websocket:message-broker>
Now about 20 clients connect to web-sockets in the same time. All works well, but periodically I have an error in logs (estimated 8-10 times in an hour). How can I fix it?
2017-10-06 09:54:01,046 ERROR [StompSubProtocolHandler] Failed to parse TextMessage payload=[], byteCount=1, last=true] in session 6f. Sending STOMP ERROR to client.
java.lang.IllegalStateException: No decoder for session id '6f'
at org.springframework.web.socket.messaging.StompSubProtocolHandler.handleMessageFromClient(StompSubProtocolHandler.java:249)
at org.springframework.web.socket.messaging.SubProtocolWebSocketHandler.handleMessage(SubProtocolWebSocketHandler.java:307)
at org.springframework.web.socket.handler.WebSocketHandlerDecorator.handleMessage(WebSocketHandlerDecorator.java:75)
at org.springframework.web.socket.handler.LoggingWebSocketHandlerDecorator.handleMessage(LoggingWebSocketHandlerDecorator.java:56)
at org.springframework.web.socket.handler.ExceptionWebSocketHandlerDecorator.handleMessage(ExceptionWebSocketHandlerDecorator.java:58)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.handleTextMessage(StandardWebSocketHandlerAdapter.java:110)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter.access$000(StandardWebSocketHandlerAdapter.java:42)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:81)
at org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter$3.onMessage(StandardWebSocketHandlerAdapter.java:78)
at org.apache.tomcat.websocket.WsFrameBase.sendMessageText(WsFrameBase.java:394)
at org.apache.tomcat.websocket.server.WsFrameServer.sendMessageText(WsFrameServer.java:119)
at org.apache.tomcat.websocket.WsFrameBase.processDataText(WsFrameBase.java:495)
at org.apache.tomcat.websocket.WsFrameBase.processData(WsFrameBase.java:294)
at org.apache.tomcat.websocket.WsFrameBase.processInputBuffer(WsFrameBase.java:133)
at org.apache.tomcat.websocket.server.WsFrameServer.onDataAvailable(WsFrameServer.java:82)
at org.apache.tomcat.websocket.server.WsFrameServer.doOnDataAvailable(WsFrameServer.java:171)
at org.apache.tomcat.websocket.server.WsFrameServer.notifyDataAvailable(WsFrameServer.java:151)
at org.apache.tomcat.websocket.server.WsHttpUpgradeHandler.upgradeDispatch(WsHttpUpgradeHandler.java:148)
at org.apache.coyote.http11.upgrade.UpgradeProcessorInternal.dispatch(UpgradeProcessorInternal.java:54)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:53)
It's solved in new spring framework version (https://github.com/spring-projects/spring-framework/commit/f425a993e7be82ffdbdda24370925a34c42925f2)
If you are using old version ( up to 2019 ), you can fix this problem by overriding WebSocketHandlerDecorator :
#Slf4j
public class WebSocketSessionCapturingHandlerDecorator extends WebSocketHandlerDecorator {
public void handleMessage(WebSocketSession session, WebSocketMessage<?> message) throws Exception {
if(session.isOpen()){
super.handleMessage(session, message);
}else{
log.info("Dropped inbound WebSocket message due to closed session");
}
}
And use it in main WebSocketConfig :
#Slf4j
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration registration) {
registration.addDecoratorFactory(new WebSocketHandlerDecoratorFactory() {
#Override
public WebSocketHandler decorate(WebSocketHandler webSocketHandler) {
return new WebSocketSessionCapturingHandlerDecorator(webSocketHandler);
}
});
}
Update the heartbeat header in the connect frame to 5000,5000 which means both client and server will need to send the heartbeat frame every frame 5 sec. In my case the server was dropping the connection when it was idle for too long. What this essentially does is keep the underlying TCP connection alive so that the server does not close it. Also restart the server after making this change.
Hope this helps!!
If you are using a java client over amqp to connect to rabbitMq you can set
ConnectionFactory cf = new ConnectionFactory();
// set the heartbeat timeout to 60 seconds
cf.setRequestedHeartbeat(60);
References:
https://www.rabbitmq.com/heartbeats.html
https://stomp.github.io/stomp-specification-1.2.html#CONNECT_or_STOMP_Frame

Listen to RabbitMQ queue and get notifications on events

I am currently developing an Java application using Spring AMQP with RabbitMQ and would like to monitor my queues and being informed if some events happen, like
element was added to queue,
element was removed, or
element was but in the queue again (rollback).
How can I listen to such events, or what is the RabbitMQ-way of doing such things?
To receive a message asynchronously from a queue is to use the annotated listener endpoint infrastructure. In a nutshell, it allows you to expose a method of a managed bean as a Rabbit listener endpoint.
#Component
public class MyService {
#RabbitListener(queues = "myQueue")
public void processOrder(String data) {
...
}
}
Check this
Whenever there a message is pushed to the queue myQueue processOrder method is triggered.
For your other requirements you can also use spring events to monitor any operation performed on the queue. Just right before performing any operation on the queue a respective event will be triggered.

Push Message from ActiveMQ to Spring Controller

I'm using Spring MVC, ActiveMQ and WebSocket(via sock.js and stomp.js) to build a real-time data delivery application.
As we know, when a producer(another desktop application) push a message to ActiveMQ, and the onMessage() method will catch it.
public class MessageReceiver implements MessageListener {
public void onMessage(Message message) {
System.out.println(message);
// How to push the message to a Controller in Spring?
}
}
Most of the tutorials just print the message to the console.
I have another controller called WebSocketController:
#Controller
public class WebSocketController {
#SubscribeMapping("/getRealTimeResult.action/{submissionId}")
public Message getRealTimeResultAction(
#DestinationVariable long submissionId) {
return new Message("Message content from onMessage() method");
}
}
I want to push the message received in onMessage() method to the getRealTimeResultAction() method. Can you tell me how to do it?
I know that the ActiveMQ can communicate with the browser using stomp via the port 61613.
I don't want to do this because I think the MQ should be transparent to the user. Also I need to do some authorization in the WebSocketController.
Generally speaking an #Controller with #SubscribeMapping and #MessageMapping methods can handle subscriptions and messages from STOMP clients connected over WebSocket.
From your description it's not clear what you're trying to do. Was the message pushed to ActiveMQ via STOMP from a browser client or was it produced by some other back-end JMS client? Also the MessageReceiver receives an actual message while the #Controller method has an #SubscribeMapping method for handling a subscription from a STOMP client. It's not clear what you're trying to do. Please provide more information so I can provide a better answer.

spring websockets: sending a message to an offline user - messages not enqueued in message broker

I have a webapp with spring and websockets using a message broker (activemq).
here is my config class:
#Configuration
#EnableWebSocketMessageBroker
#EnableScheduling
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableStompBrokerRelay("/topic","/queue/");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/hello").withSockJS();
}
}
I have a scheduled task that constantly pushing messages to the username "StanTheMan" :
#Scheduled(fixedDelay = 1000)
public void sendGreetings() {
HelloMessage hello = new HelloMessage();
hello.setContent("timeStamp:" + System.currentTimeMillis());
String queueMapping = "/queue/greetings";
template.convertAndSendToUser(
"DannyD",
queueMapping,
hello);
}
Currently, if the user is NOT connected via a websocket to the server - all the messages for him are not being en-queued for him, they simply discarded. whenever he connects - fresh messages are being en-queued for him.
Is it possible for me to "convertAndSendToUser" a message to an offline user in any way? i would like to en-queue messages with an expired time for offline users to be later on pushed when they are connecting again and the expired time wasn't over.
how can i achieve that? Obviously using a real message broker (activemq) supposed to help achieving that, but how?
Thanks!
Indeed this feature can only be used to send messages to a (presently) connected user.
We plan to provide better ways to track failed messages (see SPR-10891). In the meantime as a workaround you could inject the UserSessionRegistry into your #Scheduled component and check if the getSessionIds methods returns a non-empty Set. That would indicate the user is connected.
One alternative may be to use your own convention for a queue name that each user can subscribe to (probably based on their user name or something else that's unique enough) in order to receive persistent messages. The ActiveMQ STOMP page has a section on persistent messages and expiration times.
This is a default behavior for message brokers.
And you haven't setup the ActiveMQ broker as your default broker so Spring is setting up a in-memory broker.
To achieve what you want setup/give your details of activeMQ to your spring websocket message broker. As Spring doesn't provide these settings, you have to do all the persistence settings at the ActiveMQ side. .
To setup ActiveMQ for spring websocket message broker you also need to enable stomp and use this:
registry.enableStompBrokerRelay("/topic").setRelayHost("hostName").setRelayPort("00000");
For more information checkout:
http://docs.spring.io/spring/docs/4.0.0.RELEASE/javadoc-api/org/springframework/messaging/simp/config/StompBrokerRelayRegistration.html

Message Driven Bean does not receive messages

I'm trying to implement a service which listens to a topic to receive messages sent to that topic. The code is quite simple:
#MessageDriven(mappedName="jms/TEST", activationConfig={
#ActivationConfigProperty(propertyName="destinationType",propertyValue="javax.jms.Topic"),
#ActivationConfigProperty(propertyName="subscriptionName",propertyValue="TEST")
})
public class MessageListener implements MessageListener {
private static final Logger logger = Logger.getLogger(MessageListener.class);
#Override
public void onMessage(Message arg0) {
logger.info("Receiving " + arg0);
}
}
The listener is deployed on Glassfish. In Glassfish, I also add an Admin Object Resource:
JNDI Name: jms/TEST
Resource Adaptor: jmsra
Resource Type: javax.jms.Topic
Class Name: com.sun.messaging.Topic
Name: TEST
Physical Name: TEST
I have another service, also in Glassfish, sending messages to topic TEST. However, my listener doesn't receive any messages at all. I create another service listening to the same topic without using Message Driven Bean and am able to receive, i.e. a message is sent without any problem. I wonder if I do anything wrong with my bean.
L

Categories