Path variables in Spring WebSockets @SendTo mapping

JavaSpringWebsocketStompSockjs

Java Problem Overview


I have, what I think to be, a very simple Spring WebSocket application. However, I'm trying to use path variables for the subscription as well as the message mapping.

I've posted a paraphrased example below. I would expect the @SendTo annotation to return back to the subscribers based on their fleetId. ie, a POST to /fleet/MyFleet/driver/MyDriver should notify subscribers of /fleet/MyFleet, but I'm not seeing this behavior.

It's worth noting that subscribing to literal /fleet/{fleetId} works. Is this intended? Am I missing some piece of configuration? Or is this just not how it works?

I'm not very familiar with WebSockets or this Spring project yet, so thanks in advance.

Controller.java

...
@MessageMapping("/fleet/{fleetId}/driver/{driverId}")
@SendTo("/topic/fleet/{fleetId}")
public Simple simple(@DestinationVariable String fleetId, @DestinationVariable String driverId) {
	return new Simple(fleetId, driverId);
}
...

WebSocketConfig.java

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
	@Override
	public void configureMessageBroker(MessageBrokerRegistry config) {
		config.enableSimpleBroker("/topic");
		config.setApplicationDestinationPrefixes("/live");
	}

	@Override
	public void registerStompEndpoints(StompEndpointRegistry registry) {
		registry.addEndpoint("/fleet").withSockJS();
	}
}

index.html

var socket = new SockJS('/fleet');
var stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
    // Doesn't Work
    stompClient.subscribe('/topic/fleet/MyFleet', function(greeting) {
    // Works
	stompClient.subscribe('/topic/fleet/{fleetId}', function(greeting) {
		// Do some stuff
	});
});

Send Sample

	stompClient.send("/live/fleet/MyFleet/driver/MyDriver", {}, JSON.stringify({
		// Some simple content
	}));

Java Solutions


Solution 1 - Java

Even though @MessageMapping supports placeholders, they are not exposed / resolved in @SendTo destinations. Currently, there's no way to define dynamic destinations with the @SendTo annotation (see issue SPR-12170). You could use the SimpMessagingTemplate for the time being (that's how it works internally anyway). Here's how you would do it:

@MessageMapping("/fleet/{fleetId}/driver/{driverId}")
public void simple(@DestinationVariable String fleetId, @DestinationVariable String driverId) {
    simpMessagingTemplate.convertAndSend("/topic/fleet/" + fleetId, new Simple(fleetId, driverId));
}

In your code, the destination '/topic/fleet/{fleetId}' is treated as a literal, that's the reason why subscribing to it works, just because you are sending to the exact same destination.

If you just want to send some initial user specific data, you could return it directly in the subscription:

@SubscribeMapping("/fleet/{fleetId}/driver/{driverId}")
public Simple simple(@DestinationVariable String fleetId, @DestinationVariable String driverId) {
    return new Simple(fleetId, driverId);
}

Update: In Spring 4.2, destination variable placeholders are supported it's now possible to do something like:

@MessageMapping("/fleet/{fleetId}/driver/{driverId}")
@SendTo("/topic/fleet/{fleetId}")
public Simple simple(@DestinationVariable String fleetId, @DestinationVariable String driverId) {
    return new Simple(fleetId, driverId);
}

Solution 2 - Java

Actually I think this is what you might be looking for:

@Autorwired
lateinit var template: SimpMessageTemplate;

@MessageMapping("/class/{id}")
@Throws(Exception::class)
fun onOffer(@DestinationVariable("id") id: String?, @Payload msg: Message) {
    println("RECEIVED " + id)
    template.convertAndSend("/topic/class/$id", Message("The response"))
}

Hope this helps someone! :)

Solution 3 - Java

you can send a variable inside the path. for example i send "este/es/el/chat/java/" and obtaned in the server as "este:es:el:chat:java:"

client:

stompSession.send("/app/chat/este/es/el/chat/java/*", ...);

server:

@MessageMapping("/chat/**")
@SendToUser("/queue/reply")
public WebsocketData greeting(Message m,HelloMessage message,@Header("simpSessionId") String sessionId) throws Exception {
	Map<String, LinkedList<String>> nativeHeaders = (Map<String, LinkedList<String>>) m.getHeaders().get("nativeHeaders");
	String value= nativeHeaders.get("destination").getFirst().replaceAll("/app/chat/","").replaceAll("/",":");
	

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionbvulajView Question on Stackoverflow
Solution 1 - JavaSergi AlmarView Answer on Stackoverflow
Solution 2 - JavaJoão RodriguesView Answer on Stackoverflow
Solution 3 - Javaivan rcView Answer on Stackoverflow