Linking a user to a session

We are still missing a critical ingredient--linking the username entered to the user's WebSocket session.

Since every one of our WebSocketHandler services we built may need access to this user data, we should build a shim called UserParsingHandshakeHandler to slip in like this:

    abstract class UserParsingHandshakeHandler 
      implements WebSocketHandler { 
 
        private final Map<String, String> userMap; 
 
        UserParsingHandshakeHandler() { 
          this.userMap = new HashMap<>(); 
        } 
 
        @Override 
        public final Mono<Void> handle(WebSocketSession session) { 
 
          this.userMap.put(session.getId(), 
           Stream.of(session.getHandshakeInfo().getUri() 
            .getQuery().split("&")) 
            .map(s -> s.split("=")) 
            .filter(strings -> strings[0].equals("user")) 
            .findFirst() 
            .map(strings -> strings[1]) 
            .orElse("")); 
 
          return handleInternal(session); 
        } 
 
        abstract protected Mono<Void> handleInternal( 
          WebSocketSession session); 
 
        String getUser(String id) { 
          return userMap.get(id); 
        } 
    } 

The previous code can be described as follows:

  • This abstract class implements WebSocketHandler; it will be invoked when a new WebSocketSession is created
  • It contains a mapping between session ID and username, called userMap, initialized in the constructor
  • The implementation of handle(WebSocketSession) takes the userMap and puts a new entry keyed off the session's ID
  • The value stored under that session ID is extracted from the session's handshake, granting access to the original URI
  • With some Java 8 stream magic, we can extract the query string from this URI, and find the user argument
  • findFirst() produces an Optional, so we can either map over the answer or fall back to an empty string (no user)
  • Having loaded the userMap, we then invoke the concrete subclass through a custom abstract method, handleInternal(WebSocketMessage)
  • To facilitate looking up the current username, getUser(String) is provided to look up user based on session ID

This chunk of code will handle user details, allowing each concrete WebSocketHandler to do its thing while also having access to the current session's username.

To use this new handshake handler, we need to update the InboundChatService like this:

    @Service 
    @EnableBinding(ChatServiceStreams.class) 
    public class InboundChatService extends UserParsingHandshakeHandler 
{ private final ChatServiceStreams chatServiceStreams; public InboundChatService(ChatServiceStreams chatServiceStreams){ this.chatServiceStreams = chatServiceStreams; } @Override protected Mono<Void> handleInternal(WebSocketSession session) { return session .receive() .log(getUser(session.getId()) + "-inbound-incoming-chat-message") .map(WebSocketMessage::getPayloadAsText) .log(getUser(session.getId()) + "-inbound-convert-to-text") .flatMap(message -> broadcast(message, getUser(session.getId()))) .log(getUser(session.getId()) + "-inbound-broadcast-to-broker") .then(); } public Mono<?> broadcast(String message, String user) { return Mono.fromRunnable(() -> { chatServiceStreams.clientToBroker().send( MessageBuilder .withPayload(message) .setHeader(ChatServiceStreams.USER_HEADER, user) .build()); }); } }

It's almost the same as what we coded earlier in this chapter, with a few key differences:

  • It now extends UserParsingHandshakeHandler instead of WebSocketHandler.
  • Instead of implementing handle(WebSocketSession), we must now write handleInternal(WebSocketSession). This is a classic pattern of using a parent abstract class to intercept and then delegate.
  • broadcast() takes two arguments--message and user. The user field is populated using getUser(session.getId()).
  • broadcast() builds a Message like it did earlier in this chapter, but also adds a custom header containing the user of the creator of the message.
Part of the power of the Message API are headers. You can use standard headers as well as make up your own to suit your needs. In this case, we mark up every message with the originator. Other useful details could include the timestamp of creation and origin address. Really, anything.
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.117.187.62