Web Socket using Spring and Stomp

WebSocket is used for bidirectional communication between the server and the client.
We need this when there is a need to two directional communication. HTTP is single directional.
Sometimes you might need the server to push data to the client without client request which is not possible via http. Now days websocket is supported in all the browsers.

Stomp is a text oriented messaging protocol. It supports fallback options for websocket.

Example Angular 6 and Spring:

We will be using @stomp/ng2-stompjs. npm install --save @stomp/ng2-stompjs

import { StompConfig, StompService } from '@stomp/ng2-stompjs';
import { WebSocketConfig } from './websocket.config';


export const stompConfig: StompConfig = {
url: WebSocketConfig.uri,
headers: {
},
heartbeat_in: 0,
heartbeat_out: 20000,
reconnect_delay: 5000,
debug: false
};

export class WebSocketConfig{
public static uri:string = 'ws://localhost:8080/testws';
public static topic:string = "/topic/hello";
}

import { Injectable } from '@angular/core';
import { Subscription, Observable } from 'rxjs';
import { Message } from '@stomp/stompjs';
import { StompService, StompState } from '@stomp/ng2-stompjs';
import {map} from 'rxjs/operators';
import { WebSocketConfig } from './websocket.config';

@Injectable({
providedIn: 'root'
})
export class WebsocketService {
public message: Observable<Message>;
public wsstate: Observable<string>;

constructor(private stompService: StompService) { }

public connectWebSocket() {
this.wsstate = this.stompService.state.pipe(map((state: number) => StompState[state]));
this.message = this.stompService.subscribe(WebSocketConfig.topic);
}
public getSocketDataObservable(){
return this.message;
}
public getSocketStateObservable(){
return this.wsstate;
}
}

Add this to the app.module.ts
providers: [StompService,
{
provide: StompConfig,
useValue: stompConfig
}],
This component will be consuming the data.

import { Component,OnInit } from '@angular/core';
import { Subscription } from 'rxjs';
import { Message } from '@stomp/stompjs';
import { WebsocketService } from './ws/websocket.service';
import { WsData } from './ws/wsdata.model';

@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css'],
viewProviders: [WebsocketService]
})
export class AppComponent implements OnInit{
title = 'app';
private datasubscription: Subscription;
private statesubscription: Subscription;
public uiData : WsData;
constructor(private websocketService:WebsocketService) { }
ngOnInit() {
this.websocketService.connectWebSocket();
this.datasubscription = this.websocketService.getSocketDataObservable().subscribe(this.onData);
this.statesubscription = this.websocketService.getSocketStateObservable().subscribe(this.onStateChange);
}
private onData = (message: Message) => {
this.uiData = JSON.parse(message.body);
}
private onStateChange = (state: String) => {
console.log('WS connection state changed '+state);
}
ngOnDestroy() {
this.datasubscription.unsubscribe();
this.statesubscription.unsubscribe();
}
}

Spring code:

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 configureMessageBroker(MessageBrokerRegistry config) {
config.enableSimpleBroker("/topic");
config.setApplicationDestinationPrefixes("/test");
}
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/testws").setAllowedOrigins("*");
}
}

import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Component;
@Component
public class WebSocketController{
    private static final Logger LOGGER = Logger.getLogger(WebSocketController.class.getName());
    int time = 10;
    @Autowired
    private SimpMessagingTemplate template;
    public void publishWebSocket(String data){
        LOGGER.info("Publis the result : " + data);
        template.convertAndSend("/topic/hello", data);
    }
}

With these two configuration our webscolet is ready. Send some data to the client in each interval.

import javax.annotation.PostConstruct;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Component;

@Component
public class WebSocketController implements Runnable{
    @Autowired
    private SimpMessagingTemplate template;
    public void startWebSocket(){
        new Thread(this).start();
    }
@PostConstruct
    public void postConstruct() {
        System.out.println("Start websocket");
        startWebSocket();
    }
    @Override
    public void run() {
        int data = 10;
        while (true){
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            i++;
            template.convertAndSend("/topic/hello", data);
        }
    }
}