Example of Creating a WebSocket Server in Java

This post shows how to implement a WebSocket server in Java using the @ServerEndpoint annotation. A WebSocket server application can be deployed to Tomcat 7 or higher, or to any other Java EE servlet container that supports WebSockets. There are two packages for WebSocket programming:

javax.websocket – APIs common to both the client and server side
javax.websocket.server – APIs used only by server side applications

WebSocket is a technology for establishing a persistent, low-latency, full-duplex communication channel over a single http connection for real-time data exchange between a server endpoint (Java, .NET, PHP etc.) and a client (HTML5 / JavaScript, iOS).

  • The WebSocket protocol is an IETF proposed standard as described in RFC6455.
  • The WebSocket protocol defines 2 new URI schemes: ws and wss for TLS encryption.
  • WebSocket server URIs support query parameters as in: ws://hostname:8080/AppContext/endpoint?userId=12345&location=London
  • The Java WebSocket API runs on Servlet containers such as Tomcat, JBoss and Websphere.
  • See Oracle’s JSR 356 for specification details of the Java API for WebSocket.
  • The W3C maintains the WebSocket JavaScript API Specification and defines the WebSocket interface.
  • HTML5 compliant web browsers provide an implementation of the specification to enable clients to connect to a WebSocket server and to send and receive data (IE10+, Chrome 16+, Firefox 11+, Safari 6+).

For an example of how to create a WebSocket client in JavaScript and HTML 5, see the post below:

Summary

  1. Implementation of WebSocket Server in Java
  2. Running the WebSocket Server on Tomcat
  3. Testing the WebSocket Server Endpoint from Web Browser
  4. Automatically Pushing Notifications to WebSocket Clients
  5. Monitoring WebSocket Traffic with Chrome Developer Tools

1  Implementation of WebSocket Server in Java

Below is the Java source code for the WebSocket server endpoint implementation. In line 10, the annotation @ServerEndpoint is used to decorate a class that implements a WebSocket server endpoint. Four more method annotations are used to decorate event handlers for WebSocket client connections.

@OnOpen – Called when a client connects
@OnClose – Called when a client connection is closed
@OnMessage – Called when a message is received by the client
@OnError – Called when an error for this endpoint occurred

These four methods are invoked by the container.

package com.pgx.java.web;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/endpoint")
public class MyWebSocket {
    
    private static PushTimeService pst;

    @OnOpen
    public void onOpen(Session session) {
        System.out.println("onOpen::" + session.getId());        
    }

    @OnClose
    public void onClose(Session session) {
        System.out.println("onClose::" +  session.getId());
    }
    
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("onMessage::From=" + session.getId() + " Message=" + message);
        
        try {
            session.getBasicRemote().sendText("Hello Client " + session.getId() + "!");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    @OnError
    public void onError(Throwable t) {
        System.out.println("onError::" + t.getMessage());
    }
}

The Eclipse Neon IDE for Java web development and Apache Tomcat 9 were used as described in the post below to implement the class and to deploy it.

Eclipse Neon - Structure of Dynamic Web Project for WebSocket Server in Java.

This setup allows to run the WebSocket server application on Tomcat from within Eclipse. The above image shows the basic structure of the dynamic web project in the Project Explorer. Note the tomcat-websocket.jar file in the Apache Tomcat v9.0 server runtime library. It contains the Java API for WebSocket support.

2  Running the WebSocket Server on Tomcat

To export the WebSocket server application so that it can be deployed to an external Tomcat server, see the below post on creating a WAR file:

Here, the web application is run from within Eclipse using the dynamic web project setup mentioned earlier. No configuration changes to Tomcat are required to run a WebSocket server endpoint. The port number will be the same as the one used for connections via the http protocol, e.g. 8080.

Eclipse Neon - Apache Tomcat Server Runtime Environment. Start WebSocket Server Application.

Use the Console tab in Eclipse to view the Tomcat server output and to catch potential errors on startup.

Eclipse Neon - Apache Tomcat Server Console Output. Successful WebSocket Server in Java Startup.

If there are no errors, the WebSocket server is up and running and ready to accept connections from clients. The WebSocket server endpoint URL for accessing it from a local machine would be:

ws://127.0.0.1:8080/WebSocketServer/endpoint

3  Testing the WebSocket Server Endpoint from Web Browser

The JavaScript source code given below creates a class called WebSocketClient which implements a basic WebSocket client.

class WebSocketClient {
    
    constructor(protocol, hostname, port, endpoint) {
        
        this.webSocket = null;
        
        this.protocol = protocol;
        this.hostname = hostname;
        this.port     = port;
        this.endpoint = endpoint;
    }
    
    getServerUrl() {
        return this.protocol + "://" + this.hostname + ":" + this.port + this.endpoint;
    }
    
    connect() {

        try {
            this.webSocket = new WebSocket(this.getServerUrl());
            
            // 
            // Implement WebSocket event handlers!
            //
            this.webSocket.onopen = function(event) {
                console.log('onopen::' + JSON.stringify(event, null, 4));
            }
            
            this.webSocket.onmessage = function(event) {
                var msg = event.data;
                console.log('onmessage::' + JSON.stringify(msg, null, 4));
            }

            this.webSocket.onclose = function(event) {
                console.log('onclose::' + JSON.stringify(event, null, 4));                
            }

            this.webSocket.onerror = function(event) {
                console.log('onerror::' + JSON.stringify(event, null, 4));
            }
            
        } catch (exception) {
            console.error(exception);
        }
    }
    
    getStatus() {
        return this.webSocket.readyState;
    }

    send(message) {
        
        if (this.webSocket.readyState == WebSocket.OPEN) {
            this.webSocket.send(message);
            
        } else {
            console.error('webSocket is not open. readyState=' + this.webSocket.readyState);
        }
    }

    disconnect() {

        if (this.webSocket.readyState == WebSocket.OPEN) {
            this.webSocket.close();
            
        } else {
            console.error('webSocket is not open. readyState=' + this.webSocket.readyState);
        }
    }
}

The JavaScript code can be executed with Chrome (or any other browser), using its built-in Developer Tools. Open a new window in Chrome, copy the above JavaScript WebSocket client code and paste it into the console provided by the Chrome Developer tools.

Google Chrome Developer Tools - Use JavaScript Console to Define WebSocketClient Class.

The WebSocketClient class has now been defined on the current page. In the console, enter the JavaScript code below to create a new object of that class.

var client = new WebSocketClient('ws', '127.0.0.1', 8080, '/WebSocketServer/endpoint');

Then call the connect method to open a new connection to the WebSocket server endpoint. The browser will call the event handler onopen once the connection has been established.

client.connect();
Google Chrome Developer Tools - Use JavaScript Console to Create new Instance of WebSocketClient Class

At this point, the container that is hosting the WebSocket server endpoint will call the server implementation’s onOpen method. In this example, the server simply prints the session Id, which is 0 here.

  • See the onOpen method of the MyWebSocket.java class from section 1.
  • See the Oracle documentation for details on javax.websocket.Session.
Eclipse Neon - Apache Tomcat Server Console Output. New WebSocket Connection Opened.

On the client side, execute the send(String message) method using the JavaScript below to send a new message to the server.

client.send('Hello Server!');
Google Chrome Developer Tools - Use JavaScript Console to Send WebSocket Message to Server.

The server returns a greeting message to the client, here "Hello Client 0!" See line 34 of the MyWebSocket.java class from section 1. At the same time, the container that is hosting the WebSocket server endpoint will call the server implementation’s onMessage method. The server simply prints the client’s message to the console.

Eclipse Neon - Apache Tomcat Server Console Output. WebSocket Message Received from Client.

4  Automatically Pushing Notifications to WebSocket Clients

In this section, the MyWebSocket.java class is changed a bit so that clients can subscribe to a notification service that will periodically send the server’s current system time. The clients that want to receive those notifications need to provide a URL query string parameter as shown below:

ws://127.0.0.1:8080/WebSocketServer/endpoint?push=TIME

New code has been added to the onOpen method between line 21 and line 32 as shown below. The PushTimeService class maintains a collection of active Session objects and periodically sends the current server time to all clients that have subscribed.

package com.pgx.java.web;

import java.io.IOException;
import java.util.List;
import java.util.Map;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/endpoint")
public class MyWebSocket {

    @OnOpen
    public void onOpen(Session session) {
        System.out.println("onOpen::" + session.getId());
        
        ///////////////////////////////////////////////////////////////////////////// 
        // Access request parameters from URL query String.
        // If a client subscribes, add Session to PushTimeService. 
        //
        Map<String, List<String>> params = session.getRequestParameterMap();
        
        if (params.get("push") != null && (params.get("push").get(0).equals("TIME"))) {
            
          PushTimeService.initialize();
          PushTimeService.add(session);
        }
        /////////////////////////////////////////////////////////////////////////////
    }

    @OnClose
    public void onClose(Session session) {
        System.out.println("onClose::" +  session.getId());
    }
    
    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("onMessage::From=" + session.getId() + " Message=" + message);
        
        try {
            session.getBasicRemote().sendText("Hello Client " + session.getId() + "!");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    @OnError
    public void onError(Throwable t) {
        System.out.println("onError::" + t.getMessage());
    }
}

The Java source code of the PushTimeService.java class is given below. This class implements a singleton and runs in a separate thread. Every 10 seconds, it iterates over its collection of subscribed clients and sends the server time to each one as a WebSocket message. If the session has been closed, it is removed from the collection.

package com.pgx.java.web;

import java.util.*;
import javax.websocket.Session;

public class PushTimeService implements Runnable {
    
    private static PushTimeService instance;
    private static Map<String, Session> sMap = new HashMap<String, Session>();

    private PushTimeService() {}
    
    public static void add(Session s) {
        sMap.put(s.getId(), s);
    }
    
    public static void initialize() {
        if (instance == null) {
            instance = new PushTimeService();
            new Thread(instance).start();
        }
    }

    @Override
    public void run() {
        
        while (true) {
            try {
                Thread.sleep(10*1000);

                for (String key : sMap.keySet()) {
                    
                    Session s = sMap.get(key); 
                    
                    if (s.isOpen()) {
                        Date d = new Date(System.currentTimeMillis());
                        s.getBasicRemote().sendText(d.toString());

                    } else {
                        sMap.remove(key);
                    }
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

In the below scenario, 2 WebSocket clients have been started in different browsers using the WebSocketClient JavaScript class from section 3. Both have subscribed to receive the server system time.

Google Chrome Developer Tools - JavaScript Console to View WebSocket Messages Pushed to Client 1.

The PushTimeService class sends the server’s system time to both clients, every 10 seconds.

Google Chrome Developer Tools - JavaScript Console to View WebSocket Messages Pushed to Client 2.

Similar approaches can be used to push a variety of event driven data from the server to clients in real time. Since the client’s WebSocket connection resides in the UI layer, WebSockets are a good solution (alternative to polling) for driving real-time UI components such as stock tickers or notifications.

5  Monitoring WebSocket Traffic with Chrome Developer Tools

The Chrome Developer Tools provide means for basic monitoring of WebSocket traffic. Use the Network tab and then filter the traffic with the WS button to only show WebSockets. The Headers tab shows the client http request and the server response. The Query String Parameters are listed at the bottom.

Google Chrome Developer Tools - Use Network Tab to View WebSocket Headers.

Note that the server responds with a connection upgrade in the response header to upgrade the http connection to a persistent WebSocket connection. See the protocol upgrade mechanism for details on this process. The Frames tab shows all outgoing and incoming WebSocket messages. Click on a frame to see its content.

Google Chrome Developer Tools - Use Network Tab to View WebSocket Frames.

The Timig tab shows how long the WebSocket connection has been open. In the below example, it has been open for 4.4 mins.

Google Chrome Developer Tools - Use Network Tab to View WebSocket Timing.

Related Posts

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.