I can't get the Spring Message Sample to work in Unit tests using the SimpMessagingTemplate to send messages to the Endpoints.
I followed the instructions here:
https://spring.io/guides/gs/messaging-stomp-websocket/
So far my Controller looks like:
#Data #NoArgsConstructor #AllArgsConstructor
public static class Message {
private Long id;
private String value;
private long time;
}
#MessageMapping("/message")
#SendTo("/topic/response")
public Message slowEndpont(Message message) throws Exception {
Thread.sleep(3000); // simulated delay
System.err.println("Message Received: " + message);
return new Message(message.id, "Hello Client", System.currentTimeMillis());
}
My Unit Test now tries to send a message:
#Autowired
SimpMessagingTemplate messageTemplate;
#Test
public void sendMessage() throws Exception {
System.err.println("** Sending messages...");
messageTemplate.convertAndSend("/app/message",
new MessageController.Message(1L, "Hello Server", System.currentTimeMillis()));
messageTemplate.convertAndSend("/topic/message",
new MessageController.Message(1L, "Hello Server", System.currentTimeMillis()));
messageTemplate.convertAndSend("/queue/message",
new MessageController.Message(1L, "Hello Server", System.currentTimeMillis()));
messageTemplate.convertAndSend("/message",
new MessageController.Message(1L, "Hello Server", System.currentTimeMillis()));
System.err.println("** Messages send!");
Thread.sleep(1500);
}
Full Code sample is here:
https://github.com/puel/training/tree/master/messaging
So far so good. Messages are all send. But never received. I traced it down and the registry of the MessageTemplate is empty. But why?
This problem seems to be close too:
Send Message to all clients via SimpMessagingTemplate in ServletContextListener
But using MessageSendingOperations doesn't help either.
Thanks,
Paul
Related
Im writing back front java code spring 2.2.5. The front is connected to the back via a websocket. I want to send notifications to the client without request sent by client only connection and subscrition events are received by the server.
I tried some solotion based on SimpMessagingTemplate but when i'm trying to send back to the client using this interface (without request) the data is null pointer.
Controller code :
private SimpMessagingTemplate messagingTemplate;
#Autowired
public void WebSocketController(SimpMessagingTemplate messagingTemplate) {
this.messagingTemplate = messagingTemplate;
}
#RequestMapping(value = "/post/message", method = RequestMethod.POST)
public void PostMessage(#PathVariable String message) {
this.messagingTemplate.convertAndSend("/topic/myDest", message);
}
With the following config code i intercept connect and subscribe events, is there a way to save client parameter in order to send back notifications to it.
#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())){
String sessionId = accessor.getSessionId();
System.out.println("Connect " + sessionId);
} else if(StompCommand.SUBSCRIBE.equals(accessor.getCommand())){
System.out.println("Subscribe ");
} else if(StompCommand.SEND.equals(accessor.getCommand())){
System.out.println("Send message " );
} else if(StompCommand.DISCONNECT.equals(accessor.getCommand())){
System.out.println("Exit ");
} else {
}
return message;
}
});
}
I finally find the solution: In my previous PreSend interceptor, i can save all subscribed clients :
xxxx.Channels.add(channel);
In xxx class : Channels is defined as :
public final ArrayList<MessageChannel> Channels = new ArrayList<MessageChannel>();
In Post function i created a SimpMessagingTemplate instance for each channel :
for (int i = 0; i < Channels.size(); i++) {
MessageChannel channel = Channels.get(i);
SimpMessagingTemplate messagingTemplate = new SimpMessagingTemplate(channel);
messagingTemplate.convertAndSend("/topic/myDest", data.toString().getBytes());
}
And the message is correctly sent.
I am going to do send my DATA toRabbitMq producer(message sender) and get responsible data from RabbitMq consumer(message receiver). producer part is working fine .now my problem is how to implement consumer part (receiver part) in side the Spring boot API. .Below is My spring boot API and i written ProducerAndConsumer one class.
ProducerAndConsumer.class
#Component
public class ProducerAndConsumer {
#Autowired
private RabbitTemplate rabbitTemplate;
//MessageProducer part (send part)
public boolean sendMessage(String message) {
rabbitTemplate.convertAndSend(RobbitMqConfig.ROUTING_KEY, message);
System.out.println("Is listener returned ::: ==========="+rabbitTemplate.isReturnListener());
return rabbitTemplate.isReturnListener();
}
//Consumer part (receiver part)
#RabbitListener(queues = RobbitMqConfig.QUEUE_NAME1)
public void receiveMessage ( final Message message){
System.out.println("Received message====Receiver=====" + message.getPayload());
}
}
API part
#PostMapping(value = {"/sendFilesName"})
public ResponseEntity<?> sendFilesName(#RequestBody SendFileNameRequest sendFileNameRequest, HttpServletRequest request) throws ParseException {
System.out.println("FileNameArray="+sendFileNameRequest.getFileNameArray());
if(sendFileNameRequest.getFileNameArray().size()!=0) {
List<String> message = sendFileNameRequest.getFileNameArray();
**//see here i send my message array data**
if(producerAndConsumer.sendMessage(message.toString())){
**//here i want implement my receiver part how to?**
return ResponseEntity.ok(new ApiResponse(true, "fileName List sent successfully", "",true));
}else {
return ResponseEntity.ok(new ApiResponse(false, "fileName List sent Fails", "",true));
}
}else {
return ResponseEntity.ok(new ApiResponse(false, "fileName List not present ", "",true));
}
}
The routing algorithm behind a direct exchange is simple - a message goes to the queues whose binding key exactly matches the routing key of the message.
spring amqp
Note: Check the routing key and queues binded using rabbitmq admin console to figure out whats going on or share the rabbitmq configuration.
I am working with Spring Boot and Redis server for my web application. When a client sends a post, a message is sent to Redis on channel "chat". This works well. For recieving messages I created the class RedisMessageSubscriber, which recieves messages from Redis.
All clients have subscribed the STOMP Websocket client "topic/test".
My question is: How can I redirect the message I recieved in RedisMessageSubscriber to the client Websocket channel?
My first try below didn't worked, because the controller sent a message from the queue to the client before the message received from Redis.
DwittrController.class (Messages are sent to clients here):
#MessageMapping("/newPost")
#SendTo("/topic/test")
public Message message(ClientMessage message) {
log.debug("a message from client was recieved");
userRepository.sendMessage(message.getMessage());
String redisMessage = RedisMessageSubscriber.messageList.get(0);
RedisMessageSubscriber.messageList.remove(0);
return new Message("New post from " + redisMessage + " is available.");
}
UserRepositoryImpl.class:
public void sendMessage(String message) {
stringRedisTemplate.convertAndSend("chat", message);
}
RedisMessageSubscriber.class:
#Component
public class RedisMessageSubscriber implements MessageListener {
private final Logger log = LoggerFactory.getLogger(this.getClass());
public static List<String> messageList = new ArrayList<String>();
public void onMessage(Message message, byte[] pattern) {
messageList.add(message.toString());
log.debug("Message received: " + message.toString());
}
}
In your RedisMessageSubscriber class you can autowire a SimpMessagingTemplate. Code example is not precise, but you get the idea.
#Component
public class RedisMessageSubscriber implements MessageListener {
private final Logger log = LoggerFactory.getLogger(this.getClass());
public static List<String> messageList = new ArrayList<String>();
#Autowired
SimpMessagingTemplate messagingTemplate;
public void onMessage(Message message, byte[] pattern) {
messageList.add(message.toString());
log.debug("Message received: " + message.toString());
messagingTemplate.convertAndSend( "/topic/test", message);
}
}
I have a Spring Websocket Stomp application that accepts SUBSCRIBE requests.
In application I have a handler for SUBSCRIBE, that is,
#Component
public class SubscribeStompEventHandler implements ApplicationListener<SessionSubscribeEvent> {
#Override
public void onApplicationEvent(SessionSubscribeEvent event) {}
}
that I use to validate subscription.
I would check something in the onApplicationEvent and send STOMP ERROR message back to client from this function.
I found this recipe How to send ERROR message to STOMP clients with Spring WebSocket? but I need to understand how to get outboundChannel.
I tried also the following code:
public void sendStompError(SimpMessagingTemplate simpMessagingTemplate, String sessionId, String topic, String errorMessage) {
StompHeaderAccessor headerAccessor = StompHeaderAccessor.create(StompCommand.ERROR);
headerAccessor.setMessage(errorMessage);
headerAccessor.setSessionId(sessionId);
headerAccessor.setLeaveMutable(true);
simpMessagingTemplate.convertAndSendToUser(sessionId, topic, new byte[0], headerAccessor.getMessageHeaders());
}
and I tried topic to be some subsciption topic and /queue/error topic. However I did not see messages propagating to client.
In Client, I use:
stompClient.connect(headers
, function (frame) {
console.log("Conn OK " + url);
}, function (error) {
console.log("Conn NOT OK " + url + ": " + JSON.stringify(error));
});
}
and my goal is to have function(error) called when I send STOMP ERROR.
Please advice me how exactly I can send proper STOMP ERROR, e.g. by getting Outboundchannel.
You can send ERROR Message like this:
StompHeaderAccessor headerAccessor = StompHeaderAccessor.create(StompCommand.ERROR);
headerAccessor.setMessage(error.getMessage());
headerAccessor.setSessionId(sessionId);
this.clientOutboundChannel.send(MessageBuilder.createMessage(new byte[0], headerAccessor.getMessageHeaders()));
The following is just enough to inject that clientOutboundChannel:
#Autowired
#Qualifier("clientOutboundChannel")
private MessageChannel clientOutboundChannel;
Just because clientOutboundChannel bean is declared in the AbstractMessageBrokerConfiguration.
UPDATE
STOMP ERROR always closes connection? I am getting this effect. Code 1002.
Yes, it is. See StompSubProtocolHandler.sendToClient():
if (StompCommand.ERROR.equals(command)) {
try {
session.close(CloseStatus.PROTOCOL_ERROR);
}
catch (IOException ex) {
// Ignore
}
}
I have implemented Web Socket using Spring MVC and it is working fine for me i.e work from one browser to another browser which is open for those socket using this code.
#MessageMapping("/hello")
#SendTo("/topic/greetings")
public HelloMessage greeting(HelloMessage message) throws Exception {
Thread.sleep(3000); // simulated delay
return message;
}
Can any one help me for who to call #SendTo("/topic/greetings") from normal api controller.I have try using this but it is not working for me
#RequestMapping(value = "/sendMessage")
#SendTo("/topic/greetings")
public HelloMessage sendMessage() throws Exception {
return new HelloMessage((int) Math.random(), "This is Send From Server");
}
any idea for this?
Thanks
I have found solution for that
#Autowired
private SimpMessagingTemplate template;
#RequestMapping(value = "/sendMessage")
public void sendMessage() throws Exception {
this.template.convertAndSend("/topic/greetings", new HelloMessage(
(int) Math.random(), "This is Send From Server"));
}
by using this we can send message to open WebSocket.
Thanks