title: Spring+WebSocket+STOMP. Message to specific session (NOT user)
body: <p>I am trying to set up basic message broker on Spring framework, using a recipe I found <a href="https://stackoverflow.com/questions/34929578/spring-websocket-sendtosession-send-message-to-specific-session">here</a></p>
  
  <p>Author claims it has worked well, but I am unable to receive messages on client, though no visible errors were found.</p>
  
  <p><strong>Goal:</strong></p>
  
  <p>What I am trying to do is basically the same - a client connects to server and requests some async operation. After operation completes the client should receive an event. Important note: client is not authenticated by Spring, but an event from async back-end part of the message broker contains his login, so I assumed it would be enough to store concurrent map of Login-SessionId pairs for sending messages directly to particular session.</p>
  
  <p><strong>Client code:</strong></p>
  
  <pre><code>//app.js
  
  var stompClient = null;
  var subscription = '/user/queue/response';
  
  //invoked after I hit "connect" button
  function connect() {
  //reading from input text form
  var agentId = $("#agentId").val();
  
  var socket = new SockJS('localhost:5555/cti');
  stompClient = Stomp.over(socket);
  stompClient.connect({'Login':agentId}, function (frame) {
      setConnected(true);
      console.log('Connected to subscription');
      stompClient.subscribe(subscription, function (response) {
          console.log(response);
      });
  });
  
  }
  
  //invoked after I hit "send" button
  function send() {
  
  var cmd_str = $("#cmd").val();
  var cmd = {
      'command':cmd_str
  };
  console.log("sending message...");
  stompClient.send("/app/request", {}, JSON.stringify(cmd));
  console.log("message sent");
  }
  </code></pre>
  
  <p>Here is my configuration.</p>
  
  <pre><code>//message broker configuration
  
  @Configuration
  @EnableWebSocketMessageBroker
  public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer{
  
  
  @Override
  public void configureMessageBroker(MessageBrokerRegistry config) {
      /** queue prefix for SUBSCRIPTION (FROM server to CLIENT)  */
      config.enableSimpleBroker("/topic");
      /** queue prefix for SENDING messages (FROM client TO server) */
      config.setApplicationDestinationPrefixes("/app");
  }
  
  
  @Override
  public void registerStompEndpoints(StompEndpointRegistry registry) {
  
      registry
              .addEndpoint("/cti")
              .setAllowedOrigins("*")
              .withSockJS();
  }
  
  }
  </code></pre>
  
  <p>Now, after basic config I should implement an application event handler to provide session-related information on client connect.</p>
  
  <pre><code>//application listener
  
  @Service
  public class STOMPConnectEventListener implements ApplicationListener&lt;SessionConnectEvent&gt; {
  
  @Autowired
  //this is basically a concurrent map for storing pairs "sessionId - login"
  WebAgentSessionRegistry webAgentSessionRegistry;
  
  @Override
  public void onApplicationEvent(SessionConnectEvent event) {
      StompHeaderAccessor sha = StompHeaderAccessor.wrap(event.getMessage());
  
      String agentId = sha.getNativeHeader("Login").get(0);
      String sessionId = sha.getSessionId();
  
      /** add new session to registry */
      webAgentSessionRegistry.addSession(agentId,sessionId);
  
      //debug: show connected to stdout
      webAgentSessionRegistry.show();
  
  }
  }
  </code></pre>
  
  <p>All good so far. After I run my spring webapp in IDE and connected my "clients" from two browser tabs I got this in IDE console:</p>
  
  <pre><code>session_id / agent_id
  -----------------------------
  |kecpp1vt|user1|
  |10g5e10n|user2|
  -----------------------------
  </code></pre>
  
  <p>Okay, now let's try to implement message mechanics. </p>
  
  <pre><code>//STOMPController
  
  
  @Controller
  public class STOMPController {
  
  @Autowired
  //our registry we have already set up earlier
  WebAgentSessionRegistry webAgentSessionRegistry;
  @Autowired
  //a helper service which I will post below
  MessageSender sender;
  
  @MessageMapping("/request")
  public void handleRequestMessage() throws InterruptedException {
  
      Map&lt;String,String&gt; params = new HashMap(1);
      params.put("test","test");
      //a custom object for event, not really relevant
      EventMessage msg = new EventMessage("TEST",params);
  
      //send to user2 (just for the sake of it)
      String s_id = webAgentSessionRegistry.getSessionId("user2");
      System.out.println("Sending message to user2. Target session: "+s_id);
      sender.sendEventToClient(msg,s_id);
      System.out.println("Message sent");
  
  }
  }
  </code></pre>
  
  <p>A service to send messages from any part of the application:</p>
  
  <pre><code>//MessageSender
  
  @Service
  public class MessageSender implements IMessageSender{
  
  @Autowired
  WebAgentSessionRegistry webAgentSessionRegistry;
  @Autowired
  SimpMessageSendingOperations messageTemplate;
  
  private String qName = "/queue/response";
  
  private MessageHeaders createHeaders(String sessionId) {
      SimpMessageHeaderAccessor headerAccessor = SimpMessageHeaderAccessor.create(SimpMessageType.MESSAGE);
      headerAccessor.setSessionId(sessionId);
      headerAccessor.setLeaveMutable(true);
      return headerAccessor.getMessageHeaders();
  }
  
  @Override
  public void sendEventToClient(EventMessage event,String sessionId) {
      messageTemplate.convertAndSendToUser(sessionId,qName,event,createHeaders(sessionId));
  }
  }
  </code></pre>
  
  <p>Now, let's try to test it. I run my IDE, opened Chrome and created 2 tabs form which I connected to server. User1 and User2. Result console:</p>
  
  <pre><code> session_id / agent_id
      -----------------------------
      |kecpp1vt|user1|
      |10g5e10n|user2|
      -----------------------------
  Sending message to user2. Target session: 10g5e10n
  Message sent
  </code></pre>
  
  <p>But, as I mentioned in the beginning - user2 got absolutely nothing, though he is connected and subscribed to "/user/queue/response". No errors either.</p>
  
  <p><strong>A question</strong> is, where exactly I am missing the point? I have read many articles on the subject, but to no avail.
  <a href="https://jira.spring.io/browse/SPR-11309" rel="nofollow noreferrer">SPR-11309</a> says it's possible and should work. Maybe, id-s aren't actual session id-s?
  And well maybe someone knows how to monitor if the message actually has been sent, not dropped by internal Spring mechanics?</p>
  
  <h2><strong>SOLUTION UPDATE:</strong></h2>
  
  <p>A misconfigured bit:</p>
  
  <pre><code>//WebSocketConfig.java:
  ....
   @Override
  public void configureMessageBroker(MessageBrokerRegistry config) {
      /** queue prefix for SUBSCRIPTION (FROM server to CLIENT)  */
      // + parameter "/queue"
      config.enableSimpleBroker("/topic","/queue");
      /** queue prefix for SENDING messages (FROM client TO server) */
      config.setApplicationDestinationPrefixes("/app");
  }
  ....
  </code></pre>
  
  <p>I've spent a day debugging internal spring mechanics to find out where exactly it goes wrong:</p>
  
  <pre><code>//AbstractBrokerMessageHandler.java: 
  ....
  protected boolean checkDestinationPrefix(String destination) {
      if ((destination == null) || CollectionUtils.isEmpty(this.destinationPrefixes)) {
          return true;
      }
      for (String prefix : this.destinationPrefixes) {
          if (destination.startsWith(prefix)) {
  //guess what? this.destinationPrefixes contains only "/topic". Surprise, surprise
              return true;
          }
      }
      return false;
  }
  ....
  </code></pre>
  
  <p>Although I have to admit I still think the documentation mentioned that user personal queues aren't to be configured explicitly cause they "already there". Maybe I just got it wrong.</p>