WebSocket with Spring Boot

Introduction

In this post I am going to talk briefly about developing a WebSocket based application using Spring Boot framework. WebSocket[1] is a full duplex protocol allows bi-directional communication. At the moment, widely using protocols such as HTTP is uni-directional and use long polling mechanisms to achieve bi-directional behavior. But using protocols such as WebSocket allows sending requests from server to client-side. However unlike HTTP, WebSocket does not form a strong application-level protocol. WebSocket sits as a thin layer on-top-of TCP protocol and allow application developers to come-up with a high-level messaging protocol design.

STOMP

Simple Text-Oriented Messaging Protocol (STOMP) is selected by the Spring framework for its WebSocket support. STOMP uses message brokers to provide bi-directional communication.

Server-side Code

Server-side code of a websocket server is as follows:


import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Controller;

@Controller
public class GreetingController {

@MessageMapping("/hello")
 @SendTo("/topic/greetings")
 public Greeting greeting(HelloMessage helloMessage) throws Exception {
 return new Greeting("Hello," + helloMessage.getName() + "!");
 }
}

In the above code @MessageMapping annotation is used to map the messages come with “/hello” in the path. @SendTo is used to specify the destination of the result. Here, HelloMessage and Greeting are bean classes which can be found in [2].

The server-side configuration is as follows:


@Configuration
@EnableWebSocketMessageBroker
public class WebsocketConfigurer extends AbstractWebSocketMessageBrokerConfigurer {

@Override
 public void configureMessageBroker(MessageBrokerRegistry registry) {
 registry.enableSimpleBroker("/topic");
 registry.setApplicationDestinationPrefixes("/app");
 }

@Override
 public void registerStompEndpoints(StompEndpointRegistry stompEndpointRegistry) {
 stompEndpointRegistry.addEndpoint("/gs-guide-websocket").withSockJS();
 }
}

WebsocketConfigurer class is annotated as the configuration class for the application. By configureMessageBroker method it enables message broker and add “/topic” as topic it holds. “/app” is used to identify messages which need to send to the controller.

In the second method, “/gs-guide-websocket” is used as a endpoint which clients can connect. So let’s move in to the client-side code. It contains 2 files, a static HTML and a JS file.

<html>
<head>
<title>Hello WebSocket</title>
	<link href="/webjars/bootstrap/css/bootstrap.min.css" rel="stylesheet">
<!--	<link href="/main.css" rel="stylesheet">-->
<script src="/webjars/jquery/jquery.min.js"></script>
    <script src="/webjars/sockjs-client/sockjs.min.js"></script>
<script src="/webjars/stomp-websocket/stomp.min.js"></script>
    <script src="/app.js"></script>
</head>
<body>
<noscript>
<h2 style="color: #ff0000">Seems your browser doesn't support Javascript! Websocket relies on Javascript being
enabled. Please enable
Javascript and reload this page!</h2>
</noscript>
<div id="main-content" class="container">
<div class="row">
<div class="col-md-6">
<form class="form-inline">
<div class="form-group">
<label for="connect">WebSocket connection:</label>
<button id="connect" class="btn btn-default" type="submit">Connect</button>
<button id="disconnect" class="btn btn-default" type="submit" disabled="disabled">Disconnect
</button>
</div>
</form>
</div>
<div class="col-md-6">
<form class="form-inline">
<div class="form-group">
<label for="name">What is your name?</label>
<input type="text" id="name" class="form-control" placeholder="Your name here...">
</div>
<button id="send" class="btn btn-default" type="submit">Send</button>
</form>
</div>
</div>
<div class="row">
<div class="col-md-12">
<table id="conversation" class="table table-striped">
<thead>
<tr>
<th>Greetings</th>
</tr>
</thead>
<tbody id="greetings"></tbody>
</table>
</div>
</div>
</div>
</body>
</html>

var stompClient = null;

function setConnected(connected) {
$("#connect").prop("disabled", connected);
$("#disconnect").prop("disabled", !connected);
if (connected) {
$("#conversation").show();
}
else {
$("#conversation").hide();
}
$("#greetings").html("");
}

function connect() {
var socket = new SockJS('/gs-guide-websocket');
stompClient = Stomp.over(socket);
stompClient.connect({}, function (frame) {
setConnected(true);
console.log('Connected: ' + frame);
stompClient.subscribe('/topic/greetings', function (greeting) {
showGreeting(JSON.parse(greeting.body).content);
});
});
}

function disconnect() {
if (stompClient != null) {
stompClient.disconnect();
}
setConnected(false);
console.log("Disconnected");
}

function sendName() {
stompClient.send("/app/hello", {}, JSON.stringify({'name': $("#name").val()}));
}

function showGreeting(message) {
$("#greetings").append("
<tr>
<td>" + message + "</td>
</tr>
");
}

$(function () {
$("form").on('submit', function (e) {
e.preventDefault();
});
$( "#connect" ).click(function() { connect(); });
$( "#disconnect" ).click(function() { disconnect(); });
$( "#send" ).click(function() { sendName(); });
});

Finally the pom file of the project is as follows:

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.4.1.RELEASE</version>
</parent>
<groupId>com.buddhima.websocket</groupId>
<artifactId>websocket-try</artifactId>
<version>1.0-SNAPSHOT</version>

<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

<dependency>
<groupId>org.webjars</groupId>
<artifactId>webjars-locator</artifactId>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>sockjs-client</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>stomp-websocket</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>bootstrap</artifactId>
<version>3.3.7</version>
</dependency>
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.1.0</version>
</dependency>
</dependencies>

<properties>
<java.version>1.8</java.version>
</properties>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

 

References

[1] WebSocket protocol RFC – https://tools.ietf.org/html/rfc6455

[2] Using WebSocket to build an interactive web application – https://spring.io/guides/gs/messaging-stomp-websocket/

[3] WebSocket Support – https://docs.spring.io/spring/docs/current/spring-framework-reference/html/websocket.html

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s