Tuesday, January 8, 2019

Building a WebSocket application with Spring Boot

This article shows you how to setup a very basic WebSocket application powered by Spring Boot. It uses the Java API for WebSocket (JSR-356) to define a WebSocket server endpoint and includes a very simple JavaScript WebSocket client.

As usual for a Spring Boot application, it all starts with the Maven POM:

  <?xml version="1.0" encoding="UTF-8"?>
  <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>
      <groupId>com.ervacon.sb</groupId>
      <artifactId>websocket</artifactId>
      <version>0.0.1-SNAPSHOT</version>

      <!-- Inherit defaults from Spring Boot -->
      <parent>
          <groupId>org.springframework.boot</groupId>
          <artifactId>spring-boot-starter-parent</artifactId>
          <version>2.1.1.RELEASE</version>
      </parent>

      <!-- Add typical dependencies for a websocket application -->
      <dependencies>
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-websocket</artifactId>
          </dependency>
      </dependencies>

      <!-- Package as an executable jar -->
      <build>
          <plugins>
              <plugin>
                  <groupId>org.springframework.boot</groupId>
                  <artifactId>spring-boot-maven-plugin</artifactId>
              </plugin>
          </plugins>
      </build>

  </project>
Notice how we pull in the spring-boot-starter-websocket dependency. This will make sure the Java API for WebSocket (javax.websocket) is on the classpath, together with Spring's support for WebSocket. When Spring Boot starts it will detect these dependencies and auto-configure the embedded Servlet engine (Tomcat by default) to handle the WebSocket protocol.

Next, we define our @SpringBootApplication class:

package com.ervacon.sb.websocket;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;

@SpringBootApplication
public class Application {

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

    @Bean
    public ServerEndpointExporter serverEndpointExporter() {
        return new ServerEndpointExporter();
    }
}
The only special thing here is the fact that we register the ServerEndpointExporter, which will make sure our @ServerEndpoint annotated classes get registered with the WebSocket runtime.

We can now define our server side WebSocket endpoint:

package com.ervacon.sb.websocket;

import org.springframework.stereotype.Component;

import javax.websocket.OnMessage;
import javax.websocket.server.ServerEndpoint;

@Component
@ServerEndpoint("/socket")
public class WebSocketEndpoint {

    @OnMessage
    public String echo(String message) {
        return "Echo: " + message;
    }
}
Nothing to surprising here: we're just using the Java API for WebSocket (JSR-356) to define a method that will handle incoming text messages by simply echoing them back to the client. That client is implemented using some basic JavaScript code in a simple HTML page (add this as index.html to src/main/resources/static in your Spring Boot application):
<html>
  <head>
      <title>WebSocket Client</title>
      <!-- based on https://www.nexmo.com/blog/2018/10/08/create-websocket-server-spring-boot-dr/ -->
  </head>
  <body>
  <div class="container">
      <div id="messages" style="border: 1px solid gray; padding: 1em;"></div>
      <div class="input-fields">
          <p>Type your message:</p>
          <input id="message"/>
          <button id="send">Send</button>
      </div>
  </div>
  </body>
  <script>
      const messageWindow = document.getElementById("messages");

      const sendButton = document.getElementById("send");
      const messageInput = document.getElementById("message");

      const socket = new WebSocket("ws://localhost:8080/socket");

      socket.onopen = function (event) {
          addMessageToWindow("Connected");
      };

      socket.onmessage = function (event) {
          addMessageToWindow(`Got Message: ${event.data}`);
      };

      sendButton.onclick = function (event) {
          sendMessage(messageInput.value);
          messageInput.value = "";
      };

      function sendMessage(message) {
          socket.send(message);
          addMessageToWindow("Sent Message: " + message);
      }

      function addMessageToWindow(message) {
          messageWindow.innerHTML += `<div>${message}</div>`
      }
  </script>
</html>
We can now launch our Spring Boot application using a simple mvn spring-boot:run and test it by accessing http://localhost:8080/.

No comments:

Post a Comment