C2B2 logo icon

Adding the capabilities of WebSockets to your Spring application deployed on Tomcat 8.x

Supported in all major browsers, this powerful full-duplex TCP-based protocol is the perfect solution to implement a real-time data transfer from and to the server. Follow this simple example to explore the Spring 4 WebSocket API.

A customer we support wanted to implement a fast and reliable client-server communication mechanism between a server and the web browser, without resorting to HTTP - and was interested in wexploring the capabilities of WebSocket.

In fact, HTTP wasn't built to guarantee this sort of interactivity - let’s just think to the overhead introduced by its headers!

However, WebSocket provides an alternative to this (and other HTTP limitations) by providing bidirectional, full-duplex, real-time, client/server communications - meaning that the server can send data to the client at any time.

Because WebSocket runs over TCP, it not only reduces the overhead of each message, but provides greater scalability for message-intensive applications, because only one connection per client is used (whereas HTTP creates one request per message).

So, to explore further, let's deploy a simple application based on Spring 4 WebSocket API on Tomcat 8, using a STOMP client to communicate with the server. The application in question simply outputs a greeting in response to the user input.

To build and run it you will need the following installed on your machine:

  • JDK 8 
  • Gradle 4 
  • Apache Tomcat 8.x

Let’s have a look at the code. The uk.co.c2b2.websocket.config package contains the following classes:

  • ContextConfig
    An alternative to web.xml to configure and initialize our Spring web applicatio
     
  • MessageBrokerConfig
    Contains the @EnableWebSocketMessageBroker annotation, which, added to a class already annotated with the @Configuration annotation enables broker-backed messaging over WebSocket using a higher-level messaging sub-protocol.

    The MessageBrokerRegistry component is responsible for registering a message broker, in our example we enabled the topic broker and a destination name used by client and server to communicate over WebSockets, greetingApp. The StompEndpointRegistry component is responsible for registering a STOMP endpoint and for enabling SockJS . Our messages will be  delivered via the URL /greetingApp/greet.
package uk.co.c2b2.websocket.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
@Configuration
@EnableWebSocketMessageBroker
public class MessageBrokerConfig extends AbstractWebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry messageBrokerRegistry) {
messageBrokerRegistry.enableSimpleBroker("/topic");
messageBrokerRegistry.setApplicationDestinationPrefixes("/greetingApp");
}

@Override
public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
stompEndpointRegistry.addEndpoint("/greet").withSockJS();
}
}
  • WebConfig
    A handler to delegate unhandled requests by forwarding to the Servlet container's "default" servlet

The uk.co.c2b2.websocket.controller package contains the application controller, consisting in two methods, greet() and start().

greet(), annotated with @RequestMapping, starts the conversation between client and server over HTTP, while start(), annotated with @MessageMapping and @SendTo, takes care of the communication over web sockets: the target URL of the response is /topic/greeting 

package uk.co.c2b2.websocket.controller;
import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import uk.co.c2b2.websocket.beans.Name;
import uk.co.c2b2.websocket.beans.Result;
@Controller
public class AppController {
@MessageMapping("/greet" )
@SendTo("/topic/greeting")
public Result greet(Name input) throws Exception {
Result result = new Result("Welcome " + input.getName() + "!"); 
return result;
}
    
@RequestMapping("/start")
public String start() {
return "start";
}
}

The client module consist in an HTML document, it allows the user to connect and retrieve a greeting from the server. The communication over WebSockets is made possible by the use of SockJS, a 
JavaScript library (for browsers) that provides a WebSocket-like object: it gives you a coherent, cross-browser, Javascript API which creates a low latency, full duplex, cross-domain communication channel between the browser and the web server, with WebSockets or without. At the time this article is written the latest version of SockJS is 1.1.4.

Data are transferred between client and server through two simple beans, Name.java and Result.java.
Following the Gradle build file (note the Spring Boot Tomcat runtime among the dependencies);
version 2 and 4 of Gradle are supported.

To build and run the application :

  1. Download the source code here, navigate to the root folder and run gradle clean build
  2. Deploy the generated WAR file on Apache Tomcat and start the server
  3. Navigate to http://localhost:8080/SimpleWebSocketExample/start
  4. Click on connect.
  5. Enter your name in the text field
  6. Click on Send and there you go!

Following the output in the Tomcat console:

Summary

We explored the Spring Framework WebSocket API, which is designed to adapt to various WebSocket engines. Currently the list includes WebSocket runtimes such as Tomcat 7.0.47+, Jetty 9.1+, GlassFish 4.1+, WebLogic 12.1.3+, and WildFly 8.0+.

It could be interesting for the user to explore the WebSocket example applications which comes embedded within the Apache Tomcat distribution, to have a look how the same results were achieved without using the Spring Framework.