6

I code WebSocket application with help classical Spring 5 WebSocket API, i.e. without using of SockJS and STOMP. I have a problem to get all active http-sessions. I can get one current session, but how to get all active sessions? If I would use a classical Java API(JSR356), I would use a method: session.getOpenSessions() to get all opened sessions. But I can't find anything like this method in Spring 5 WebSocket API. How to get all active sessions?

//Configuration of WebSocket.

@Configuration
@EnableWebSocket
public class WebSocketConfig implements 
WebSocketConfigurer {

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
  //Map endpoint URL to handler's method.
  //Add interceptor of HTTP-handshake to copy HTTP-session's attributes to WebSocket-session's attributes.
  registry.addHandler(getWsEndpoint(), "/endpoint").addInterceptors(new HttpSessionHandshakeInterceptor());
}

@Bean
public WebSocketHandler getWsEndpoint() {
    return new WsEndpoint();
}
}

My WebSocket endpoint:

// WebSocket endpoint class. 

public class WsEndpoint extends TextWebSocketHandler {

 public WsEndpoint(){}

 public WebSocketMessage<String> wsMsg;                      

  // This method will be called after successful websocket connection.
  @Override
  public void afterConnectionEstablished(WebSocketSession session)                                     
   throws java.lang.Exception {

     wsMsg = new TextMessage(new String("Connection ok!"));        
   session.sendMessage(wsMsg);  
  }

    // This method is called if message was recieved. 
    @Override
    public void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException {       

     wsMsg = new TextMessage(new String("Message recieved!")); 
     session.sendMessage(wsMsg);
    }
}
Mikhail
  • 61
  • 1
  • 1
  • 4

2 Answers2

5

The SimpUserRegistry (DefaultSimpUserRegistry) keeps track of connected websocket users.

The method getUsers() returns all the connected users along with their sessions.

Here is a sample code:

@RestController
public class WebSocketController {

    private final SimpUserRegistry simpUserRegistry;

    public WebSocketController(SimpUserRegistry simpUserRegistry) {
        this.simpUserRegistry = simpUserRegistry;
    }

    @GetMapping("/ws/users")
    public List<String> connectedEquipments() {
        return this.simpUserRegistry
                .getUsers()
                .stream()
                .map(SimpUser::getName)
                .collect(Collectors.toList());
    }
}
Florian Lopes
  • 1,093
  • 1
  • 13
  • 20
  • 5
    Doesn't look like this is working without using STOMP? getUsers() is empty in my case. – Peter Lustig Sep 29 '20 at 01:35
  • That doesn't answer the original question. You are adding a Controller class here, which is completely irrelevant to the requirement asked by @Mikhail. I have the same problem. My service would have supposed to have a communication among all the connected websocket sessions, but the way Spring websocket library works, it leaves me with isolated singular websocket objects. (I'm thinking of using some message queue such as JMS maybe in order to achieve it) – Majid May 02 '21 at 10:17
  • As I said, it is a sample code. You could also simply inject the `SimpUserRegistry` into a service. – Florian Lopes May 02 '21 at 13:13
  • @FlorianLopes For me injecting `SimpUserRegistry` throws error. If you can take a look into this question https://stackoverflow.com/questions/67471616/simpuserregistry-doesnot-contain-any-session-objects. – pacman May 10 '21 at 17:32
2

If you do not want to use STOMP, you can simply keep track of open sessions by yourself like this:

public class Server extends TextWebSocketHandler {

    private final Map<String, WebSocketSession> idToActiveSession = new HashMap<>();

    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        idToActiveSession.put(session.getId(), session);
        super.afterConnectionEstablished(session);
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
        String payload = message.getPayload();
        for (Map.Entry<String, WebSocketSession> otherSession : idToActiveSession.entrySet()) {
            if (otherSession.getKey().equals(session.getId())) continue;
            otherSession.getValue().sendMessage(new TextMessage(payload));
        }
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
        idToActiveSession.remove(session.getId());
        super.afterConnectionClosed(session, status);
    }
}

It works like this:

  • We get notified in afterConnectionEstablished about new sessions and store them in a map
  • When new messages arrive you can use those stored sessions in handleTextMessage
  • Remove sessions from our map in afterConnectionClosed

Keep in mind, that Server has to be a singleton. You can use @Component to let spring manage a single instance of this class.

slartidan
  • 20,403
  • 15
  • 83
  • 131