Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

WebSocket: a blog chat

Ordinary HTTP works as "question and answer": the client asks, the server answers, and the connection is no longer needed. That is not enough for a chat: the server must send a message by itself as soon as someone else writes it. WebSocket is a protocol for a permanent two-way connection: once connected, both the client and the server can send messages at any moment. In Javalin a WebSocket route is declared with the config.routes.ws method.

Connection events

A WebSocket handler receives a WsConfig object on which you register reactions to events:

EventWhen it happens
onConnectA client connected
onMessageA text message arrived, ctx.message() gives it
onCloseThe connection was closed
onErrorAn error occurred

The chat class

The chat keeps all open connections and sends every message to everyone:

// src/main/java/com/example/blog/Chat.java
package com.example.blog;
 
import io.javalin.websocket.WsConfig;
import io.javalin.websocket.WsContext;
 
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
 
public class Chat {
    private final Set<WsContext> sessions = ConcurrentHashMap.newKeySet();
 
    public void configure(WsConfig ws) {
        ws.onConnect(ctx -> {
            sessions.add(ctx);
            broadcast("Someone joined, online: " + sessions.size());
        });
        ws.onMessage(ctx -> broadcast(ctx.message()));
        ws.onClose(ctx -> {
            sessions.remove(ctx);
            broadcast("Someone left, online: " + sessions.size());
        });
    }
 
    private void broadcast(String text) {
        for (WsContext client : sessions) {
            if (client.session.isOpen()) {
                client.send(text);
            }
        }
    }
}

WsContext is the context of one connection, similar to ctx in HTTP. The send method sends a string to the client; if you pass an object, Javalin turns it into JSON. A set made from ConcurrentHashMap is safe for different threads.

The route

Register the chat in Main:

// src/main/java/com/example/blog/Main.java
var chat = new Chat();
 
// inside Javalin.create(config -> { ... })
config.routes.ws("/chat", chat::configure);

Checking it in the browser

curl cannot do WebSocket, but every browser can. Open http://localhost:8080 in two tabs, open the developer console (F12) in each and run:

const ws = new WebSocket("ws://localhost:8080/chat");
ws.onmessage = e => console.log("Received:", e.data);

The address starts with ws:// instead of http://. In the second tab send a message with ws.send("Hi, Anna!"). Both tabs show:

Received: Someone joined, online: 1
Received: Someone joined, online: 2
Received: Hi, Anna!

The first line appears only in the first tab: it connected while it was alone. Close one tab, and the remaining one receives Someone left, online: 1.

Common mistake

A WebSocket route does not answer ordinary HTTP requests. If you open its address in a browser or call curl, you get 404:

curl -i http://localhost:8080/chat
HTTP/1.1 404 Not Found
Content-Type: text/plain

Endpoint GET /chat not found

Connect to it only with a WebSocket client and with an address using the ws:// scheme (or wss:// for HTTPS).

Thanks for reading!

Donate to the site — we work hard for you.