or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

containers.mdinbound.mdindex.mdoutbound.mdprotocols.mdsockjs.md
tile.json

containers.mddocs/

WebSocket Containers

WebSocket containers provide high-level connection management for both client and server-side WebSocket connections. They handle session lifecycle, protocol negotiation, and provide a unified abstraction over Spring's WebSocket infrastructure.

Capabilities

IntegrationWebSocketContainer

Abstract base class providing common container functionality for WebSocket connection management.

/**
 * Abstract base class for WebSocket containers.
 * Manages WebSocket sessions and delegates events to WebSocketListener.
 * Thread-safe for concurrent session access.
 *
 * @since 4.1
 */
public abstract class IntegrationWebSocketContainer implements DisposableBean {
    public static final int DEFAULT_SEND_TIME_LIMIT = 10000; // 10 seconds
    public static final int DEFAULT_SEND_BUFFER_SIZE = 524288; // 512 KB

    /**
     * Set the timeout for sending messages in milliseconds.
     * Default: 10000 (10 seconds)
     * Messages that take longer than this timeout may trigger buffer overflow.
     *
     * @param sendTimeLimit timeout in milliseconds (must be > 0)
     * @throws IllegalArgumentException if sendTimeLimit <= 0
     */
    public void setSendTimeLimit(int sendTimeLimit);

    /**
     * Set the send buffer size limit in bytes.
     * Default: 524288 (512 KB)
     * When buffer exceeds this limit, overflow strategy is applied.
     *
     * @param sendBufferSizeLimit buffer size in bytes (must be > 0)
     * @throws IllegalArgumentException if sendBufferSizeLimit <= 0
     */
    public void setSendBufferSizeLimit(int sendBufferSizeLimit);

    /**
     * Set the send buffer overflow strategy for concurrent sends.
     * Determines behavior when buffer reaches configured limit.
     * Default: TERMINATE
     *
     * @param overflowStrategy the overflow strategy (must not be null)
     * @throws IllegalArgumentException if overflowStrategy is null
     * @since 5.5.19
     */
    public void setSendBufferOverflowStrategy(
        ConcurrentWebSocketSessionDecorator.OverflowStrategy overflowStrategy);

    /**
     * Replace the default WebSocketHandler with a custom one.
     * Useful for applying decorator factories or advanced customization.
     * Must be called before container is started.
     *
     * @param handler the WebSocketHandler to use (must not be null)
     * @throws IllegalStateException if container is already started
     * @since 5.5.18
     */
    protected void setWebSocketHandler(WebSocketHandler handler);

    /**
     * Set the WebSocketListener to receive session events and messages.
     * Typically set to WebSocketInboundChannelAdapter.
     *
     * @param messageListener the listener implementation (must not be null)
     * @throws IllegalArgumentException if messageListener is null
     */
    public void setMessageListener(WebSocketListener messageListener);

    /**
     * Set the supported WebSocket sub-protocols.
     * Replaces any previously configured protocols.
     *
     * @param protocols the sub-protocol names (may be empty, null values ignored)
     */
    public void setSupportedProtocols(String... protocols);

    /**
     * Add supported WebSocket sub-protocols.
     * Adds to existing configured protocols.
     *
     * @param protocols the sub-protocol names to add (null values ignored)
     */
    public void addSupportedProtocols(String... protocols);

    /**
     * Get the internal WebSocket handler.
     *
     * @return the WebSocketHandler instance (may be null if not initialized)
     */
    public WebSocketHandler getWebSocketHandler();

    /**
     * Get all supported sub-protocols from both the listener and container.
     *
     * @return list of supported sub-protocol names (never null, may be empty)
     */
    public List<String> getSubProtocols();

    /**
     * Get all active WebSocket sessions.
     * Thread-safe: returns unmodifiable view of session map.
     *
     * @return unmodifiable map of session ID to WebSocketSession (never null)
     */
    public Map<String, WebSocketSession> getSessions();

    /**
     * Get a specific WebSocket session by ID.
     *
     * @param sessionId the session identifier (must not be null)
     * @return the WebSocketSession (never null)
     * @throws IllegalStateException if session not found
     * @throws IllegalArgumentException if sessionId is null
     */
    public WebSocketSession getSession(String sessionId);

    /**
     * Close a WebSocket session with the specified status.
     *
     * @param session the session to close (must not be null)
     * @param closeStatus the close status (must not be null)
     * @throws Exception if closing fails
     * @throws IllegalArgumentException if session or closeStatus is null
     */
    public void closeSession(WebSocketSession session, CloseStatus closeStatus)
        throws Exception;

    /**
     * Cleanup method that closes all active sessions.
     * Called on container destruction.
     * Thread-safe: closes all sessions safely.
     */
    @Override
    public void destroy();
}

ClientWebSocketContainer

Client-side WebSocket container for connecting to remote WebSocket servers.

/**
 * Client-side WebSocket container implementation.
 * Manages outbound connection to a WebSocket server.
 * Thread-safe for concurrent operations.
 *
 * @since 4.1
 */
public class ClientWebSocketContainer extends IntegrationWebSocketContainer
        implements SmartLifecycle {

    /**
     * Create client container with URI template and variables.
     *
     * @param client the WebSocketClient implementation (must not be null)
     * @param uriTemplate the URI template (e.g., "ws://localhost:8080/ws/{param}")
     * @param uriVariables values to substitute in template
     * @throws IllegalArgumentException if client or uriTemplate is null
     */
    public ClientWebSocketContainer(
        WebSocketClient client,
        String uriTemplate,
        Object... uriVariables);

    /**
     * Create client container with prepared URI.
     *
     * @param client the WebSocketClient implementation (must not be null)
     * @param uri the complete WebSocket URI (must not be null)
     * @throws IllegalArgumentException if client or uri is null
     * @since 6.1
     */
    public ClientWebSocketContainer(WebSocketClient client, URI uri);

    /**
     * Set the Origin header for the WebSocket handshake.
     *
     * @param origin the origin value (may be null to remove)
     */
    public void setOrigin(String origin);

    /**
     * Set HTTP headers from a map for the WebSocket handshake.
     *
     * @param headers map of header name to comma-delimited values (must not be null)
     * @throws IllegalArgumentException if headers is null
     */
    public void setHeadersMap(Map<String, String> headers);

    /**
     * Set HTTP headers for the WebSocket handshake.
     *
     * @param headers the HttpHeaders instance (must not be null)
     * @throws IllegalArgumentException if headers is null
     */
    public void setHeaders(HttpHeaders headers);

    /**
     * Set the connection timeout in seconds.
     * Default: 10 seconds
     *
     * @param connectionTimeout timeout in seconds (must be > 0)
     * @throws IllegalArgumentException if connectionTimeout <= 0
     * @since 4.2
     */
    public void setConnectionTimeout(int connectionTimeout);

    /**
     * Get the established client session.
     * If not connected and running, attempts to reconnect.
     * Waits up to connectionTimeout for connection.
     *
     * @param sessionId ignored for client container (can be null)
     * @return the established WebSocketSession (never null)
     * @throws IllegalStateException if session cannot be established
     */
    @Override
    public WebSocketSession getSession(String sessionId);

    /**
     * Check if the client session is currently open.
     *
     * @return true if session is open and connected
     * @since 4.2.6
     */
    public boolean isConnected();

    /**
     * Configure auto-startup behavior.
     *
     * @param autoStartup true to start automatically on application context startup
     */
    public void setAutoStartup(boolean autoStartup);

    /**
     * Set the lifecycle phase.
     *
     * @param phase the phase value (lower values start earlier)
     */
    public void setPhase(int phase);

    // SmartLifecycle methods
    @Override
    public boolean isAutoStartup();

    @Override
    public int getPhase();

    @Override
    public boolean isRunning();

    @Override
    public void start();

    @Override
    public void stop();

    @Override
    public void stop(Runnable callback);
}

Usage Example:

import org.springframework.integration.websocket.ClientWebSocketContainer;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.http.HttpHeaders;

// Create client with URI template
WebSocketClient client = new StandardWebSocketClient();
ClientWebSocketContainer container = new ClientWebSocketContainer(
    client,
    "ws://localhost:8080/websocket/{userId}",
    "user123"
);

// Configure headers
HttpHeaders headers = new HttpHeaders();
headers.add("Authorization", "Bearer token123");
container.setHeaders(headers);

// Set connection timeout
container.setConnectionTimeout(15); // 15 seconds

// Configure send buffer
container.setSendBufferSizeLimit(1024 * 1024); // 1 MB
container.setSendTimeLimit(20000); // 20 seconds
container.setSendBufferOverflowStrategy(
    ConcurrentWebSocketSessionDecorator.OverflowStrategy.DROP_OLDEST
);

// Configure lifecycle
container.setAutoStartup(true);
container.setPhase(100);

// Start connection
container.start();

// Wait for connection
if (!container.isConnected()) {
    // Handle connection failure
    throw new IllegalStateException("Failed to connect");
}

// Get the session
WebSocketSession session = container.getSession(null);

// Check connection status
if (container.isConnected()) {
    System.out.println("Connected: " + session.getId());
}

ServerWebSocketContainer

Server-side WebSocket container for accepting WebSocket connections from clients.

/**
 * Server-side WebSocket container implementation.
 * Registers WebSocket handlers for specified paths.
 * Thread-safe for concurrent client connections.
 *
 * @since 4.1
 */
public class ServerWebSocketContainer extends IntegrationWebSocketContainer
        implements WebSocketConfigurer, SmartLifecycle {

    /**
     * Create server container for specified WebSocket paths.
     *
     * @param paths the URL paths to handle (e.g., "/ws", "/websocket")
     *              Must not be null or empty
     * @throws IllegalArgumentException if paths is null or empty
     */
    public ServerWebSocketContainer(String... paths);

    /**
     * Set the handshake handler.
     * Required for server operation.
     * Must be called before container is started.
     *
     * @param handshakeHandler the HandshakeHandler implementation (must not be null)
     * @return this container for fluent configuration
     * @throws IllegalArgumentException if handshakeHandler is null
     * @throws IllegalStateException if container is already started
     */
    public ServerWebSocketContainer setHandshakeHandler(HandshakeHandler handshakeHandler);

    /**
     * Set handshake interceptors.
     *
     * @param interceptors the HandshakeInterceptor instances (may be empty)
     * @return this container for fluent configuration
     */
    public ServerWebSocketContainer setInterceptors(HandshakeInterceptor... interceptors);

    /**
     * Configure WebSocket handler decorator factories.
     * Useful for advanced use cases like Spring Security integration.
     *
     * @param factories the WebSocketHandlerDecoratorFactory instances (may be empty)
     * @return this container for fluent configuration
     * @since 4.2
     */
    public ServerWebSocketContainer setDecoratorFactories(
        WebSocketHandlerDecoratorFactory... factories);

    /**
     * Configure allowed CORS origins.
     *
     * @param origins the allowed origin patterns (e.g., "http://localhost:3000", "*")
     *                May be empty to allow all origins
     * @return this container for fluent configuration
     * @since 4.3
     */
    public ServerWebSocketContainer setAllowedOrigins(String... origins);

    /**
     * Enable SockJS fallback support.
     * Can be called without options to use defaults.
     *
     * @param sockJsServiceOptions optional SockJS configuration
     * @return this container for fluent configuration
     */
    public ServerWebSocketContainer withSockJs(SockJsServiceOptions... sockJsServiceOptions);

    /**
     * Set SockJS service options.
     *
     * @param sockJsServiceOptions the SockJS configuration (must not be null)
     * @throws IllegalArgumentException if sockJsServiceOptions is null
     */
    public void setSockJsServiceOptions(SockJsServiceOptions sockJsServiceOptions);

    /**
     * Configure TaskScheduler for SockJS service.
     * Alternative to default SockJS scheduler for runtime registration.
     *
     * @param sockJsTaskScheduler the TaskScheduler instance (must not be null)
     * @throws IllegalArgumentException if sockJsTaskScheduler is null
     * @since 5.5.1
     */
    public void setSockJsTaskScheduler(TaskScheduler sockJsTaskScheduler);

    /**
     * Get the configured SockJS TaskScheduler.
     *
     * @return the TaskScheduler or null if not configured
     */
    public TaskScheduler getSockJsTaskScheduler();

    /**
     * Register WebSocket handlers with the WebSocketHandlerRegistry.
     * Called by Spring WebSocket infrastructure.
     *
     * @param registry the WebSocketHandlerRegistry (must not be null)
     * @throws IllegalArgumentException if registry is null
     */
    @Override
    public void registerWebSocketHandlers(WebSocketHandlerRegistry registry);

    /**
     * Configure auto-startup behavior.
     *
     * @param autoStartup true to start automatically on application context startup
     */
    public void setAutoStartup(boolean autoStartup);

    /**
     * Set the lifecycle phase.
     *
     * @param phase the phase value (lower values start earlier)
     */
    public void setPhase(int phase);

    // SmartLifecycle methods
    @Override
    public boolean isAutoStartup();

    @Override
    public int getPhase();

    @Override
    public boolean isRunning();

    @Override
    public void start();

    @Override
    public void stop();

    @Override
    public void stop(Runnable callback);
}

Usage Example:

import org.springframework.integration.websocket.ServerWebSocketContainer;
import org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy;
import org.springframework.web.socket.server.support.DefaultHandshakeHandler;

// Create server container for multiple paths
ServerWebSocketContainer serverContainer = new ServerWebSocketContainer(
    "/websocket",
    "/ws/messages",
    "/ws/notifications"
);

// Configure handshake handler (required)
DefaultHandshakeHandler handshakeHandler = new DefaultHandshakeHandler(
    new TomcatRequestUpgradeStrategy()
);
serverContainer.setHandshakeHandler(handshakeHandler);

// Configure CORS
serverContainer.setAllowedOrigins(
    "http://localhost:3000",
    "https://app.example.com"
);

// Add custom interceptors
serverContainer.setInterceptors(new HttpSessionHandshakeInterceptor());

// Configure send limits
serverContainer.setSendTimeLimit(20000); // 20 seconds
serverContainer.setSendBufferSizeLimit(1024 * 1024); // 1 MB
serverContainer.setSendBufferOverflowStrategy(
    ConcurrentWebSocketSessionDecorator.OverflowStrategy.DROP_OLDEST
);

// Start the container
serverContainer.start();

// Access sessions
Map<String, WebSocketSession> sessions = serverContainer.getSessions();
System.out.println("Active sessions: " + sessions.size());

Lifecycle Management

Client Container Lifecycle

Startup:

ClientWebSocketContainer container = new ClientWebSocketContainer(client, uri);

// Configure before starting
container.setConnectionTimeout(10);
container.setAutoStartup(true);
container.setPhase(100);

// Start connection
container.start();

// Verify connection
if (!container.isConnected()) {
    // Handle failure
}

Shutdown:

// Stop container
container.stop();

// Or with callback
container.stop(() -> {
    System.out.println("Container stopped");
});

// Container automatically closes session on stop

Server Container Lifecycle

Startup:

ServerWebSocketContainer container = new ServerWebSocketContainer("/ws");

// Configure before starting
container.setHandshakeHandler(handshakeHandler);
container.setAllowedOrigins("*");
container.setAutoStartup(true);
container.setPhase(100);

// Start accepting connections
container.start();

Shutdown:

// Stop container (closes all sessions)
container.stop();

// Or with callback
container.stop(() -> {
    System.out.println("Container stopped, all sessions closed");
});

Important: Always configure containers (handshake handler, CORS, etc.) before calling start().

Error Handling

Client Connection Errors

Connection Timeout:

try {
    container.start();
} catch (Exception e) {
    // Handle connection failure
    logger.error("Failed to connect", e);
    // Container will not be in running state
}

Connection Lost:

// Monitor connection status
if (!container.isConnected()) {
    // Attempt reconnection
    try {
        container.start();
    } catch (Exception e) {
        logger.error("Reconnection failed", e);
    }
}

Server Handshake Errors

Handshake Failures:

  • Handled by HandshakeHandler
  • Failed handshakes don't create sessions
  • Errors logged but don't affect existing sessions

CORS Errors:

// Configure allowed origins
container.setAllowedOrigins("http://localhost:3000");

// Or allow all (not recommended for production)
container.setAllowedOrigins("*");

Session Management Errors

Session Not Found:

try {
    WebSocketSession session = container.getSession(sessionId);
} catch (IllegalStateException e) {
    // Session not found
    logger.warn("Session not found: {}", sessionId);
}

Session Already Closed:

WebSocketSession session = container.getSession(sessionId);
if (session != null && !session.isOpen()) {
    // Session closed
    logger.warn("Session closed: {}", sessionId);
}

Thread Safety

Container Thread Safety

  • IntegrationWebSocketContainer: Thread-safe for concurrent session access
  • ClientWebSocketContainer: Thread-safe for concurrent operations
  • ServerWebSocketContainer: Thread-safe for concurrent client connections
  • Session Map: Uses ConcurrentHashMap internally for thread-safe session storage

Session Access Thread Safety

  • getSessions(): Returns unmodifiable view, safe for concurrent reads
  • getSession(): Thread-safe lookup
  • closeSession(): Thread-safe, can be called concurrently

Best Practice: Multiple threads can safely access container and sessions concurrently.

Performance Considerations

Send Buffer Configuration

Buffer Size:

  • Default: 512 KB per session
  • Larger buffers: Better for high-frequency messages, more memory usage
  • Smaller buffers: Less memory, more frequent overflow

Send Timeout:

  • Default: 10 seconds
  • Longer timeout: Better for slow networks, more buffer accumulation
  • Shorter timeout: Faster failure detection, less buffer accumulation

Overflow Strategy:

  • TERMINATE: Closes session, guarantees no message loss (default)
  • DROP_OLDEST: Drops oldest messages, keeps session open
  • DROP_CURRENT: Drops current message, keeps session open

Optimization:

// High-frequency, non-critical messages
container.setSendBufferSizeLimit(1024 * 1024); // 1 MB
container.setSendBufferOverflowStrategy(
    ConcurrentWebSocketSessionDecorator.OverflowStrategy.DROP_OLDEST
);

// Critical messages requiring delivery
container.setSendBufferSizeLimit(512 * 1024); // 512 KB
container.setSendBufferOverflowStrategy(
    ConcurrentWebSocketSessionDecorator.OverflowStrategy.TERMINATE
);

Session Management Overhead

  • Session lookup: O(1) via ConcurrentHashMap
  • Session storage: ~1-2 KB per active session
  • Session cleanup: Automatic on disconnect

Memory Usage:

  • Each session: ~1-2 KB metadata + send buffer (512 KB default)
  • 1000 sessions: ~500 MB (mostly send buffers)

Troubleshooting

Issue: Client container fails to connect

Symptoms: container.start() throws exception or isConnected() returns false.

Causes:

  1. Server not running or wrong URI
  2. Network connectivity issues
  3. Handshake failure (protocol mismatch, CORS, etc.)

Solutions:

// Verify URI and network
URI uri = URI.create("ws://localhost:8080/websocket");
container = new ClientWebSocketContainer(client, uri);

// Check connection status
if (!container.isConnected()) {
    // Implement retry logic
    container.start();
}

// Verify protocols match
container.setSupportedProtocols("v1.protocol");

// Check network connectivity
// Verify firewall rules
// Check server is running

Issue: Server container not accepting connections

Symptoms: Clients cannot establish WebSocket connections.

Causes:

  1. Handshake handler not configured
  2. CORS restrictions
  3. Path not registered

Solutions:

// Ensure handshake handler is set (required)
serverContainer.setHandshakeHandler(new DefaultHandshakeHandler());

// Configure CORS if needed
serverContainer.setAllowedOrigins("*"); // Or specific origins

// Verify paths are correct
ServerWebSocketContainer container = new ServerWebSocketContainer("/websocket");

// Check container is started
if (!container.isRunning()) {
    container.start();
}

Issue: Sessions closing unexpectedly

Symptoms: Sessions close without explicit close call.

Causes:

  1. Send buffer overflow (TERMINATE strategy)
  2. Network issues
  3. Server/client shutdown

Solutions:

// Check buffer overflow
container.setSendBufferOverflowStrategy(
    ConcurrentWebSocketSessionDecorator.OverflowStrategy.DROP_OLDEST
);

// Increase buffer size
container.setSendBufferSizeLimit(1024 * 1024); // 1 MB

// Monitor session state
for (WebSocketSession session : container.getSessions().values()) {
    if (!session.isOpen()) {
        logger.warn("Session closed: {}", session.getId());
    }
}

Issue: Send buffer overflow

Symptoms: SessionLimitExceededException or sessions closing.

Causes:

  1. Buffer too small for message frequency
  2. Slow network causing buffer accumulation
  3. TERMINATE strategy too aggressive

Solutions:

// Increase buffer size
container.setSendBufferSizeLimit(1024 * 1024); // 1 MB

// Use DROP_OLDEST for non-critical messages
container.setSendBufferOverflowStrategy(
    ConcurrentWebSocketSessionDecorator.OverflowStrategy.DROP_OLDEST
);

// Increase send timeout
container.setSendTimeLimit(30000); // 30 seconds

// Implement message rate limiting

Types

Overflow Strategy

// From Spring Framework
enum ConcurrentWebSocketSessionDecorator.OverflowStrategy {
    /**
     * Throw SessionLimitExceededException when buffer is full.
     * Session is closed with status from exception.
     */
    TERMINATE,

    /**
     * Drop the oldest message when buffer is full.
     * Session remains open.
     */
    DROP_OLDEST,

    /**
     * Drop the current message when buffer is full.
     * Session remains open.
     */
    DROP_CURRENT
}

WebSocket Client

// From Spring Framework - common implementations
interface WebSocketClient {
    CompletableFuture<WebSocketSession> execute(
        WebSocketHandler webSocketHandler,
        WebSocketHttpHeaders headers,
        URI uri);
}

// Standard Java WebSocket client
class StandardWebSocketClient implements WebSocketClient { }

// Jetty WebSocket client
class JettyWebSocketClient implements WebSocketClient { }

// Undertow WebSocket client
class UndertowWebSocketClient implements WebSocketClient { }

Handshake Handler

// From Spring Framework
interface HandshakeHandler {
    boolean doHandshake(
        ServerHttpRequest request,
        ServerHttpResponse response,
        WebSocketHandler wsHandler,
        Map<String, Object> attributes);
}

// Default implementation
class DefaultHandshakeHandler implements HandshakeHandler {
    public DefaultHandshakeHandler(RequestUpgradeStrategy upgradeStrategy);
}

// Tomcat upgrade strategy
class TomcatRequestUpgradeStrategy implements RequestUpgradeStrategy { }

// Jetty upgrade strategy
class JettyRequestUpgradeStrategy implements RequestUpgradeStrategy { }

Handshake Interceptor

// From Spring Framework
interface HandshakeInterceptor {
    boolean beforeHandshake(
        ServerHttpRequest request,
        ServerHttpResponse response,
        WebSocketHandler wsHandler,
        Map<String, Object> attributes);

    void afterHandshake(
        ServerHttpRequest request,
        ServerHttpResponse response,
        WebSocketHandler wsHandler,
        Exception exception);
}

// HTTP session handshake interceptor
class HttpSessionHandshakeInterceptor implements HandshakeInterceptor { }