How to pass message to controller by #MessageMapping with specified user url? - websocket

I have such problem. When i try to send message from client side to server, it doesn't match with my #MessageMapping methods. I don't know how to intercept messages on controller layer.
Client side
sends message (it's react-stomp that uses sockjs):
move = (move) => {
this.clientRef.sendMessage("/user/${this.state.opponentId}/queue/move", JSON.stringify(move))
};
Server side. WebSocketConfig:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/handler")
.setHandshakeHandler(new CustomHandshakeHandler())
.setAllowedOrigins("http://localhost:3000")
.withSockJS();
}
#Override
public void configureMessageBroker(MessageBrokerRegistry brokerRegistry) {
brokerRegistry.setApplicationDestinationPrefixes("/app");
brokerRegistry.enableSimpleBroker("/topic", "/queue", "/user");
}
#EventListener
void handleSessionConnectedEvent(SessionConnectedEvent event) {
StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage());
}
#Override
public void configureClientInboundChannel(ChannelRegistration registration) {
registration.interceptors(new MyChannelInterceptor());
}
}
I also added interceptor class to check path of incomming message:
public class MyChannelInterceptor implements ChannelInterceptor {
#Override
public Message<?> preSend(Message<?> message, MessageChannel channel) {
StompHeaderAccessor accessor = StompHeaderAccessor.wrap(message);
return message;
}
}
On debugging of MyChannelInterceptor i see message with payload and headers. There is simpDestination header with such value:
/user/baedde36-0f9e-4fa5-b8d7-687db1dbcd67/queue/move
What #MessageMapping value should i write to handle messages from specified users? This message succesfully gets to frontside by subscription on this topic but doesn't stay on any controller:
`/user/${message}/queue/move`
I just want to handle messages on server side but i can't catch it there.

Okay. As i understood there is 3 ways to handle websocket messages:
/app - handles with controller
/user - handles with broker, sends messages to specific users
/topic - broadcast to topic for all subscribers
In my situation i just need to create json object with userId, receiverId and message. On server side add DTO class and get it as attribute in my controller method.
Solution:
move = (move) => {
let moveDto = {move: move, userId: this.state.userId, opponentId: this.state.opponentId}
this.clientRef.sendMessage(`/app/move`, JSON.stringify(moveDto))
};
Server side:
#Data
#NoArgsConstructor
#AllArgsConstructor
public class MoveDto {
private String userId;
private String opponentId;
private int move;
}
Controller class:
#RestController
public class GameController {
#Autowired
private SimpMessagingTemplate simpMessagingTemplate;
...//some code here
#MessageMapping("/move")
public void message(MoveDto moveDto) {
String userMessage= "foo";
String opponentMessage = "bar";
simpMessagingTemplate.convertAndSendToUser(
moveDto.getUserId(), "/queue/message", userMessage);
simpMessagingTemplate.convertAndSendToUser(
moveDto.getOpponentId(), "/queue/message", opponentMessage );
}

Related

Capturing a message from unity using WebSocketSharp on a Spring server

I can't solve the problem. It is necessary to send messages to the server through the socket and process them. I can intercept the subscription, unsubscribe, connection and disconnection events on the server. But I can’t understand how to send a message to a specific destonation and intercept it on the server. On the client I use the library https://github.com/sta/websocket-sharp
Server code
#Configuration
#EnableWebSocket
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer implements WebSocketConfigurer {
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/multiplayer");
config.setApplicationDestinationPrefixes("/app");
}
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/multiplayer").setAllowedOrigins("*").withSockJS();
}
#Autowired
MessageHandler messageHandler;
// message interception option
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry.addHandler(messageHandler, "/result-game");
}
}
In the registerWebSocketHandlers method, I register a listener, it intercepts messages only if you specify "/multiplayer/websocket" in the "/result-game" place, the only trouble is that it intercepts everything in general and the #EventListaner listeners that intercepted my connection events stop working , shutdowns, etc. And when you try to send data to the client on the socket, the client does not receive them.
Client code
var DOMAIN = "1.1.0.1:8080";
_webSocket = new WebSocket($"ws://{DOMAIN}/multiplayer/websocket");
_webSocket.OnOpen += (sender, e) =>
{
// Sending a connection message
StompMessageSerializer serializer = new StompMessageSerializer();
var connect = new StompMessage("CONNECT");
connect["accept-version"] = "1.1";
connect["heart-beat"] = "10000,10000";
connect["playerId"] = _id;
var s = serializer.Serialize(connect);
SubscribeSocket(clientId.ToString(), $"/multiplayer/connect/{_id}", ConnectServer);
_webSocket.Send(s);
// Attempt to send messages to a specific destination
var content = new { Subject = "Stomp client", Message = "Hello World!!" };
var broad = new StompMessage("SEND", JsonConvert.SerializeObject(content));
broad["content-type"] = "application/json";
broad["destination"] = "/result-game";
_webSocket.Send(serializer.Serialize(broad));
Debug.LogError(TAG + "Connect open");
};
_webSocket.ConnectAsync();
On the server, in addition to the option above, there are two more attempts to intercept
First
#Slf4j
#Controller
#RequiredArgsConstructor
public class MessageController{
#MessageMapping("/result-game")
public void say(String message) throws InterruptedException {
Thread.sleep(30);
}
}
Second
#ServerEndpoint("/result-game")
public class MessageHandle {
#OnMessage
public void handleMessage(Session session, String message) {
// Do something with the message
System.out.println("Received message: " + message);
}
#OnMessage
public void processGreeting(String message, Session session) {
System.out.println("Greeting received:" + message);
}
}
Could someone tell me what I'm doing wrong with spring and working with sockets for the first time

#RabbitListener for multiple object types

In SpringBoot I use RabbitTemplate and #RabbitListener to produce and receive Messages.
I have an abstract class to send and receive RabbitMQ message of a specific type and send those messages to a STOMP web socket.
I use one RabbitMQ topic exchange which is bind to one RabbitMQ queue. In the topic-exchange I send messages (Java objects) of 2 different types and I am using 2 #RabbitListener to consume these messages.
#Configuration
public class WsRabbitMqConfiguration {
public static final String WS_EXCHANGE_NAME = "websocket.replication";
#Getter
#Value(value = "${spring.application.name}")
private String routingKey;
#Bean
TopicExchange wsReplicationExchange() {
return new TopicExchange(
WS_EXCHANGE_NAME,
true,
false,
null);
}
#Bean
public Queue wsReplicationQueue() {
return new AnonymousQueue();
}
#Bean
public Binding bindingWsAttributeValueQueue(TopicExchange wsReplicationExchange,
Queue wsReplicationQueue) {
return BindingBuilder
.bind(wsReplicationQueue)
.to(wsReplicationExchange)
.with(routingKey);
}
}
#RequiredArgsConstructor
public abstract class AbstractWebsocketService<T> implements WebSocketService {
public static final String HEADER_WS_DESTINATION = "x-Websocket-Destination";
private final RabbitTemplate rabbitTemplate;
private final WsRabbitMqConfiguration wsRabbitMqConfiguration;
#Override
public final <T> void send(String destination, T payload) {
rabbitTemplate.convertAndSend(WsRabbitMqConfiguration.WS_EXCHANGE_NAME, wsRabbitMqConfiguration.getRoutingKey(), payload,
message -> putDestination(message, destination));
}
protected abstract void handleWebsocketSending(T payload, String destination);
#Service
public class FooWebSocketService extends AbstractWebsocketService<Foo> {
public FooWebSocketService(RabbitTemplate rabbitTemplate,
WsRabbitMqConfiguration rabbitMqConfiguration) {
super(rabbitTemplate, rabbitMqConfiguration);
}
#RabbitListener(queues = "#{wsReplicationQueue.name}", ackMode = "NONE")
protected void handleWebsocketSending(#Payload Foo payload, #Header(HEADER_WS_DESTINATION) String destination) {
// logic here
}
}
#Service
public class BarWebSocketService extends AbstractWebsocketService<Bar> {
public BarWebSocketService(RabbitTemplate rabbitTemplate,
WsRabbitMqConfiguration rabbitMqConfiguration) {
super(rabbitTemplate, rabbitMqConfiguration);
}
#RabbitListener(queues = "#{wsReplicationQueue.name}", ackMode = "NONE")
protected void handleWebsocketSending(#Payload Bar payload, #Header(HEADER_WS_DESTINATION) String destination) {
// logic here
}
}
From another service class I want to send RMQ messages, but randomly wrong #RabbitListener is activated.
Eg.
#Service
#RequiredArgsConstructor
public class TestService {
private final BarWebSocketService barWebSocketService;
public void sendMessage(Bar bar) {
// 1st message
barWebSocketService.send("bar-destination", bar);
// 2nd message
barWebSocketService.send("bar-destination2", bar);
}
}
For the 1st message #RabbitListener in FooWebSocketService is activated (which is wrong) and for the 2nd message #RabbitListener in BarWebSocketService (which is right).
Any suggestions what I am doing wrong? Thank you!

Is there a way to send websocket message from regular method in spring

I am building web application. There are admin and user roles provided. When user making some action admin is recieving a message that something happened. Websocket connection establishing when user logged. Is there a way to not create ws connection for user and use only HHTP protocol to sending message and send WS message from controller method only?
Now i have theese settings:
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
public void configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/app");
}
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/ws").setAllowedOriginPatterns("*").withSockJS();
}
}
#Controller
public class NotificationController {
#MessageMapping("/notification")
#SendTo("/topic/test")
public Notification getNotification(Notification notification) {
return notification;
}
}
Yes it is possible.
You have to inject SimpleMessagintTemplate, with #Autowire or with constructor.
private final SimpMessagingTemplate simpMessagingTemplate;
public ConstructorName(SimpMessagingTemplate simpMessagingTemplate){
this.simpMessagingTemplate = simpMessagingTemplate;
}
In your controller, or function where you want to send the message to the client use the convertAndSendToUser function.
simpMessagingTemplate.convertAndSendToUser("userId","/private", messageData);
On javascript client side.
var Sock = new SockJS('http://localhost:8080/ws');
stompClient = over(Sock);
stompClient.connect({}, onConnected, onError);
stompClient.subscribe('/topic/' + userId + '/private', onMessageReceived);

How to send message using websocket whenever an API is called using springboot?

I have a simple controller which return name . I have websocket handler which return message to client as: Hey there, presentation recieved from user. whenever http://localhost:8080/sample is called, i need to display the above message to <ws://localhost:8080/presentation>, using https://websocketking.com/ to connect to websocket.
#RestController
public class WebController {
#RequestMapping("/sample")
public SampleResponse Sample(#RequestParam(value = "name",
defaultValue = "Robot") String name) {
SampleResponse response = new SampleResponse();
response.setId(1);
response.setMessage("Your name is "+name);
return response;
}
}
#Component
public class WebSocketHandler extends AbstractWebSocketHandler {
#Override
public void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {
System.out.println("New Text Message Received from presetation");
String payload = message.getPayload();
System.out.println(payload);
session.sendMessage(new TextMessage("Hey there, presentation recieved from user"));
}
}
public class WebSocketConfiguration implements WebSocketConfigurer {
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
registry.addHandler(new WebSocketHandler(), "/presentation").setAllowedOrigins("*");
}
}

spring websocket send messages to specific users

I am developing a Springboot application which has a websocket server to handle messages coming from different users. I need to develop this server such that a socket client can send messages to specific client. Below given is my WebSocketHandler class.
public class WebSocketHandler extends AbstractWebSocketHandler {
static Logger logger = LogManager.getLogger(WebSocketHandler.class);
#Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
String payLoad = message.getPayLoad();
session.sendMessage("message : "+payLoad+" is received!!!");
////
}
}
}
}
I prefer to use a one handler instance as given below.
public class WebSocketConfiguration implements WebSocketConfigurer {
static WebSocketHandler handler = new WebSocketHandler();
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry webSocketHandlerRegistry) {
webSocketHandlerRegistry.addHandler(handler,"/websocket");
}
}
Is there way to achieve this ? Thanks everyone.

Resources