4

I'm trying to apply a destination variable to a method in my controller that handles incoming messages from WebSocket. Here's what I want to achieve:

@Controller
public class DocumentWebsocketController {

    @MessageMapping("/lock-document")
    @SendTo("/notify-open-documents/{id}")
    public Response response(@DestinationVariable("id") Long id, Message message) {
        return new Response(message.getDocumentId());
    }
}

The problem is, that the destination variable is applied only to @SendTo annotation. It causes following stack trace while attempting this endpoint:

12:36:43.044 [clientInboundChannel-7] ERROR org.springframework.web.socket.messaging.WebSocketAnnotationMethodMessageHandler - Unhandled exception
org.springframework.messaging.MessageHandlingException: Missing path template variable 'id' for method parameter type [class java.lang.Long]
    at org.springframework.messaging.handler.annotation.support.DestinationVariableMethodArgumentResolver.handleMissingValue(DestinationVariableMethodArgumentResolver.java:70) ~[spring-messaging-4.2.4.RELEASE.jar:4.2.4.RELEASE]
    at org.springframework.messaging.handler.annotation.support.AbstractNamedValueMethodArgumentResolver.resolveArgument(AbstractNamedValueMethodArgumentResolver.java:96) ~[spring-messaging-4.2.4.RELEASE.jar:4.2.4.RELEASE]

(...)
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624) [?:1.8.0_144]
        at java.lang.Thread.run(Thread.java:748) [?:1.8.0_144]

My question is: is something like I want to achieve possible at all?

pidabrow
  • 966
  • 1
  • 21
  • 47
  • Possible duplicate of [Path variables in Spring WebSockets @SendTo mapping](https://stackoverflow.com/questions/27047310/path-variables-in-spring-websockets-sendto-mapping) – Sergi Almar Apr 06 '18 at 11:13
  • I don't see it as a different case, it's just you are trying to use `@DestinationVariable` in the wrong context. `@DestinationVariable` can only be used for placeholders in `@MessageMapping` / `@SubscribeMapping` not in `@SendTo`. The error is complaining you want to get something called "id" but you don't define it in your destination. In order to have a dynamic return destination with different placeholders as the original destination you'll have to use the MessagingTemplate. – Sergi Almar Apr 06 '18 at 16:14

2 Answers2

4

This should be possible. I am referring to the following answer: Path variables in Spring WebSockets @SendTo mapping

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);
}
rieckpil
  • 10,470
  • 3
  • 32
  • 56
4

The error you get is telling you there's no placeholder called id in your destination (defined in your @MessageMapping). @DestinationVariable tries to get the variable from the incoming destination, it's not binding to the outgoing destination as you seem to try. However, you can use the same placeholders from the destination in @MessageMappinginside the @SendTo (but that's not your case).

If you want to have a dynamic destination, use the MessagingTemplate like:

@MessageMapping("/lock-document")
public void response(Message message) {
    simpMessagingTemplate.convertAndSend("/notify-open-documents/" + message.getDocumentId(), new Response(message.getDocumentId());
}
Sergi Almar
  • 8,054
  • 3
  • 32
  • 30