A Java-based WebSocket Client in Spring

A Java-based WebSocket Client in Spring

In my previous post, we saw how to implement a real-time notification system using WebSockets. But if you are like me, you know that’s only half of the journey. Truly, we need to write tests for that code! For that purpose, this post will examine how to write a Java-based WebSocket client in Spring Boot.

The Spring framework provides what we need to implement a WebSocket client in Java. In order to keep up with the previously-described notification system, we will implement a STOMP client on top SockJs.

Build a Java WebSocket Client in Spring

With this in mind, let’s start by initiating a STOMP session:

WebSocketClient webSocketClient = new StandardWebSocketClient();
SockJsClient sockJsClient = new SockJsClient(listOf(new WebSocketTransport(webSocketClient)));
WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);
stompClient.setMessageConverter(new MappingJackson2MessageConverter());

StompSession session;
try {
    session = stompClient
            .connect(
                "http://localhost:" + testServerPort + "/notifications", 
                 new StompSessionHandlerAdapter() {})
            .get();
} catch (InterruptedException | ExecutionException e) {
    throw new RuntimeException(e);
}

As you can see, you start by building a simple WebSocket client. Then you use it to build a WebSocket transport, which helps building a SockJs client. Finally, you pass the SockJs client instance to build a Stomp client.

Pay special attention to the STOMP client configuration. Notably, we know we’ll exchange JSON messages. Thus we must provide an adequate message converter to the client, such as one from Jackson.

Also, look closely to the URL we use for connecting our client. Where does that testServerPort variable come from? In this case, Spring Boot provides that value. Indeed, I run my tests using the @SpringBootTest annotation. In addition, I specify the annotation’s webEnvironment parameter to start a web server with a random port. So to retrieve that random port I do as follows:

@LocalServerPort
protected int testServerPort;

Of course, you don’t need to use Spring Boot. So you can replace that with your own test server port as needed.

Now stomp on it!

Now that we have a STOMP session, we can do exactly the same things we did with the JavaScript or Angular client.

For instance, we can send a message to the back end like this:

session.send("/swns/start", null);

This amounts to the Angular code:

this.client.publish({destination: '/swns/start'});

Our front end client did not need a payload, hence the null parameter in Java. But if you need to send a payload, just proceed as follows:

session.send("/swns/start", javaObject);

Since we registered a Jackson message converter for our Java client, the specified javaObject will be converted to JSON. Easy!

What about listening to what the server sends you? For instance, in our Angular client we subscribed for any message addressed to us like this:

let notifications: string[] = [];

...

this.client.watch('/user/notification/item')
  .pipe(
    map((response) => {
      const text: string = JSON.parse(response.body).text;
      console.log('Got ' + text);
      return text;
    }))
  .subscribe((notification: string) => this.notifications.push(notification));

Similarly, we can subscribe to server messages with our Java client like this:

session.subscribe("/user/notification/item", new StompFrameHandler() {
    @Override
    public Type getPayloadType(StompHeaders headers) {
        return Notification.class;
    }
    @Override
    public void handleFrame(StompHeaders headers, Object payload) {
        System.out.println((Notification) payload);
    }
});

The StompFrameHandler handles messages that match the specified destination. We override two methods. First, we indicate the type of object that maps to the received payload. In this case, we use the same DTO the server uses to send us messages: Notification. Second, we tell the handler what it must do with the received payload. Truly, we don’t do a lot in this example, but… you get the point right?

Conclusion

So we now can build a Java-based WebSocket client in Spring. It wasn’t that hard, was it?

Of course, we can and should use it to test our server code. However such a client can come in handy in many other situations!

Cheers!

[Photo by Markus Spiske from Pexels]

Leave A Comment

Please be polite. We appreciate that. Your email address will not be published and required fields are marked

This site uses Akismet to reduce spam. Learn how your comment data is processed.