Introduction

WebSocket is a very thin, lightweight layer above TCP used to build interactive web applications that send messages back and forth between a browser and the server.

The best examples are live updates websites, where once user access the website neither user nor browser sends request to the server to get the latest updates. Server only keeps sending the messages to the browser.

For more information on WebSocket please read https://en.wikipedia.org/wiki/WebSocket

In this example, I am building a WebSocket application with Spring Boot using STOMP Messaging to provide automatic updates on the greeting message depending on the time of the day for every 3 secs.

Server Application

Prerequisites

Java at least version 8 needs to be installed and configured
Gradle plugin needs to be installed into Eclipse
Gradle 4.x needs to installed and configured
Dependencies : Spring boot, slf4j, websocket dependency

Creating and setting up Gradle project

Create gradle project called SpringBootWebSocketAngularJSGradle using the following gradle dependencies

buildscript {
	ext {
		springBootVersion = '1.5.9.RELEASE'
	}

    repositories {
        mavenCentral()
    }

    dependencies {
        classpath "org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}"
    }

}

apply plugin: 'java'
apply plugin: 'org.springframework.boot'

repositories {
    mavenCentral()
}

sourceCompatibility = 1.8
targetCompatibility = 1.8

configurations {
    all*.exclude module : 'spring-boot-starter-logging'
}

dependencies {
	compile "org.springframework.boot:spring-boot-starter-websocket:${springBootVersion}"
	compile "org.slf4j:slf4j-api:1.7.21"
	compile "org.slf4j:slf4j-log4j12:1.7.21"
	compile "commons-logging:commons-logging:1.2"
}

jar {
	baseName = 'SpringBootWebSocket'
	version = '0.0.1-SNAPSHOT'
    manifest {
        attributes("Main-Class": "com.jeejava.main.Application")
    }
}

When referring to build directories, the tutorial assumes the standard Gradle build paths for your gradle project:

SpringBootWebSocketAngularJSGradle/src/main/java – Java source directory
SpringBootWebSocketAngularJSGradle/src/main/resources – Resource directory
SpringBootWebSocketAngularJSGradle/src/test/java – Java test directory
SpringBootWebSocketAngularJSGradle/src/test/resources – Resource test directory

Create main class

package com.jeejava.main;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication(scanBasePackages = "com.jeejava")
public class Application {

	public static void main(String[] args) {
		SpringApplication.run(Application.class, args);
	}
}

 

You should be able to build the blank project. Please ensure that the overall state is “BUILD SUCCESS” before continuing.

Note: You won’t be able to import Spring Boot dependencies until your project downloads all dependencies. So first create main class with empty main method and later when your project is successfully built then you can import required dependencies.

Execute command – gradle clean build on the project root directory from cmd prompt.

You will see the required jar files get downloaded and finally you would get “BUILD SUCCESSFUL” message.

Create loggers

Put below log4j.properties file under classpath resource SpringBootWebSocketAngularJSGradle/src/main/resources

log4j.rootLogger=INFO, ACT

log4j.appender.ACT=org.apache.log4j.ConsoleAppender
log4j.appender.ACT.layout=org.apache.log4j.PatternLayout
log4j.appender.ACT.layout.ConversionPattern= %d{hh:mm:ss,SSS} [%t] %-5p %c %x - %m%n

Change port

We don’t want tomcat server in spring boot application to be started on random port, so set the server port in SpringBootWebSocketAngularJSGradle/src/main/resources/application.properties

server.port=9999

Create model class Message.java

package com.jeejava.model;

public class Message {
	private String msg;

	public Message(String msg) {
		this.msg = msg;
	}

	public String getMsg() {
		return msg;
	}

	public void setMsg(String msg) {
		this.msg = msg;
	}
}

 

Create WebSocket configuration class

Configure Spring to enable WebSocket and STOMP messaging.

package com.jeejava.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 WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {

	@Override
	public void registerStompEndpoints(StompEndpointRegistry registry) {
		registry.addEndpoint("/websocket").setAllowedOrigins("*").withSockJS();
	}

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

 

WebSocketConfig is annotated with @Configuration to indicate that it is a Spring configuration class. It is also annotated @EnableWebSocketMessageBroker. @EnableWebSocketMessageBroker enables WebSocket message handling, backed by a message broker.

The configureMessageBroker() method overrides the default method in WebSocketMessageBrokerConfigurer to configure the message broker. It starts by calling enableSimpleBroker() to enable a simple memory-based message broker to carry the greeting messages back to the client on destinations prefixed with “/topic”. It also designates the “/app” prefix for messages that are bound for @MessageMapping annotated methods. This prefix will be used to define all the message mappings; for example, “/app/hello” is the endpoint that the GreetingController.greeting() method is mapped to handle.

The registerStompEndpoints() method registers the “/websocket” endpoint, enabling SockJS fallback options so that alternate transports may be used if WebSocket is not available. The SockJS client will attempt to connect to “/websocket” and use the best transport available (websocket, xhr-streaming, xhr-polling, etc).

Create service class

This class will generate different greet messages depending upon the time of the day. I have also appended random generated string so that we will get different random string appended with actual greet message to differentiate from each other every 3 seconds. and it will actually tell us that we are getting every time the new message.

package com.jeejava.service;

import java.util.Calendar;
import java.util.UUID;

import org.springframework.stereotype.Service;

import com.jeejava.model.Message;

@Service
public class GreetingService {

	public Message getMessage() {
		Calendar c = Calendar.getInstance();
		int timeOfDay = c.get(Calendar.HOUR_OF_DAY);
		StringBuilder sb = new StringBuilder();
		String message = "Have a Good Day";
		if (timeOfDay >= 0 && timeOfDay < 12) {
			message = "Good Morning";
		} else if (timeOfDay >= 12 && timeOfDay < 16) {
			message = "Good Afternoon";
		} else if (timeOfDay >= 16 && timeOfDay < 21) {
			message = "Good Evening";
		} else if (timeOfDay >= 21 && timeOfDay < 24) {
			message = "Good Night";
		}

		sb.append(message).append(" - ").append(generateString());
		return new Message(sb.toString());

	}

	private String generateString() {
		String uuid = UUID.randomUUID().toString();
		return uuid;
	}
}

 

Create controller class

In Spring’s approach to working with STOMP messaging, STOMP messages can be routed to @Controller classes. For example the GreetingController is mapped to handle messages to destination “/hello”.

package com.jeejava.controller;

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

import com.jeejava.model.Message;
import com.jeejava.service.GreetingService;

@Controller
public class GreetingController {

	@Autowired
	private GreetingService greetingService;

	@MessageMapping("/hello")
	@SendTo("/topic/greeting")
	public Message greeting() {
		return greetingService.getMessage();
	}
}

 

The @MessageMapping annotation ensures that if a message is sent to destination “/hello”, then the greeting() method is called.

Create scheduler

Create scheduler to push updates every 3 seconds to client

package com.jeejava.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;

import com.jeejava.service.GreetingService;

@Configuration
@EnableScheduling
public class GreetingSchedulerConfig {

	private static final Logger LOGGER = LoggerFactory.getLogger(GreetingSchedulerConfig.class);

	@Autowired
	private SimpMessagingTemplate template;
	@Autowired
	private GreetingService greetingService;

	@Scheduled(fixedRate = 3000)
	public void publishUpdates() {
		LOGGER.info("Message: " + greetingService.getMessage().getMsg());
		template.convertAndSend("/topic/greeting", greetingService.getMessage());
	}
}

 

Running the application

Run the main class Application.java file.

Console output

INFO com.jeejava.config.GreetingSchedulerConfig - Message: Good Afternoon - e6947953-68ef-46b2-b365-1b6455d2e6a4
INFO com.jeejava.config.GreetingSchedulerConfig - Message: Good Afternoon - 1c6b8e9b-aa10-4334-a811-656e1f13c47d
INFO com.jeejava.config.GreetingSchedulerConfig - Message: Good Afternoon - 260b61cb-99a8-48cf-bd3b-59345f8c6b8c
INFO com.jeejava.config.GreetingSchedulerConfig - Message: Good Afternoon - 33011df6-482d-4edd-bcab-83da6e8a982a
INFO com.jeejava.config.GreetingSchedulerConfig - Message: Good Afternoon - c5c69c85-711a-42a3-974f-0fcd96154922
INFO com.jeejava.config.GreetingSchedulerConfig - Message: Good Afternoon - c4282753-adb4-4f91-99f8-368f3a73faff
...

If you hit the URL http://localhost:9999/websocket in browser then you will see below message

Welcome to SockJS!

Client Application

With the server side pieces in place, now let’s turn our attention to the client that will send messages to and receive messages from the server side.

This HTML file imports the SockJS and STOMP javascript libraries that will be used to communicate with our server using STOMP over websocket.

Create a directory anywhere and create below index.html file

<html>
<head>
<meta charset="UTF-8">
<title>WebSocket Client</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.5/angular.min.js"></script>
<script src="ng-stomp.standalone.min.js"></script>
<script type="text/javascript">

var app = angular.module('greetingMsg', ['ngStomp']);
app.controller('GreetingController', function ($stomp, $scope) {

$scope.myres = "";

$stomp.connect('http://localhost:9999/websocket', {})
.then(function (frame) {
var subscription = $stomp.subscribe('/topic/greeting',
function (payload, headers, res) {
$scope.myres = payload;
$scope.$apply($scope.myres);
console.log($scope.myres);
});

$stomp.send('/app/hello', '');
});
});

</script>

<style> 
.msg{
color : blue;
}

li{
list-style: none;
padding:0px 0px 10px 0px;
}
</style>

</head>
<body >

<div class="msg" ng-app="greetingMsg" ng-controller="GreetingController">
<p>Greeting Message - <b>{{myres.msg}}</b></p>
</div>

</body>
</html>

The connect() function uses SockJS and stomp.js to open a connection to “/websocket”, which is where our SockJS server is waiting for connections. Upon a successful connection, the client subscribes to the “/topic/greeting” destination, where the server will publish greeting messages. When a greeting is received on that destination, it will append a paragraph element to the DOM to display the greeting message.

We do not want to send any message to the destination “/app/hello”, so we have just put empty string(”) using STOMP client($stomp.send(‘/app/hello’, ”);). We are here to just get the message from server.

Download the stomp js file from https://github.com/beevelop/ng-stomp/blob/master/dist/ng-stomp.standalone.min.js

Running the client application

Run the index.html file. You should see similar kind message on the browser

springboot websocket angularjs gradle

Greeting Message - Good Afternoon - eda9cec8-93dd-4e65-a8ee-8e5c7ddff639

In browser console you should be able to see the similar kind of message as shown below

Objectmsg: "Good Afternoon - 0598d8b9-d44b-4d61-a09d-fd1116276fa6"__proto__: Object
Objectmsg: "Good Afternoon - 60777730-75e1-4e4c-81e7-7edfe74ed45f"__proto__: Object
Objectmsg: "Good Afternoon - 9e8432a8-0a31-44f3-95cf-e26c43768d11"__proto__: Object
Objectmsg: "Good Afternoon - b920246b-a2b5-4701-8c39-7c56fde01bdc"__proto__: Object

Thanks for reading.

Tags:

I am a professional Web developer, Enterprise Application developer, Software Engineer and Blogger. Connect me on Roy Tutorials | TwitterFacebook Google PlusLinkedin | Reddit

Leave a Reply

Your email address will not be published. Required fields are marked *