or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

adapter-support.mdclient-support.mdconfiguration.mdcore-websocket.mdhandlers.mdindex.mdserver-support.mdsockjs-client.mdsockjs-frames.mdsockjs-support.mdstomp-messaging.md
tile.json

client-support.mddocs/

Client Support

Client-side WebSocket connection management and implementations. Supports initiating WebSocket connections, managing connection lifecycle, and provides JSR-356 standard client implementations.

Capabilities

WebSocketClient Interface

Contract for initiating WebSocket connections from the client side.

/**
 * Contract for establishing WebSocket connections to a server.
 * Implementations handle the client-side handshake and connection setup.
 */
public interface WebSocketClient {
    /**
     * Execute a WebSocket connection using a URI template.
     *
     * @param webSocketHandler the handler for the connection
     * @param uriTemplate      the URI template (e.g., "ws://localhost:8080/ws")
     * @param uriVariables     variables to expand in the URI template
     * @return a CompletableFuture that completes when connection is established
     */
    CompletableFuture<WebSocketSession> execute(
            WebSocketHandler webSocketHandler,
            String uriTemplate,
            Object... uriVariables);

    /**
     * Execute a WebSocket connection with headers.
     *
     * @param webSocketHandler the handler for the connection
     * @param headers          HTTP headers for the handshake
     * @param uri              the WebSocket URI
     * @return a CompletableFuture that completes when connection is established
     */
    CompletableFuture<WebSocketSession> execute(
            WebSocketHandler webSocketHandler,
            WebSocketHttpHeaders headers,
            URI uri);
}

WebSocketConnectionManager

Manages WebSocket connection lifecycle with automatic start/stop support.

/**
 * WebSocket connection manager that connects on start and disconnects on stop.
 * Integrates with Spring's SmartLifecycle for automatic connection management.
 * Useful for long-lived connections that should be established when the
 * application starts.
 */
public class WebSocketConnectionManager extends ConnectionManagerSupport {
    /**
     * Create a connection manager with URI template.
     *
     * @param client           the WebSocket client
     * @param webSocketHandler the WebSocket handler
     * @param uriTemplate      the URI template
     * @param uriVariables     variables for the URI template
     */
    public WebSocketConnectionManager(
            WebSocketClient client,
            WebSocketHandler webSocketHandler,
            String uriTemplate,
            Object... uriVariables);

    /**
     * Create a connection manager with URI.
     *
     * @param client           the WebSocket client
     * @param webSocketHandler the WebSocket handler
     * @param uri              the WebSocket URI
     */
    public WebSocketConnectionManager(
            WebSocketClient client,
            WebSocketHandler webSocketHandler,
            URI uri);

    /**
     * Set the sub-protocols to request during handshake.
     *
     * @param protocols the sub-protocols to request
     */
    public void setSubProtocols(List<String> protocols);

    /**
     * Get the configured sub-protocols.
     *
     * @return the sub-protocols
     */
    public List<String> getSubProtocols();

    /**
     * Set the Origin header value for the handshake.
     *
     * @param origin the origin value
     */
    public void setOrigin(String origin);

    /**
     * Get the configured Origin header value.
     *
     * @return the origin
     */
    public String getOrigin();

    /**
     * Set additional HTTP headers for the handshake.
     *
     * @param headers the HTTP headers
     */
    public void setHeaders(HttpHeaders headers);

    /**
     * Get the configured HTTP headers.
     *
     * @return the headers
     */
    public HttpHeaders getHeaders();

    /**
     * Open a new connection. Called automatically on start if autoStartup is true.
     */
    @Override
    protected void openConnection();

    /**
     * Close the connection. Called automatically on stop.
     */
    @Override
    protected void closeConnection() throws Exception;

    /**
     * Whether the connection is currently open.
     *
     * @return true if connected
     */
    @Override
    public boolean isConnected();
}

ConnectionManagerSupport

Base class for connection managers with lifecycle support.

/**
 * Abstract base class for connection managers. Implements SmartLifecycle
 * for integration with Spring's container lifecycle.
 */
public abstract class ConnectionManagerSupport implements SmartLifecycle {
    /**
     * Set whether to automatically connect on startup.
     * Default is true.
     *
     * @param autoStartup whether to auto-connect
     */
    public void setAutoStartup(boolean autoStartup);

    /**
     * Whether auto-startup is enabled.
     *
     * @return true if auto-startup is enabled
     */
    @Override
    public boolean isAutoStartup();

    /**
     * Set the lifecycle phase. Lower values start earlier.
     * Default is Integer.MAX_VALUE.
     *
     * @param phase the phase value
     */
    public void setPhase(int phase);

    /**
     * Get the lifecycle phase.
     *
     * @return the phase value
     */
    @Override
    public int getPhase();

    /**
     * Start the connection manager (establish connection).
     */
    @Override
    public void start();

    /**
     * Stop the connection manager (close connection).
     */
    @Override
    public void stop();

    /**
     * Stop with a callback.
     */
    @Override
    public void stop(Runnable callback);

    /**
     * Whether the connection manager is running.
     *
     * @return true if running
     */
    @Override
    public boolean isRunning();

    /**
     * Open a new connection. Subclasses implement the actual connection logic.
     */
    protected abstract void openConnection();

    /**
     * Close the connection. Subclasses implement the actual close logic.
     */
    protected abstract void closeConnection() throws Exception;

    /**
     * Whether the connection is currently established.
     *
     * @return true if connected
     */
    public abstract boolean isConnected();
}

AbstractWebSocketClient

Base implementation of WebSocketClient with common client logic.

/**
 * Abstract base class for WebSocketClient implementations.
 * Provides common connection logic and task executor support.
 */
public abstract class AbstractWebSocketClient implements WebSocketClient {
    /**
     * Create a client with default configuration.
     */
    public AbstractWebSocketClient();

    /**
     * Set the task executor for handling connection callbacks.
     * If not set, callbacks are executed on the I/O thread.
     *
     * @param taskExecutor the task executor
     */
    public void setTaskExecutor(AsyncTaskExecutor taskExecutor);

    /**
     * Get the configured task executor.
     *
     * @return the task executor, or null if not set
     */
    public AsyncTaskExecutor getTaskExecutor();

    @Override
    public CompletableFuture<WebSocketSession> execute(
            WebSocketHandler webSocketHandler,
            String uriTemplate,
            Object... uriVariables);

    @Override
    public CompletableFuture<WebSocketSession> execute(
            WebSocketHandler webSocketHandler,
            WebSocketHttpHeaders headers,
            URI uri);

    /**
     * Perform the actual connection. Subclasses implement this method
     * with client-specific connection logic.
     *
     * @param webSocketHandler the WebSocket handler
     * @param headers          the handshake headers
     * @param uri              the WebSocket URI
     * @return a CompletableFuture for the connection
     */
    protected abstract CompletableFuture<WebSocketSession> doHandshakeInternal(
            WebSocketHandler webSocketHandler,
            WebSocketHttpHeaders headers,
            URI uri);
}

StandardWebSocketClient

JSR-356 (Jakarta WebSocket) client implementation.

/**
 * WebSocketClient implementation using the standard JSR-356 Java WebSocket API.
 * Works with any JSR-356 compliant WebSocket client implementation.
 */
public class StandardWebSocketClient extends AbstractWebSocketClient {
    /**
     * Create a client using the default WebSocketContainer.
     */
    public StandardWebSocketClient();

    /**
     * Create a client using a specific WebSocketContainer.
     *
     * @param webSocketContainer the container to use
     */
    public StandardWebSocketClient(WebSocketContainer webSocketContainer);

    /**
     * Set user properties to pass to the WebSocketContainer.
     *
     * @param userProperties the user properties
     */
    public void setUserProperties(Map<String, Object> userProperties);

    /**
     * Get the configured user properties.
     *
     * @return the user properties
     */
    public Map<String, Object> getUserProperties();

    @Override
    protected CompletableFuture<WebSocketSession> doHandshakeInternal(
            WebSocketHandler webSocketHandler,
            WebSocketHttpHeaders headers,
            URI uri);
}

WebSocketContainerFactoryBean

FactoryBean for creating and configuring a WebSocketContainer.

/**
 * FactoryBean that creates a JSR-356 WebSocketContainer with configurable
 * properties. Useful for customizing the WebSocket client container.
 */
public class WebSocketContainerFactoryBean
        implements FactoryBean<WebSocketContainer>, InitializingBean {

    /**
     * Create a factory bean with default configuration.
     */
    public WebSocketContainerFactoryBean();

    /**
     * Set the maximum text message buffer size in bytes.
     * Default is typically 8192 (8KB).
     *
     * @param bufferSize the buffer size in bytes
     */
    public void setMaxTextMessageBufferSize(int bufferSize);

    /**
     * Set the maximum binary message buffer size in bytes.
     * Default is typically 8192 (8KB).
     *
     * @param bufferSize the buffer size in bytes
     */
    public void setMaxBinaryMessageBufferSize(int bufferSize);

    /**
     * Set the maximum idle timeout in milliseconds.
     * A timeout of zero or negative means no timeout.
     *
     * @param timeout the idle timeout in milliseconds
     */
    public void setMaxSessionIdleTimeout(long timeout);

    /**
     * Set the timeout in milliseconds for asynchronous send operations.
     *
     * @param timeout the send timeout in milliseconds
     */
    public void setAsyncSendTimeout(long timeout);

    @Override
    public WebSocketContainer getObject() throws Exception;

    @Override
    public Class<?> getObjectType();

    @Override
    public boolean isSingleton();
}

EndpointConnectionManager

Connection manager for JSR-356 Endpoint connections.

/**
 * WebSocket connection manager that connects using a JSR-356 Endpoint.
 * Provides programmatic endpoint connection with Spring lifecycle support.
 */
public class EndpointConnectionManager extends ConnectionManagerSupport {
    /**
     * Create a connection manager for an Endpoint.
     *
     * @param endpointClass the Endpoint class
     * @param uriTemplate   the URI template
     * @param uriVars       variables for the URI template
     */
    public EndpointConnectionManager(
            Class<? extends Endpoint> endpointClass,
            String uriTemplate,
            Object... uriVars);

    /**
     * Create a connection manager with a WebSocketContainer.
     *
     * @param container     the WebSocket container
     * @param endpointClass the Endpoint class
     * @param uriTemplate   the URI template
     * @param uriVars       variables for the URI template
     */
    public EndpointConnectionManager(
            WebSocketContainer container,
            Class<? extends Endpoint> endpointClass,
            String uriTemplate,
            Object... uriVars);

    /**
     * Create a connection manager with an Endpoint supplier.
     *
     * @param container      the WebSocket container
     * @param endpointSupplier supplier for Endpoint instances
     * @param uriTemplate    the URI template
     * @param uriVars        variables for the URI template
     */
    public EndpointConnectionManager(
            WebSocketContainer container,
            Supplier<Endpoint> endpointSupplier,
            String uriTemplate,
            Object... uriVars);

    @Override
    protected void openConnection();

    @Override
    protected void closeConnection() throws Exception;

    @Override
    public boolean isConnected();
}

AnnotatedEndpointConnectionManager

Connection manager for @ClientEndpoint annotated classes.

/**
 * Connection manager for JSR-356 @ClientEndpoint annotated classes.
 * Enables using Spring-managed beans as WebSocket client endpoints.
 */
public class AnnotatedEndpointConnectionManager extends ConnectionManagerSupport {
    /**
     * Create a connection manager for an annotated endpoint.
     *
     * @param endpoint    the @ClientEndpoint annotated object
     * @param uriTemplate the URI template
     * @param uriVars     variables for the URI template
     */
    public AnnotatedEndpointConnectionManager(
            Object endpoint,
            String uriTemplate,
            Object... uriVars);

    /**
     * Create a connection manager with a WebSocketContainer.
     *
     * @param container   the WebSocket container
     * @param endpoint    the @ClientEndpoint annotated object
     * @param uriTemplate the URI template
     * @param uriVars     variables for the URI template
     */
    public AnnotatedEndpointConnectionManager(
            WebSocketContainer container,
            Object endpoint,
            String uriTemplate,
            Object... uriVars);

    @Override
    protected void openConnection();

    @Override
    protected void closeConnection() throws Exception;

    @Override
    public boolean isConnected();
}

Usage Examples

Basic WebSocket Client Connection

import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

public class WebSocketClientExample {

    public static void main(String[] args) throws Exception {
        WebSocketClient client = new StandardWebSocketClient();

        WebSocketHandler handler = new TextWebSocketHandler() {
            @Override
            public void afterConnectionEstablished(WebSocketSession session)
                    throws Exception {
                System.out.println("Connected to server");
                session.sendMessage(new TextMessage("Hello Server!"));
            }

            @Override
            protected void handleTextMessage(WebSocketSession session, TextMessage message)
                    throws Exception {
                System.out.println("Received: " + message.getPayload());
            }
        };

        CompletableFuture<WebSocketSession> future = client.execute(
                handler,
                "ws://localhost:8080/ws"
        );

        // Wait for connection
        WebSocketSession session = future.get();

        // Keep connection open
        Thread.sleep(10000);

        // Close connection
        session.close();
    }
}

WebSocket Connection with Headers

import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.WebSocketHttpHeaders;

WebSocketClient client = new StandardWebSocketClient();

WebSocketHttpHeaders headers = new WebSocketHttpHeaders();
headers.add("Authorization", "Bearer token123");
headers.add("X-Client-Version", "1.0");

URI uri = new URI("ws://localhost:8080/ws");

CompletableFuture<WebSocketSession> future = client.execute(
        myHandler,
        headers,
        uri
);

future.thenAccept(session -> {
    System.out.println("Connected with custom headers");
}).exceptionally(ex -> {
    System.err.println("Connection failed: " + ex.getMessage());
    return null;
});

WebSocket Connection Manager with Auto-Reconnect

import org.springframework.web.socket.client.WebSocketConnectionManager;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;

@Configuration
public class WebSocketClientConfig {

    @Bean
    public WebSocketConnectionManager connectionManager() {
        WebSocketClient client = new StandardWebSocketClient();
        WebSocketHandler handler = new MyClientHandler();

        WebSocketConnectionManager manager = new WebSocketConnectionManager(
                client,
                handler,
                "ws://localhost:8080/ws"
        );

        // Configure connection manager
        manager.setAutoStartup(true);  // Connect on startup
        manager.setHeaders(customHeaders());

        return manager;
    }

    private HttpHeaders customHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.add("X-Client-ID", "client-123");
        return headers;
    }
}

// Handler implementation
class MyClientHandler extends TextWebSocketHandler {
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        System.out.println("Connected to server");
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message)
            throws Exception {
        System.out.println("Received: " + message.getPayload());
    }

    @Override
    public void afterConnectionClosed(WebSocketSession session, CloseStatus status)
            throws Exception {
        System.out.println("Disconnected: " + status);
    }
}

Custom WebSocketContainer Configuration

import org.springframework.web.socket.client.standard.WebSocketContainerFactoryBean;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import jakarta.websocket.WebSocketContainer;

@Configuration
public class WebSocketClientConfig {

    @Bean
    public WebSocketContainerFactoryBean webSocketContainer() {
        WebSocketContainerFactoryBean factory = new WebSocketContainerFactoryBean();

        // Configure buffer sizes
        factory.setMaxTextMessageBufferSize(16 * 1024);      // 16KB
        factory.setMaxBinaryMessageBufferSize(16 * 1024);    // 16KB

        // Configure timeouts
        factory.setMaxSessionIdleTimeout(60000);  // 60 seconds
        factory.setAsyncSendTimeout(10000);       // 10 seconds

        return factory;
    }

    @Bean
    public StandardWebSocketClient webSocketClient(WebSocketContainer container) {
        return new StandardWebSocketClient(container);
    }
}

Sub-Protocol Negotiation

import org.springframework.web.socket.client.WebSocketConnectionManager;

@Bean
public WebSocketConnectionManager stompConnectionManager() {
    WebSocketClient client = new StandardWebSocketClient();
    WebSocketHandler handler = new StompClientHandler();

    WebSocketConnectionManager manager = new WebSocketConnectionManager(
            client,
            handler,
            "ws://localhost:8080/stomp"
    );

    // Request STOMP sub-protocol
    manager.setSubProtocols(Arrays.asList("v12.stomp", "v11.stomp"));

    return manager;
}

Connection with Error Handling

import org.springframework.web.socket.client.standard.StandardWebSocketClient;

public class RobustWebSocketClient {

    public void connect() {
        StandardWebSocketClient client = new StandardWebSocketClient();

        WebSocketHandler handler = new TextWebSocketHandler() {
            @Override
            public void afterConnectionEstablished(WebSocketSession session)
                    throws Exception {
                System.out.println("Connected successfully");
            }

            @Override
            public void handleTransportError(WebSocketSession session, Throwable exception)
                    throws Exception {
                System.err.println("Transport error: " + exception.getMessage());
            }

            @Override
            public void afterConnectionClosed(WebSocketSession session, CloseStatus status)
                    throws Exception {
                System.out.println("Connection closed: " + status);
                // Implement reconnection logic here
                scheduleReconnect();
            }
        };

        client.execute(handler, "ws://localhost:8080/ws")
                .thenAccept(session -> {
                    System.out.println("Connection established");
                })
                .exceptionally(ex -> {
                    System.err.println("Failed to connect: " + ex.getMessage());
                    scheduleReconnect();
                    return null;
                });
    }

    private void scheduleReconnect() {
        // Schedule reconnection attempt
        new Timer().schedule(new TimerTask() {
            @Override
            public void run() {
                connect();
            }
        }, 5000);  // Retry after 5 seconds
    }
}

JSR-356 ClientEndpoint with Spring

import jakarta.websocket.*;
import org.springframework.web.socket.client.standard.AnnotatedEndpointConnectionManager;

@ClientEndpoint
public class MyClientEndpoint {

    @OnOpen
    public void onOpen(Session session) {
        System.out.println("Connected: " + session.getId());
        try {
            session.getBasicRemote().sendText("Hello from client");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @OnMessage
    public void onMessage(String message) {
        System.out.println("Received: " + message);
    }

    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        System.out.println("Closed: " + closeReason);
    }

    @OnError
    public void onError(Throwable error) {
        System.err.println("Error: " + error.getMessage());
    }
}

@Configuration
public class ClientConfig {

    @Bean
    public AnnotatedEndpointConnectionManager endpointManager() {
        MyClientEndpoint endpoint = new MyClientEndpoint();

        return new AnnotatedEndpointConnectionManager(
                endpoint,
                "ws://localhost:8080/ws"
        );
    }
}

Async Connection Handling

import java.util.concurrent.CompletableFuture;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;

public class AsyncWebSocketClient {

    public CompletableFuture<String> sendAndReceive(String message) {
        CompletableFuture<String> responseFuture = new CompletableFuture<>();
        StandardWebSocketClient client = new StandardWebSocketClient();

        WebSocketHandler handler = new TextWebSocketHandler() {
            private WebSocketSession currentSession;

            @Override
            public void afterConnectionEstablished(WebSocketSession session)
                    throws Exception {
                this.currentSession = session;
                session.sendMessage(new TextMessage(message));
            }

            @Override
            protected void handleTextMessage(WebSocketSession session, TextMessage msg)
                    throws Exception {
                // Complete future with response
                responseFuture.complete(msg.getPayload());
                session.close();
            }

            @Override
            public void handleTransportError(WebSocketSession session, Throwable exception)
                    throws Exception {
                responseFuture.completeExceptionally(exception);
            }
        };

        client.execute(handler, "ws://localhost:8080/ws")
                .exceptionally(ex -> {
                    responseFuture.completeExceptionally(ex);
                    return null;
                });

        return responseFuture;
    }

    public static void main(String[] args) throws Exception {
        AsyncWebSocketClient client = new AsyncWebSocketClient();

        client.sendAndReceive("Hello")
                .thenAccept(response -> System.out.println("Got response: " + response))
                .exceptionally(ex -> {
                    System.err.println("Error: " + ex.getMessage());
                    return null;
                });
    }
}

Connection Pool Pattern

import java.util.concurrent.ConcurrentHashMap;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;

public class WebSocketConnectionPool {
    private final StandardWebSocketClient client = new StandardWebSocketClient();
    private final Map<String, WebSocketSession> connections = new ConcurrentHashMap<>();

    public CompletableFuture<WebSocketSession> getConnection(String uri) {
        WebSocketSession existing = connections.get(uri);

        if (existing != null && existing.isOpen()) {
            return CompletableFuture.completedFuture(existing);
        }

        // Create new connection
        return client.execute(createHandler(uri), uri)
                .thenApply(session -> {
                    connections.put(uri, session);
                    return session;
                });
    }

    private WebSocketHandler createHandler(String uri) {
        return new TextWebSocketHandler() {
            @Override
            public void afterConnectionClosed(WebSocketSession session, CloseStatus status)
                    throws Exception {
                connections.remove(uri);
            }
        };
    }

    public void closeAll() {
        connections.values().forEach(session -> {
            try {
                if (session.isOpen()) {
                    session.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
        connections.clear();
    }
}