Understanding how to use the WebSocket API is very simple as there are only a few core entities that developers need to be familiar with. We'll begin by covering those first. Once the basics are understood, creating websocket applications should be straight forward.
This interface provides the core functionality needed by developers to both send data to a remote end-point or handle events triggered by the remote end-point sending data. This interface is relevant for both server and client side uses cases.
public interface WebSocket { /** * <p> * Send a text frame to the remote end-point. * <p> * * @return {@link GrizzlyFuture} which could be used to control/check the sending completion state. */ GrizzlyFuture<DataFrame> send(String data); /** * <p> * Send a binary frame to the remote end-point. * </p> * * @return {@link GrizzlyFuture} which could be used to control/check the sending completion state. */ GrizzlyFuture<DataFrame> send(byte[] data); /** * <p> * Sends a <code>ping</code> frame with the specified payload (if any). * </p> * * @param data optional payload. Note that payload length is restricted * to 125 bytes or less. * * @return {@link GrizzlyFuture} which could be used to control/check the sending completion state. * * @since 2.1.9 */ GrizzlyFuture<DataFrame> sendPing(byte[] data); /** * <p> * Sends a <code>ping</code> frame with the specified payload (if any). * </p> * * <p>It may seem odd to send a pong frame, however, RFC-6455 states:</p> * * <p> * "A Pong frame MAY be sent unsolicited. This serves as a * unidirectional heartbeat. A response to an unsolicited Pong frame is * not expected." * </p> * * @param data optional payload. Note that payload length is restricted * to 125 bytes or less. * * @return {@link GrizzlyFuture} which could be used to control/check the sending completion state. * * @since 2.1.9 */ GrizzlyFuture<DataFrame> sendPong(byte[] data); /** * <p> * Sends a fragment of a complete message. * </p> * * @param last boolean indicating if this message fragment is the last. * @param fragment the textual fragment to send. * * @return {@link GrizzlyFuture} which could be used to control/check the sending completion state. */ GrizzlyFuture<DataFrame> stream(boolean last, String fragment); /** * <p> * Sends a fragment of a complete message. * </p> * * @param last boolean indicating if this message fragment is the last. * @param fragment the binary fragment to send. * @param off the offset within the fragment to send. * @param len the number of bytes of the fragment to send. * * @return {@link GrizzlyFuture} which could be used to control/check the sending completion state. */ GrizzlyFuture<DataFrame> stream(boolean last, byte[] fragment, int off, int len); /** * <p> * Closes this {@link WebSocket}. * </p> */ void close(); /** * <p> * Closes this {@link WebSocket} using the specified status code. * </p> * * @param code the closing status code. */ void close(int code); /** * <p> * Closes this {@link WebSocket} using the specified status code and * reason. * </p> * * @param code the closing status code. * @param reason the reason, if any. */ void close(int code, String reason); /** * <p> * Convenience method to determine if this {@link WebSocket} is connected. * </p> * * @return <code>true</code> if the {@link WebSocket} is connected, otherwise * <code>false</code> */ boolean isConnected(); /** * <p> * This callback will be invoked when the opening handshake between both * endpoints has been completed. * </p> */ void onConnect(); /** * <p> * This callback will be invoked when a text message has been received. * </p> * * @param text the text received from the remote end-point. */ void onMessage(String text); /** * <p> * This callback will be invoked when a binary message has been received. * </p> * * @param data the binary data received from the remote end-point. */ void onMessage(byte[] data); /** * <p> * This callback will be invoked when a fragmented textual message has * been received. * </p> * * @param last flag indicating whether or not the payload received is the * final fragment of a message. * @param payload the text received from the remote end-point. */ void onFragment(boolean last, String payload); /** * <p> * This callback will be invoked when a fragmented binary message has * been received. * </p> * * @param last flag indicating whether or not the payload received is the * final fragment of a message. * @param payload the binary data received from the remote end-point. */ void onFragment(boolean last, byte[] payload); /** * <p> * This callback will be invoked when the remote end-point sent a closing * frame. * </p> * * @param frame the close frame from the remote end-point. * * @see DataFrame */ void onClose(DataFrame frame); /** * <p> * This callback will be invoked when the remote end-point has sent a ping * frame. * </p> * * @param frame the ping frame from the remote end-point. * * @see DataFrame */ void onPing(DataFrame frame); /** * <p> * This callback will be invoked when the remote end-point has sent a pong * frame. * </p> * * @param frame the pong frame from the remote end-point. * * @see DataFrame */ void onPong(DataFrame frame); /** * Adds a {@link WebSocketListener} to be notified of events of interest. * * @param listener the {@link WebSocketListener} to add. * * @return <code>true</code> if the listener was added, otherwise * <code>false</code> * * @see WebSocketListener */ boolean add(WebSocketListener listener); /** * Removes the specified {@link WebSocketListener} as a target of event * notification. * * @param listener the {@link WebSocketListener} to remote. * * @return <code>true</code> if the listener was removed, otherwise * <code>false</code> * * @see WebSocketListener */ boolean remove(WebSocketListener listener); }
Implementations of this interface allow the developers to listen for events occurring on various WebSocket instances. This interface is relevant for both server and client side uses cases.
/** * Interface to allow notification of events occurring on specific * {@link WebSocket} instances. */ public interface WebSocketListener { /** * <p> * Invoked when {@link WebSocket#onClose(DataFrame)} has been called on a * particular {@link WebSocket} instance. * <p> * * @param socket the {@link WebSocket} being closed. * @param frame the closing {@link DataFrame} sent by the remote end-point. */ void onClose(WebSocket socket, DataFrame frame); /** * <p> * Invoked when the opening handshake has been completed for a specific * {@link WebSocket} instance. * </p> * * @param socket the newly connected {@link WebSocket} */ void onConnect(WebSocket socket); /** * <p> * Invoked when {@link WebSocket#onMessage(String)} has been called on a * particular {@link WebSocket} instance. * </p> * * @param socket the {@link WebSocket} that received a message. * @param text the message received. */ void onMessage(WebSocket socket, String text); /** * <p> * Invoked when {@link WebSocket#onMessage(String)} has been called on a * particular {@link WebSocket} instance. * </p> * * @param socket the {@link WebSocket} that received a message. * @param bytes the message received. */ void onMessage(WebSocket socket, byte[] bytes); /** * <p> * Invoked when {@link WebSocket#onPing(DataFrame)} has been called on a * particular {@link WebSocket} instance. * </p> * * @param socket the {@link WebSocket} that received the ping. * @param bytes the payload of the ping frame, if any. */ void onPing(WebSocket socket, byte[] bytes); /** * <p> * Invoked when {@link WebSocket#onPong(DataFrame)} has been called on a * particular {@link WebSocket} instance. * </p> * * @param socket the {@link WebSocket} that received the pong. * @param bytes the payload of the pong frame, if any. */ void onPong(WebSocket socket, byte[] bytes); /** * <p> * Invoked when {@link WebSocket#onFragment(boolean, String)} has been called * on a particular {@link WebSocket} instance. * </p> * * @param socket the {@link WebSocket} received the message fragment. * @param fragment the message fragment. * @param last flag indicating if this was the last fragment. */ void onFragment(WebSocket socket, String fragment, boolean last); /** * <p> * Invoked when {@link WebSocket#onFragment(boolean, byte[])} has been called * on a particular {@link WebSocket} instance. * </p> * * @param socket the {@link WebSocket} received the message fragment. * @param fragment the message fragment. * @param last flag indicating if this was the last fragment. */ void onFragment(WebSocket socket, byte[] fragment, boolean last); }
The WebSocketApplication abstract class provides the basics for creating a server-side WebSocket application.
/** * Factory method to create new {@link WebSocket} instances. Developers may * wish to override this to return customized {@link WebSocket} implementations. * * @param handler the {@link ProtocolHandler} to use with the newly created * {@link WebSocket}. * * @param listeners the {@link WebSocketListener}s to associate with the new * {@link WebSocket}. * * @return a new {@link WebSocket} instance. */ public WebSocket createSocket(ProtocolHandler handler, WebSocketListener... listeners) { ... } /** * When a {@link WebSocket#onClose(DataFrame)} is invoked, the {@link WebSocket} * will be unassociated with this application and closed. * * @param socket the {@link WebSocket} being closed. * @param frame the closing frame. */ @Override public void onClose(WebSocket socket, DataFrame frame) { ... } /** * When a new {@link WebSocket} connection is made to this application, the * {@link WebSocket} will be associated with this application. * * @param socket the new {@link WebSocket} connection. */ @Override public void onConnect(WebSocket socket) { ... } /** * Checks protocol specific information can and should be upgraded. * * The default implementation will check for the precence of the * <code>Upgrade</code> header with a value of <code>WebSocket</code>. * If present, {@link #isApplicationRequest(org.glassfish.grizzly.http.HttpRequestPacket)} * will be invoked to determine if the request is a valid websocket request. * * @return <code>true</code> if the request should be upgraded to a * WebSocket connection */ public final boolean upgrade(HttpRequestPacket request) { ... } /** * Checks application specific criteria to determine if this application can * process the request as a WebSocket connection. * * @param request the incoming HTTP request. * * @return <code>true</code> if this application can service this request */ public abstract boolean isApplicationRequest(HttpRequestPacket request);
So to create a simple server-side WebSocket application, the developer, at a minimum, would extend this class and provide an implementation of isApplicationRequest(HttpRequestPacket). Most implementations of this method will most likely key off the request URI to determine if the connection should be upgraded or not. Additional functionality can be added as required for the application - keep in mind that the WebSocketApplication is, itself, a WebSocketListener which will be added to each WebSocket registered with it. This allows handling all of the events exposed by WebSocketListener (as previously discussed). A word of caution - if overriding WebSocketApplication{onConnect,onClosed} be sure to make a call to the super() so that the WebSocket is properly associated/unassociated with the application to prevent leaks.
The final piece of the API developers should understand is the WebSocketEngine. The WebSocketEngine is a JVM singleton in which all WebSocketApplications are registered. The methods of interest here are register/unregister:
/** * Registers the specified {@link WebSocketApplication} with the * <code>WebSocketEngine</code>. * * @param app the {@link WebSocketApplication} to register. */ public void register(WebSocketApplication app) { applications.add(app); } /** * Un-registers the specified {@link WebSocketApplication} with the * <code>WebSocketEngine</code>. * * @param app the {@link WebSocketApplication} to un-register. */ public void unregister(WebSocketApplication app) { applications.remove(app); } /** * Un-registers all {@link WebSocketApplication} instances with the * {@link WebSocketEngine}. */ public void unregisterAll() { applications.clear(); }
Registration may occur at any time. For example, if this application is created/registered by a Servlet, then registration would happen in the Servlet.init() method and deregistration may happen within the Servlet.destroy() method. Regardless, the WebSocketApplication must be registered with the WebSocketEngine in order to be queried as part of the connection upgrade process.