To use WebSockets with the Java native HttpClient (introduced in Java 11 and fully supported in Java 25), you use the WebSocket.Builder.
The process involves three main steps:
1. Create an HttpClient (or use an existing one).
2. Implement a WebSocket.Listener to handle incoming messages and events.
3. Build and open the connection using HttpClient.newWebSocketBuilder().
Here is a complete example of a simple WebSocket client:
package org.kodejava.httpclient;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.WebSocket;
import java.nio.ByteBuffer;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
public class WebSocketExample {
public static void main(String[] args) throws Exception {
HttpClient client = HttpClient.newHttpClient();
// 1. Build the WebSocket connection
CompletableFuture<WebSocket> wsFuture = client.newWebSocketBuilder()
.buildAsync(URI.create("wss://echo.websocket.org"), new EchoListener());
// 2. Use the WebSocket instance to send data
wsFuture.thenAccept(webSocket -> {
System.out.println("Connected!");
webSocket.sendText("Hello, WebSocket!", true);
// Keep the connection open for a bit to receive the echo
try { Thread.sleep(2000); } catch (InterruptedException e) { }
webSocket.sendClose(WebSocket.NORMAL_CLOSURE, "Done");
}).join();
}
// 3. Implement the Listener to handle events
private static class EchoListener implements WebSocket.Listener {
@Override
public void onOpen(WebSocket webSocket) {
System.out.println("WebSocket opened");
WebSocket.Listener.super.onOpen(webSocket);
}
@Override
public CompletionStage<?> onText(WebSocket webSocket, CharSequence data, boolean last) {
System.out.println("Received message: " + data);
// Request the next message
webSocket.request(1);
return null; // Returning null means we're done with this message
}
@Override
public CompletionStage<?> onClose(WebSocket webSocket, int statusCode, String reason) {
System.out.println("Closed: " + statusCode + " " + reason);
return null;
}
@Override
public void onError(WebSocket webSocket, Throwable error) {
System.err.println("Error occurred: " + error.getMessage());
}
}
}
Key Concepts:
buildAsync(URI, Listener): This starts the opening handshake. It returns aCompletableFuture<WebSocket>because the handshake is asynchronous [1].- Backpressure (
request(n)): By default, the client doesn’t automatically pull all messages from the server. InonTextoronBinary, you usually callwebSocket.request(1)to tell the client you are ready to receive the next message [2]. - The
lastparameter: If a message is very large, it might be delivered in chunks. Thelastboolean flag tells you if the current chunk is the end of the message. CompletionStage<?>: Listener methods return aCompletionStage. If you return aCompletableFuture, the client will wait for it to complete before calling that listener method again, which is useful for processing messages asynchronously without blocking the network thread [4].
