Websockets With Angular2 and Spring Boot

In this post I try to describe how to get up and running with Spring with raw Websockets and angular2. Code for this post is located here

Websockets allow http clients to have a persistent connection with server, which allows for low latency interaction between server and client. Setting up Websocket with spring boot is super easy, thanks to spring boot. For this article I have taken the spring boot Websockets example from here, I have changed to use raw Websockets instead of SockJS.

With spring to handle request from clients via Websocket, we need implement a WebsocketHandler. The CounterHandler class in my case TextWebsocketHandler which is a subclass of WebsocketHandler . I have overridden two methods from the super class. The method afterConnectionEstablished allows us do something after connection is established and the handleTextMessage method is to do something when a message is received from client.

@Component public class CounterHandler extends TextWebsocketHandler { WebsocketSession session ; // This will send only to one client(most recently connected) public void counterIncrementedCallback ( int counter ) { System . out . println ( "Trying to send:" + counter ); if ( session != null && session . isOpen ()) { try { System . out . println ( "Now sending:" + counter ); session . sendMessage ( new TextMessage ( "{\"value\": \"" + counter + "\"}" )); } catch ( Exception e ) { e . printStackTrace (); } } else { System . out . println ( "Don't have open session to send:" + counter ); } } @Override public void afterConnectionEstablished ( WebsocketSession session ) { System . out . println ( "Connection established" ); this . session = session ; } @Override protected void handleTextMessage ( WebsocketSession session , TextMessage message ) throws Exception { if ( "CLOSE" . equalsIgnoreCase ( message . getPayload ())) { session . close (); } else { System . out . println ( "Received:" + message . getPayload ()); } } }

Now you might be wondering how do you send messages asynchronously to client. To send a message to client you need to get hold of client’s session. This is the reason I have captured the client session after client is connected. One drawback of my implementation is that only one client receives messages from server at any time. However this is easy to solve by separating the code to a new wrapper class with the websocket, so we have all the client sessions. The CounterService class periodically calls the counterIncrementedCallback method which send the counter value to the recently connected client. Simple isn’t it?

@Component public class CounterService { private AtomicInteger counter = new AtomicInteger ( 0 ); @Autowired CounterHandler counterHandler ; @Scheduled ( fixedDelay = 1000 ) public void sendCounterUpdate () { counterHandler . counterIncrementedCallback ( counter . incrementAndGet ()); } }

Websocket configuration for spring looks as below

@Configuration @EnableWebsocket @EnableScheduling public class WebsocketConfig implements WebsocketConfigurer { @Autowired CounterHandler counterHandler ; @Override public void registerWebsocketHandlers ( WebsocketHandlerRegistry registry ) { registry . addHandler ( counterHandler , "/counter" ); } }

On the client side (code in src/main/resources/static), angular2 wrapper for Websocket is copied from https://github.com/afrad/angular2-websocket and the code looks straight forward. The getDataStream method returns a Subject which our code listens to update the value of counter

subscribe ( $event ) { console . log ( "trying to subscribe to ws" ); this . ws = new $Websocket ( "ws://localhost:8088/counter" ); this . ws . send ( "Hello" ); this . ws . getDataStream (). subscribe ( res => { var count = JSON . parse ( res . data ). value ; console . log ( 'Got: ' + count ); this . counter = count ; }, function ( e ) { console . log ( 'Error: ' + e . message ); }, function () { console . log ( 'Completed' ); } ); }

Reference: * Spring Docs: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html