Spring websocket send message from async method - spring

I am using spring websocket 4.2.4 with sockjs and stomp and i am trying to send a message from the server to all the subscribers in an async task with no luck
my class is:
public class MyClass{
private timer;
public MyClass(){
this.timer = new Timer();
}
#Async
public void myMethod(){
timer.schedule(new MyReminder(), 1000);
}
#Async
private class MyReminder extends TimerTask{
#Autowired
SimpMessagingTemplate messageingTemplate;
#Override
public void run(){
messageingTemplate.convertAndSend("/app/subscribers","message");
}
}
}
but the subscribers dont get the message
any help? what did i do wrong :(
* EDIT *
my message broker:
public void configureMessageBroker(MessageBrokerRegistry config){
config.enableSimpleBroker("/topic","/myApp");
config.setApplicationDestinationPrefixes("/myApp");
}
and when i subscribe:
subscribe("myApp/someRoute")
thanks!!
** Edit 2: **
thanks for the help i fix the problem :)

You can do that but over Broker destination.
Share, please, your AbstractWebSocketMessageBrokerConfigurer.
Typically we do this:
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app/")
.enableSimpleBroker("/queue/", "/topic/");
}
Where all those /app/ destinations won't be treated by Broker. You can subscribe to them only in case of #SubscribeMapping - the request-reply scenario from the client initiative.
Your task is fully fits to the publish-subscribe story - topic in terms of STOMP.
There fore your subscribers (clients) should be subscribed to some topic on the broker and after that you can simply send message to it.

Related

Postman, Spring boot, WebSocket doesn't send notification

I'm trying send websocket notification to specific endpoint. I created the following configuration
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/my-address");
registry.addEndpoint("/my-address").withSockJS();
}
}
I'm able to connect using postman on address ws://localhost:8080/my-address (session is established). Next I want to send notification to this endpoint within the same application (I'll be generating some messages internally). I use class SimpMessageSendingOperations:
simpMessageSendingOperations.convertAndSend("/my-address", exampleMessage);
None error message is generated and notification does not appear for websocket clients. I also tried
simpMessageSendingOperations.convertAndSend("/topic/my-address", exampleMessage);
and also
simpMessageSendingOperations.convertAndSend("/app/my-address", exampleMessage);
I don't know what I'm doing wrong. Can you help me with this issue?
Thanks in advance for help

Configure Spring Boot #KafkaListener for listening to the latest messages

I am using Spring Boot's #KafkaListener to monitor some server's heartbeat messages as:
#KafkaListener(topics = "heartbeat-topic", groupId = "monitor")
public void listenToHeartbeatMsg(String message) {}
Issue is when the listener subscriber application started, even though the server is down, subscriber application will be receiving those previous heartbeat messages.
How can I fix this issue and listen only for real-time heartbeat messages?
Implement ConsumerSeekAware and, in onPartitionsAssigned call seekToBeginning on the callback.
See https://docs.spring.io/spring-kafka/docs/current/reference/html/#seek
public class MyListener implements ConsumerSeekAware {
...
#Override
public void onPartitionsAssigned(Map<TopicPartition, Long> assignments, ConsumerSeekCallback callback) {
callback.seekToEnd(assignments.keySet());
}
}

WebSocket messages are not delivered all the times

I have an application with WebSockets using spring-boot application as backend and Stomp/SockJS in the client side, the spring-boot application consume JMS queue messages and notify the changes to the right user. What is the problem? Sometimes works and sometimes doesn't work, same code and users could work or not.
The client side code is a bit more difficult to copy here because it's integrate over react/redux application but basically is a subscription to two different channels, both defined in the configuration of Spring. The sessions are created correctly according to debug information but just sometimes the message is processed to send it to connected sessions.
This is the configuration class for Spring.
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry
.addEndpoint("/stomp")
.setAllowedOrigins("*")
.withSockJS();
}
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry
.setApplicationDestinationPrefixes("/app")
.enableSimpleBroker("/xxxx/yyyy", "/ccccc");
}
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new ChannelInterceptor() {
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor =
MessageHeaderAccessor.getAccessor(message, StompHeaderAccessor.class);
if (StompCommand.CONNECT.equals(accessor.getCommand())) {
Object raw = message
.getHeaders()
.get(SimpMessageHeaderAccessor.NATIVE_HEADERS);
if (raw instanceof Map) {
Object name = ((Map<?,?>) raw).get("email");
if (name instanceof LinkedList) {
String user = ((LinkedList<?>) name).get(0).toString();
accessor.setUser(new User(user));
}
}
}
return message;
}
});
}
}
This is the JMS listener to process queue message and send it to specific user.
#Component
public class UserEventListener {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final SimpMessagingTemplate template;
#Autowired
public UserEventListener(SimpMessagingTemplate pTemplate) {
this.template = pTemplate;
}
#JmsListener(destination="user/events")
public void onStatusChange(Map<String, Object> props) {
if (props.containsKey("userEmail")) {
logger.debug("Event for user received: {}", props.get("userEmail"));
template.convertAndSendToUser((String)props.get("userEmail"), "/ccccc", props);
}
}
}
Edit 1:
After more debugging the times when doesn't work the "session" for WebSocket seems to be lost by Spring configuration. I don't see any log information about "Disconnected" messages or something similar, besides if I debug remotely the server when this happens the problem doesn't appears during debugging session. Some idea? The class from Spring where session disappear is DefaultSimpUserRegistry.
After more research I found a question with the same problem and the solution here. Basically the conclusion is this:
Channel interceptor is not the right place to authenticate user, we need to change it with a custom handshake handler.

Spring Boot WebSockets notifications

In my Spring Boot application I'm trying to implement a notifications functionality based on WebSockets.
I have provided a following configuration:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/notifications").withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.enableSimpleBroker("/topic", "/queue");
}
}
and trying to use SimpMessagingTemplate in order to send a message from server side to a specific client(user).
#Autowired
private SimpMessagingTemplate simpMessagingTemplate;
public void sendMessages() {
simpMessagingTemplate.convertAndSendToUser(%user%, "/horray", "Hello, World!");
}
Right now I don't understand a few things:
What value should be used for %user% parameter of
simpMessagingTemplate.convertAndSendToUser method ?
What is the correlation between my /notifications endpoint
registered in WebSocketConfig.registerStompEndpoints method and
destination parameter of
simpMessagingTemplate.convertAndSendToUser method and how to properly use it?
How to protect the users from reading other people's messages on the
client ?
The user parameter is the name that the client use when he subscribes the destination, see Spring Reference Chapter 26.4.11 User Destinations
Destination vs Endpoint:
Endpoint is the url where the websocket/message brocker is listening
Destination is the topic or subject within the message brocker

How to pause #JmsListener in my spring boot application?

Here are my Hornetq configuration in spring boot.
spring.hornetq.mode=embedded
spring.hornetq.embedded.enabled=true
spring.hornetq.embedded.persistent=true
spring.hornetq.port=5445
spring.hornetq.embedded.queues=jms.testqueue
Here is my Producer
public class Producer {#Inject
private JmsTemplate jmsTemplate;
public void resolveError( String message) {
try{
jmsTemplate.convertAndSend(DATA_QUEUE, message);
}catch(Exception e){
//log error
}
}}
Here is my Consumer
#JmsListener(destination = DATA_QUEUE)
public void consume(String message) throws InterruptedException {
log.info("Receiving event: {}", message);
try {
//do stuff with message
}catch (Exception e){
log.error(e.toString());
}
}
Here is my config file
#Configuration#EnableJms public class JmsConfig {
public static final String LOGGING_SCRAPPER_KEY ="DATA_SYNC_ERROR";
public static final String DATA_QUEUE = "jms.testqueue"; }
I want to slow down the consuming process of #JMSlistener, I don't want to the JMS listener hit the queue all the time any help is appreciated, thanks!!
The listeners that are created under the covers for each #JmsListener annotated method are held in a registry as explained in the documentation
If you want to pause your listener, it is very easy to look it up and stop it. Let's assume you have a way to invoke the following bean (JMX endpoint, secure rest mapping, whatever):
static class YourService {
private final JmsListenerEndpointRegistry registry;
#Autowired
public YourService(JmsListenerEndpointRegistry registry) {
this.registry = registry;
}
public void stopListener() {
this.registry.getListenerContainer("myListener").stop();
}
public void startListener() {
this.registry.getListenerContainer("myListener").start();
}
}
Then you need to associate the proper id to your listener (myListener) in the example above.
#JmsListener(id = "myListener", destination = DATA_QUEUE)
public void consume(String message) throws InterruptedException { ... }
I'm not able to set the consuming time of JmsListener but I found an alternative where I'm able to set delivery delay time limit on jmsTemplate instead, use jmsTemplate setDeliveryDelay which will delay sending it to the queue. Either way, it is delayed only if you go with delaying the consuming process of JMS listener you will have the message in the queue in my approach it won't be in the queue until the delay delivery time.

Resources