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

adapter-support.mddocs/

WebSocket Adapter Support

The adapter support package provides implementations that bridge Spring's WebSocket API with native WebSocket runtime implementations. These adapters translate between Spring's unified WebSocket abstractions and platform-specific APIs like Jakarta WebSocket (JSR-356) and Jetty WebSocket.

Capabilities

AbstractWebSocketSession

Base class for WebSocket session implementations. Provides common functionality for managing session state, attributes, and message sending across different native WebSocket implementations.

import java.io.IOException;
import java.util.Map;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.PingMessage;
import org.springframework.web.socket.PongMessage;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketMessage;
import org.springframework.web.socket.adapter.NativeWebSocketSession;

/**
 * Abstract base class for WebSocketSession implementations. Provides common
 * session management functionality including attribute storage, message routing,
 * and lifecycle management.
 *
 * @param <T> the native session type
 * @since 4.0
 */
public abstract class AbstractWebSocketSession<T> implements NativeWebSocketSession {
    /**
     * Create a new instance and associate the given attributes with it.
     *
     * @param attributes the attributes from the HTTP handshake to associate
     *                   with the WebSocket session; the provided attributes
     *                   are copied, the original map is not used
     */
    public AbstractWebSocketSession(Map<String, Object> attributes);

    /**
     * Return the map with attributes associated with the WebSocket session.
     */
    @Override
    public Map<String, Object> getAttributes();

    /**
     * Return the underlying native WebSocketSession.
     *
     * @return the native session object
     */
    @Override
    public T getNativeSession();

    /**
     * Return the underlying native WebSocketSession if it matches the
     * required type.
     *
     * @param requiredType the required type of the native session
     * @return the native session of the required type, or null if not
     *         available or not of the required type
     */
    @Override
    public <R> R getNativeSession(Class<R> requiredType);

    /**
     * Initialize the native session. This must be called before the session
     * can be used.
     *
     * @param session the native session to initialize with
     */
    public void initializeNativeSession(T session);

    /**
     * Send a WebSocket message. Routes to the appropriate send method based
     * on message type (text, binary, ping, or pong).
     *
     * @param message the message to send
     * @throws IOException if sending fails
     */
    @Override
    public final void sendMessage(WebSocketMessage<?> message) throws IOException;

    /**
     * Send a text message to the remote endpoint.
     *
     * @param message the text message to send
     * @throws IOException if sending fails
     */
    protected abstract void sendTextMessage(TextMessage message) throws IOException;

    /**
     * Send a binary message to the remote endpoint.
     *
     * @param message the binary message to send
     * @throws IOException if sending fails
     */
    protected abstract void sendBinaryMessage(BinaryMessage message) throws IOException;

    /**
     * Send a ping message to the remote endpoint.
     *
     * @param message the ping message to send
     * @throws IOException if sending fails
     */
    protected abstract void sendPingMessage(PingMessage message) throws IOException;

    /**
     * Send a pong message to the remote endpoint.
     *
     * @param message the pong message to send
     * @throws IOException if sending fails
     */
    protected abstract void sendPongMessage(PongMessage message) throws IOException;

    /**
     * Close the WebSocket connection with status 1000 (normal closure).
     *
     * @throws IOException if closing fails
     */
    @Override
    public final void close() throws IOException;

    /**
     * Close the WebSocket connection with the given status.
     *
     * @param status the close status
     * @throws IOException if closing fails
     */
    @Override
    public final void close(CloseStatus status) throws IOException;

    /**
     * Perform the actual close operation with the native session.
     *
     * @param status the close status
     * @throws IOException if closing fails
     */
    protected abstract void closeInternal(CloseStatus status) throws IOException;
}

NativeWebSocketSession

Interface for accessing the underlying native WebSocket session object. Extends the standard WebSocketSession interface to provide access to platform-specific session implementations.

import org.springframework.web.socket.WebSocketSession;

/**
 * A WebSocketSession that exposes the underlying native WebSocketSession
 * through getters. Useful for accessing platform-specific features not
 * available through the Spring abstraction.
 *
 * @since 4.0
 */
public interface NativeWebSocketSession extends WebSocketSession {
    /**
     * Return the underlying native WebSocketSession (e.g., Jakarta WebSocket
     * Session, Jetty Session, etc.).
     *
     * @return the native session object
     */
    Object getNativeSession();

    /**
     * Return the underlying native WebSocketSession if it matches the
     * required type.
     *
     * @param requiredType the required type of the session
     * @return the native session of the required type, or null if not
     *         available or not of the required type
     */
    <T> T getNativeSession(Class<T> requiredType);
}

Jakarta WebSocket (JSR-356) Adapter

The standard adapter package provides implementations for Jakarta WebSocket (formerly known as JSR-356), the standard WebSocket API for Java.

StandardWebSocketSession

WebSocket session implementation for Jakarta WebSocket API. Adapts a Jakarta jakarta.websocket.Session to Spring's WebSocketSession interface.

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.security.Principal;
import java.util.List;
import java.util.Map;
import jakarta.websocket.Session;
import org.springframework.http.HttpHeaders;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.PingMessage;
import org.springframework.web.socket.PongMessage;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketExtension;
import org.springframework.web.socket.adapter.AbstractWebSocketSession;

/**
 * A WebSocketSession for use with the standard WebSocket for Java API
 * (Jakarta WebSocket).
 *
 * @since 4.0
 */
public class StandardWebSocketSession extends AbstractWebSocketSession<Session> {
    /**
     * Constructor for a standard WebSocket session.
     *
     * @param headers       the headers of the handshake request
     * @param attributes    the attributes from the HTTP handshake to associate
     *                      with the WebSocket session; the provided attributes
     *                      are copied, the original map is not used
     * @param localAddress  the address on which the request was received
     * @param remoteAddress the address of the remote client
     */
    public StandardWebSocketSession(
            HttpHeaders headers,
            Map<String, Object> attributes,
            InetSocketAddress localAddress,
            InetSocketAddress remoteAddress);

    /**
     * Constructor that associates a user with the WebSocket session.
     *
     * @param headers       the headers of the handshake request
     * @param attributes    the attributes from the HTTP handshake to associate
     *                      with the WebSocket session
     * @param localAddress  the address on which the request was received
     * @param remoteAddress the address of the remote client
     * @param user          the user associated with the session; if null we'll
     *                      fall back on the user available in the underlying
     *                      WebSocket session
     */
    public StandardWebSocketSession(
            HttpHeaders headers,
            Map<String, Object> attributes,
            InetSocketAddress localAddress,
            InetSocketAddress remoteAddress,
            Principal user);

    /**
     * Return a unique session identifier.
     */
    @Override
    public String getId();

    /**
     * Return the URI used to open the WebSocket connection.
     */
    @Override
    public URI getUri();

    /**
     * Return the headers used in the handshake request.
     */
    @Override
    public HttpHeaders getHandshakeHeaders();

    /**
     * Return the negotiated sub-protocol, or null if none was specified or
     * negotiated successfully.
     */
    @Override
    public String getAcceptedProtocol();

    /**
     * Return the negotiated extensions, or an empty list if none.
     */
    @Override
    public List<WebSocketExtension> getExtensions();

    /**
     * Return the authenticated user for the session, if any.
     */
    @Override
    public Principal getPrincipal();

    /**
     * Return the address on which the request was received.
     */
    @Override
    public InetSocketAddress getLocalAddress();

    /**
     * Return the address of the remote client.
     */
    @Override
    public InetSocketAddress getRemoteAddress();

    /**
     * Configure the maximum size for an incoming text message.
     *
     * @param messageSizeLimit the maximum size in bytes
     */
    @Override
    public void setTextMessageSizeLimit(int messageSizeLimit);

    /**
     * Get the configured maximum size for an incoming text message.
     */
    @Override
    public int getTextMessageSizeLimit();

    /**
     * Configure the maximum size for an incoming binary message.
     *
     * @param messageSizeLimit the maximum size in bytes
     */
    @Override
    public void setBinaryMessageSizeLimit(int messageSizeLimit);

    /**
     * Get the configured maximum size for an incoming binary message.
     */
    @Override
    public int getBinaryMessageSizeLimit();

    /**
     * Return whether the connection is still open.
     */
    @Override
    public boolean isOpen();

    /**
     * Initialize the native Jakarta WebSocket session.
     *
     * @param session the Jakarta WebSocket session
     */
    @Override
    public void initializeNativeSession(Session session);

    /**
     * Send a text message to the remote endpoint.
     *
     * @param message the text message to send
     * @throws IOException if sending fails
     */
    @Override
    protected void sendTextMessage(TextMessage message) throws IOException;

    /**
     * Send a binary message to the remote endpoint.
     *
     * @param message the binary message to send
     * @throws IOException if sending fails
     */
    @Override
    protected void sendBinaryMessage(BinaryMessage message) throws IOException;

    /**
     * Send a ping message to the remote endpoint.
     *
     * @param message the ping message to send
     * @throws IOException if sending fails
     */
    @Override
    protected void sendPingMessage(PingMessage message) throws IOException;

    /**
     * Send a pong message to the remote endpoint.
     *
     * @param message the pong message to send
     * @throws IOException if sending fails
     */
    @Override
    protected void sendPongMessage(PongMessage message) throws IOException;

    /**
     * Close the WebSocket connection with the given status.
     *
     * @param status the close status
     * @throws IOException if closing fails
     */
    @Override
    protected void closeInternal(CloseStatus status) throws IOException;
}

StandardWebSocketHandlerAdapter

Adapts a Spring WebSocketHandler to the Jakarta WebSocket API by extending jakarta.websocket.Endpoint. Handles message routing, lifecycle events, and error handling.

import java.nio.ByteBuffer;
import jakarta.websocket.CloseReason;
import jakarta.websocket.Endpoint;
import jakarta.websocket.EndpointConfig;
import jakarta.websocket.Session;
import org.springframework.web.socket.WebSocketHandler;

/**
 * Adapts a WebSocketHandler to the standard WebSocket for Java API
 * (Jakarta WebSocket). This class extends jakarta.websocket.Endpoint
 * and delegates all lifecycle events to the Spring WebSocketHandler.
 *
 * @since 4.0
 */
public class StandardWebSocketHandlerAdapter extends Endpoint {
    /**
     * Create a new adapter for the given handler and session.
     *
     * @param handler   the WebSocketHandler to adapt
     * @param wsSession the StandardWebSocketSession to use
     */
    public StandardWebSocketHandlerAdapter(
            WebSocketHandler handler,
            StandardWebSocketSession wsSession);

    /**
     * Called when the WebSocket connection is opened. Initializes the session
     * and registers message handlers based on whether partial messages are
     * supported. Then delegates to the WebSocketHandler's
     * afterConnectionEstablished method.
     *
     * @param session the Jakarta WebSocket session
     * @param config  the endpoint configuration
     */
    @Override
    public void onOpen(Session session, EndpointConfig config);

    /**
     * Called when the WebSocket connection is closed. Delegates to the
     * WebSocketHandler's afterConnectionClosed method.
     *
     * @param session the Jakarta WebSocket session
     * @param reason  the reason for closure
     */
    @Override
    public void onClose(Session session, CloseReason reason);

    /**
     * Called when a transport error occurs. Delegates to the
     * WebSocketHandler's handleTransportError method.
     *
     * @param session   the Jakarta WebSocket session
     * @param exception the exception that occurred
     */
    @Override
    public void onError(Session session, Throwable exception);
}

StandardToWebSocketExtensionAdapter

Adapter that converts a Jakarta WebSocket Extension to Spring's WebSocketExtension format.

import java.util.Map;
import jakarta.websocket.Extension;
import org.springframework.web.socket.WebSocketExtension;

/**
 * A subclass of WebSocketExtension that can be constructed from a
 * Jakarta WebSocket Extension. Adapts the Jakarta extension interface
 * to Spring's extension representation.
 *
 * @since 4.0
 */
public class StandardToWebSocketExtensionAdapter extends WebSocketExtension {
    /**
     * Create a new adapter for the given Jakarta WebSocket extension.
     *
     * @param extension the Jakarta WebSocket extension to adapt
     */
    public StandardToWebSocketExtensionAdapter(Extension extension);
}

WebSocketToStandardExtensionAdapter

Adapter that converts a Spring WebSocketExtension to Jakarta WebSocket Extension format.

import java.util.List;
import jakarta.websocket.Extension;
import org.springframework.web.socket.WebSocketExtension;

/**
 * Adapts an instance of Spring's WebSocketExtension to the Jakarta
 * WebSocket Extension interface. Allows Spring extensions to be used
 * with Jakarta WebSocket APIs.
 *
 * @since 4.0
 */
public class WebSocketToStandardExtensionAdapter implements Extension {
    /**
     * Create a new adapter for the given Spring WebSocketExtension.
     *
     * @param extension the Spring WebSocketExtension to adapt
     */
    public WebSocketToStandardExtensionAdapter(WebSocketExtension extension);

    /**
     * Return the extension name.
     */
    @Override
    public String getName();

    /**
     * Return the extension parameters as a list of Parameter objects.
     */
    @Override
    public List<Extension.Parameter> getParameters();
}

ConvertingEncoderDecoderSupport

Base class for implementing Jakarta WebSocket Encoder and Decoder that delegate to Spring's ConversionService. This enables automatic conversion between domain objects and WebSocket message payloads.

import java.nio.ByteBuffer;
import jakarta.websocket.DecodeException;
import jakarta.websocket.Decoder;
import jakarta.websocket.EncodeException;
import jakarta.websocket.Encoder;
import jakarta.websocket.EndpointConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.TypeDescriptor;

/**
 * Base class for implementing Jakarta WebSocket Encoder and/or Decoder.
 * Provides encode and decode method implementations that delegate to a
 * Spring ConversionService.
 *
 * By default, this class looks up a ConversionService registered in the
 * active ApplicationContext under the name 'webSocketConversionService'.
 * This works for both client and server endpoints in a Servlet container.
 *
 * Subclasses can extend this class and should also implement one or both
 * of jakarta.websocket.Encoder and jakarta.websocket.Decoder. For
 * convenience, BinaryEncoder, BinaryDecoder, TextEncoder, and TextDecoder
 * subclasses are provided.
 *
 * Since JSR-356 only allows Encoder/Decoder to be registered by type,
 * instances are managed by the WebSocket runtime and do not need to be
 * registered as Spring Beans. They can, however, be injected with
 * Spring-managed dependencies via @Autowired.
 *
 * @param <T> the type being converted to (for Encoder) or from (for Decoder)
 * @param <M> the WebSocket message type (String or ByteBuffer)
 * @since 4.0
 */
public abstract class ConvertingEncoderDecoderSupport<T, M> {
    /**
     * Called to initialize the encoder/decoder. If running in a
     * Spring-managed context, performs autowiring of dependencies.
     *
     * @param config the endpoint configuration
     */
    public void init(EndpointConfig config);

    /**
     * Called to destroy the encoder/decoder. Subclasses can override
     * to perform cleanup.
     */
    public void destroy();

    /**
     * Strategy method to obtain the ConversionService. By default, expects
     * a bean named 'webSocketConversionService' in the active
     * ApplicationContext. Override this method for custom lookup strategies.
     *
     * @return the ConversionService (never null)
     */
    protected ConversionService getConversionService();

    /**
     * Returns the active ApplicationContext. By default, obtains the context
     * via ContextLoader.getCurrentWebApplicationContext(). Override when not
     * running in a Servlet container.
     *
     * @return the ApplicationContext or null
     */
    protected ApplicationContext getApplicationContext();

    /**
     * Returns the type being converted. By default, resolved using generic
     * type arguments of the class.
     *
     * @return the type descriptor for the conversion type
     */
    protected TypeDescriptor getType();

    /**
     * Returns the WebSocket message type. By default, resolved using generic
     * type arguments of the class.
     *
     * @return the type descriptor for the message type
     */
    protected TypeDescriptor getMessageType();

    /**
     * Encode an object to a WebSocket message using the ConversionService.
     *
     * @param object the object to encode
     * @return the encoded message
     * @throws EncodeException if encoding fails
     */
    public M encode(T object) throws EncodeException;

    /**
     * Determine if a given message can be decoded. Checks if the
     * ConversionService supports the conversion.
     *
     * @param bytes the message to check
     * @return true if the message can be decoded
     */
    public boolean willDecode(M bytes);

    /**
     * Decode a WebSocket message into an object using the ConversionService.
     *
     * @param message the message to decode
     * @return the decoded object
     * @throws DecodeException if decoding fails
     */
    public T decode(M message) throws DecodeException;

    /**
     * A binary Encoder that delegates to Spring's ConversionService.
     * Extend this class to create a custom binary encoder.
     *
     * @param <T> the type that this Encoder can convert to
     */
    public abstract static class BinaryEncoder<T>
            extends ConvertingEncoderDecoderSupport<T, ByteBuffer>
            implements Encoder.Binary<T> {
    }

    /**
     * A binary Decoder that delegates to Spring's ConversionService.
     * Extend this class to create a custom binary decoder.
     *
     * @param <T> the type that this Decoder can convert from
     */
    public abstract static class BinaryDecoder<T>
            extends ConvertingEncoderDecoderSupport<T, ByteBuffer>
            implements Decoder.Binary<T> {
    }

    /**
     * A text Encoder that delegates to Spring's ConversionService.
     * Extend this class to create a custom text encoder.
     *
     * @param <T> the type that this Encoder can convert to
     */
    public abstract static class TextEncoder<T>
            extends ConvertingEncoderDecoderSupport<T, String>
            implements Encoder.Text<T> {
    }

    /**
     * A text Decoder that delegates to Spring's ConversionService.
     * Extend this class to create a custom text decoder.
     *
     * @param <T> the type that this Decoder can convert from
     */
    public abstract static class TextDecoder<T>
            extends ConvertingEncoderDecoderSupport<T, String>
            implements Decoder.Text<T> {
    }
}

Jetty WebSocket Adapter

The Jetty adapter package provides implementations for Eclipse Jetty's native WebSocket API.

JettyWebSocketSession

WebSocket session implementation for Jetty WebSocket API. Adapts a Jetty org.eclipse.jetty.websocket.api.Session to Spring's WebSocketSession interface.

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.URI;
import java.security.Principal;
import java.util.List;
import java.util.Map;
import org.eclipse.jetty.websocket.api.Session;
import org.springframework.http.HttpHeaders;
import org.springframework.web.socket.BinaryMessage;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.PingMessage;
import org.springframework.web.socket.PongMessage;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketExtension;
import org.springframework.web.socket.adapter.AbstractWebSocketSession;

/**
 * A WebSocketSession for use with the Jetty WebSocket API.
 *
 * @since 4.0
 */
public class JettyWebSocketSession extends AbstractWebSocketSession<Session> {
    /**
     * Create a new JettyWebSocketSession instance.
     *
     * @param attributes the attributes from the HTTP handshake to associate
     *                   with the WebSocket session
     */
    public JettyWebSocketSession(Map<String, Object> attributes);

    /**
     * Create a new JettyWebSocketSession instance associated with the given
     * user.
     *
     * @param attributes the attributes from the HTTP handshake to associate
     *                   with the WebSocket session; the provided attributes
     *                   are copied, the original map is not used
     * @param user       the user associated with the session; if null we'll
     *                   fall back on the user available via Jetty's
     *                   Session.getUpgradeRequest()
     */
    public JettyWebSocketSession(Map<String, Object> attributes, Principal user);

    /**
     * Return a unique session identifier.
     */
    @Override
    public String getId();

    /**
     * Return the URI used to open the WebSocket connection.
     */
    @Override
    public URI getUri();

    /**
     * Return the headers used in the handshake request.
     */
    @Override
    public HttpHeaders getHandshakeHeaders();

    /**
     * Return the negotiated sub-protocol, or null if none was specified or
     * negotiated successfully.
     */
    @Override
    public String getAcceptedProtocol();

    /**
     * Return the negotiated extensions, or an empty list if none.
     */
    @Override
    public List<WebSocketExtension> getExtensions();

    /**
     * Return the authenticated user for the session, if any.
     */
    @Override
    public Principal getPrincipal();

    /**
     * Return the address on which the request was received.
     */
    @Override
    public InetSocketAddress getLocalAddress();

    /**
     * Return the address of the remote client.
     */
    @Override
    public InetSocketAddress getRemoteAddress();

    /**
     * Configure the maximum size for an incoming text message.
     *
     * @param messageSizeLimit the maximum size in bytes
     */
    @Override
    public void setTextMessageSizeLimit(int messageSizeLimit);

    /**
     * Get the configured maximum size for an incoming text message.
     */
    @Override
    public int getTextMessageSizeLimit();

    /**
     * Configure the maximum size for an incoming binary message.
     *
     * @param messageSizeLimit the maximum size in bytes
     */
    @Override
    public void setBinaryMessageSizeLimit(int messageSizeLimit);

    /**
     * Get the configured maximum size for an incoming binary message.
     */
    @Override
    public int getBinaryMessageSizeLimit();

    /**
     * Return whether the connection is still open.
     */
    @Override
    public boolean isOpen();

    /**
     * Initialize the native Jetty WebSocket session.
     *
     * @param session the Jetty WebSocket session
     */
    @Override
    public void initializeNativeSession(Session session);

    /**
     * Send a text message to the remote endpoint.
     *
     * @param message the text message to send
     * @throws IOException if sending fails
     */
    @Override
    protected void sendTextMessage(TextMessage message) throws IOException;

    /**
     * Send a binary message to the remote endpoint.
     *
     * @param message the binary message to send
     * @throws IOException if sending fails
     */
    @Override
    protected void sendBinaryMessage(BinaryMessage message) throws IOException;

    /**
     * Send a ping message to the remote endpoint.
     *
     * @param message the ping message to send
     * @throws IOException if sending fails
     */
    @Override
    protected void sendPingMessage(PingMessage message) throws IOException;

    /**
     * Send a pong message to the remote endpoint.
     *
     * @param message the pong message to send
     * @throws IOException if sending fails
     */
    @Override
    protected void sendPongMessage(PongMessage message) throws IOException;

    /**
     * Close the WebSocket connection with the given status.
     *
     * @param status the close status
     * @throws IOException if closing fails
     */
    @Override
    protected void closeInternal(CloseStatus status) throws IOException;
}

JettyWebSocketHandlerAdapter

Adapts a Spring WebSocketHandler to the Jetty WebSocket API by implementing org.eclipse.jetty.websocket.api.Session.Listener. Handles message routing, lifecycle events, and error handling.

import java.nio.ByteBuffer;
import org.eclipse.jetty.websocket.api.Callback;
import org.eclipse.jetty.websocket.api.Session;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.WebSocketHandler;

/**
 * Adapts a WebSocketHandler to the Jetty WebSocket API by implementing
 * Session.Listener. Delegates all lifecycle events to the Spring
 * WebSocketHandler.
 *
 * @since 4.0
 */
public class JettyWebSocketHandlerAdapter implements Session.Listener {
    /**
     * Create a new adapter for the given handler and session.
     *
     * @param webSocketHandler the WebSocketHandler to adapt
     * @param wsSession        the JettyWebSocketSession to use
     */
    public JettyWebSocketHandlerAdapter(
            WebSocketHandler webSocketHandler,
            JettyWebSocketSession wsSession);

    /**
     * Called when the WebSocket connection is opened. Initializes the session,
     * demands the first frame, and delegates to the WebSocketHandler's
     * afterConnectionEstablished method.
     *
     * @param session the Jetty WebSocket session
     */
    @Override
    public void onWebSocketOpen(Session session);

    /**
     * Called when a text message is received. Delegates to the
     * WebSocketHandler's handleMessage method and demands the next frame.
     *
     * @param payload the text message payload
     */
    @Override
    public void onWebSocketText(String payload);

    /**
     * Called when a binary message is received. Delegates to the
     * WebSocketHandler's handleMessage method and demands the next frame.
     *
     * @param payload  the binary message payload
     * @param callback the callback to invoke when processing is complete
     */
    @Override
    public void onWebSocketBinary(ByteBuffer payload, Callback callback);

    /**
     * Called when a pong message is received. Delegates to the
     * WebSocketHandler's handleMessage method and demands the next frame.
     *
     * @param payload the pong message payload
     */
    @Override
    public void onWebSocketPong(ByteBuffer payload);

    /**
     * Called when the WebSocket connection is closed. Delegates to the
     * WebSocketHandler's afterConnectionClosed method.
     *
     * @param statusCode the close status code
     * @param reason     the close reason phrase
     * @param callback   the callback to invoke when processing is complete
     */
    @Override
    public void onWebSocketClose(int statusCode, String reason, Callback callback);

    /**
     * Called when a transport error occurs. Delegates to the
     * WebSocketHandler's handleTransportError method.
     *
     * @param cause the exception that occurred
     */
    @Override
    public void onWebSocketError(Throwable cause);
}

WebSocketToJettyExtensionConfigAdapter

Adapter that converts a Spring WebSocketExtension to Jetty's ExtensionConfig format.

import org.eclipse.jetty.websocket.api.ExtensionConfig;
import org.eclipse.jetty.websocket.common.JettyExtensionConfig;
import org.springframework.web.socket.WebSocketExtension;

/**
 * Adapter class to convert a Spring WebSocketExtension to a Jetty
 * ExtensionConfig. Extends JettyExtensionConfig to provide compatibility
 * with Jetty's WebSocket API.
 *
 * @since 4.0
 */
public class WebSocketToJettyExtensionConfigAdapter extends JettyExtensionConfig {
    /**
     * Create a new adapter for the given Spring WebSocketExtension.
     *
     * @param extension the Spring WebSocketExtension to adapt
     */
    public WebSocketToJettyExtensionConfigAdapter(WebSocketExtension extension);
}

Usage Examples

Accessing Native Session Features

import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.adapter.NativeWebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import jakarta.websocket.Session;

public class NativeFeatureHandler extends TextWebSocketHandler {
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // Access native Jakarta WebSocket session for platform-specific features
        if (session instanceof NativeWebSocketSession nativeSession) {
            Session jakartaSession = nativeSession.getNativeSession(Session.class);

            if (jakartaSession != null) {
                // Use Jakarta-specific features
                jakartaSession.setMaxIdleTimeout(300000); // 5 minutes
                System.out.println("Native session ID: " + jakartaSession.getId());
            }
        }
    }

    @Override
    protected void handleTextMessage(WebSocketSession session, TextMessage message)
            throws Exception {
        session.sendMessage(new TextMessage("Echo: " + message.getPayload()));
    }
}

Custom Jakarta WebSocket Encoder

import java.nio.ByteBuffer;
import jakarta.websocket.EncodeException;
import org.springframework.web.socket.adapter.standard.ConvertingEncoderDecoderSupport;

public class PersonEncoder extends ConvertingEncoderDecoderSupport.TextEncoder<Person> {
    // No additional code needed - encoding is handled automatically
    // by the ConversionService
}

// Register the encoder in your endpoint configuration
import jakarta.websocket.server.ServerEndpointConfig;

@Configuration
public class WebSocketConfig {
    @Bean
    public ServerEndpointConfig.Configurator endpointConfigurator() {
        return new ServerEndpointConfig.Configurator() {
            @Override
            public <T> T getEndpointInstance(Class<T> endpointClass)
                    throws InstantiationException {
                // Configure encoders/decoders here
                return super.getEndpointInstance(endpointClass);
            }
        };
    }

    @Bean
    public ConversionService webSocketConversionService() {
        DefaultConversionService service = new DefaultConversionService();
        // Register custom converters for Person <-> String
        service.addConverter(new PersonToStringConverter());
        service.addConverter(new StringToPersonConverter());
        return service;
    }
}

Custom Binary Decoder

import java.nio.ByteBuffer;
import jakarta.websocket.DecodeException;
import org.springframework.web.socket.adapter.standard.ConvertingEncoderDecoderSupport;

public class ImageDecoder extends ConvertingEncoderDecoderSupport.BinaryDecoder<Image> {
    // Decoding is handled automatically by the ConversionService
    // Just register a ByteBuffer -> Image converter
}

// Converter implementation
import org.springframework.core.convert.converter.Converter;

public class ByteBufferToImageConverter implements Converter<ByteBuffer, Image> {
    @Override
    public Image convert(ByteBuffer source) {
        byte[] bytes = new byte[source.remaining()];
        source.get(bytes);
        return Image.fromBytes(bytes);
    }
}

Detecting WebSocket Implementation at Runtime

import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.adapter.NativeWebSocketSession;
import org.springframework.web.socket.adapter.jetty.JettyWebSocketSession;
import org.springframework.web.socket.adapter.standard.StandardWebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

public class AdaptiveHandler extends TextWebSocketHandler {
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        if (session instanceof StandardWebSocketSession) {
            System.out.println("Using Jakarta WebSocket (JSR-356) implementation");

            jakarta.websocket.Session nativeSession =
                ((NativeWebSocketSession) session).getNativeSession(
                    jakarta.websocket.Session.class);

            // Configure Jakarta-specific settings
            if (nativeSession != null) {
                nativeSession.setMaxTextMessageBufferSize(128 * 1024);
            }

        } else if (session instanceof JettyWebSocketSession) {
            System.out.println("Using Jetty WebSocket implementation");

            org.eclipse.jetty.websocket.api.Session nativeSession =
                ((NativeWebSocketSession) session).getNativeSession(
                    org.eclipse.jetty.websocket.api.Session.class);

            // Configure Jetty-specific settings
            if (nativeSession != null) {
                nativeSession.setMaxTextMessageSize(128 * 1024);
            }
        }
    }
}

Session Initialization and Lifecycle

import java.util.HashMap;
import java.util.Map;
import org.springframework.http.HttpHeaders;
import org.springframework.web.socket.adapter.standard.StandardWebSocketSession;
import jakarta.websocket.Session;

public class SessionLifecycleExample {
    public void createAndInitializeSession(
            Session jakartaSession,
            HttpHeaders headers,
            InetSocketAddress localAddr,
            InetSocketAddress remoteAddr) {

        // Prepare attributes from handshake
        Map<String, Object> attributes = new HashMap<>();
        attributes.put("connectionTime", System.currentTimeMillis());
        attributes.put("clientIp", remoteAddr.getAddress().getHostAddress());

        // Create Spring WebSocket session wrapper
        StandardWebSocketSession wsSession = new StandardWebSocketSession(
            headers,
            attributes,
            localAddr,
            remoteAddr
        );

        // Initialize with native session (must be called before use)
        wsSession.initializeNativeSession(jakartaSession);

        // Now the session is ready for use
        System.out.println("Session ID: " + wsSession.getId());
        System.out.println("URI: " + wsSession.getUri());
        System.out.println("Protocol: " + wsSession.getAcceptedProtocol());

        // Access session attributes
        Long connectionTime = (Long) wsSession.getAttributes().get("connectionTime");
        System.out.println("Connected at: " + connectionTime);
    }
}

Working with Extensions

import java.util.List;
import org.springframework.web.socket.WebSocketExtension;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

public class ExtensionAwareHandler extends TextWebSocketHandler {
    @Override
    public void afterConnectionEstablished(WebSocketSession session) throws Exception {
        // Check negotiated extensions
        List<WebSocketExtension> extensions = session.getExtensions();

        for (WebSocketExtension extension : extensions) {
            System.out.println("Extension: " + extension.getName());

            // Check for specific extension
            if ("permessage-deflate".equals(extension.getName())) {
                System.out.println("Compression is enabled");

                // Access extension parameters
                String serverNoContextTakeover =
                    extension.getParameter("server_no_context_takeover");
                System.out.println("Server no context takeover: " +
                    serverNoContextTakeover);
            }
        }
    }
}

Handler Adapter Configuration

import org.springframework.web.socket.WebSocketHandler;
import org.springframework.web.socket.adapter.standard.StandardWebSocketHandlerAdapter;
import org.springframework.web.socket.adapter.standard.StandardWebSocketSession;
import org.springframework.http.HttpHeaders;
import jakarta.websocket.server.ServerEndpointConfig;

public class EndpointConfiguration {
    public void configureEndpoint(
            WebSocketHandler handler,
            HttpHeaders headers,
            Map<String, Object> attributes) {

        // Create Spring WebSocket session
        StandardWebSocketSession wsSession = new StandardWebSocketSession(
            headers,
            attributes,
            null,  // local address
            null   // remote address
        );

        // Create adapter to bridge Spring handler to Jakarta API
        StandardWebSocketHandlerAdapter adapter =
            new StandardWebSocketHandlerAdapter(handler, wsSession);

        // The adapter can now be used as a Jakarta WebSocket Endpoint
        // It will receive Jakarta WebSocket events and delegate to
        // the Spring WebSocketHandler
    }
}