package org.realityforge.gwt.websockets.client; import com.google.gwt.core.shared.GWT; import com.google.gwt.typedarrays.shared.ArrayBuffer; import com.google.gwt.typedarrays.shared.ArrayBufferView; import javax.annotation.Nonnull; import javax.annotation.Nullable; /** * The WebSocket class. */ public abstract class WebSocket { /** * The factory used for creating WebSocket instances. */ public interface Factory { WebSocket newWebSocket(); } /** * The states of the WebSocket. */ public enum ReadyState { CONNECTING, OPEN, CLOSING, CLOSED } /** * The types of data the WebSocket can receive. */ public enum BinaryType { BLOB, ARRAYBUFFER } private static SupportDetector g_supportDetector; private static Factory g_factory; @Nonnull private WebSocketListener _listener = NullWebSocketListener.LISTENER; /** * Create a WebSocket if supported by the platform. * * This method will use the registered factory to create the WebSocket instance. * * @return a WebSocket instance, if supported by the platform, null otherwise. */ public static WebSocket newWebSocketIfSupported() { if ( null == g_factory && GWT.isClient() && getSupportDetector().isSupported() ) { register( getSupportDetector().newFactory() ); return g_factory.newWebSocket(); } return ( null != g_factory ) ? g_factory.newWebSocket() : null; } /** * @return true if newWebSocketIfSupported() will return a non-null value, false otherwise. */ public static boolean isSupported() { return ( null != g_factory ) || GWT.isClient() && getSupportDetector().isSupported(); } /** * Register a factory to be used to construct WebSocket instances. * This is not usually used as the built in browser based factory will * be detected and used if available. The register method is typically used * by test frameworks. * * @param factory the factory to register. */ public static void register( @Nonnull final Factory factory ) { g_factory = factory; } /** * Deregister factory if the specified factory is the registered factory. * * @param factory the factory to deregister. * @return true if able to deregister, false otherwise. */ public static boolean deregister( @Nonnull final Factory factory ) { if ( g_factory != factory ) { return false; } else { g_factory = null; return true; } } /** * Connect the WebSocket to the specified url, passing specified protocols. * * @param url the url to open. * @throws IllegalStateException if the WebSocket is already open. */ public abstract void connect( @Nonnull String url, @Nonnull String... protocols ) throws IllegalStateException; /** * Close the WebSocket and stop receiving MessageEvents. * * @throws IllegalStateException if the WebSocket is not open. */ public abstract void close() throws IllegalStateException; /** * Close the WebSocket with specified code and stop receiving MessageEvents. * * @throws IllegalStateException if the WebSocket is not open. */ public final void close( short code ) throws IllegalStateException { close( code, null ); } /** * Close the WebSocket with specified code and reason, and stop receiving MessageEvents. * * @throws IllegalStateException if the WebSocket is not open. */ public abstract void close( short code, @Nullable String reason ) throws IllegalStateException; /** * @return true if the WebSocket is connected, false otherwise. */ public abstract boolean isConnected(); /** * Send some string data across the WebSocket. * * @param data the data to send. * @throws IllegalStateException if the WebSocket is not open. */ public abstract void send( @Nonnull String data ) throws IllegalStateException; /** * Send some ArrayBuffer data across the WebSocket. * * @param data the data to send. * @throws IllegalStateException if the WebSocket is not open. */ public abstract void send( @Nonnull ArrayBuffer data ) throws IllegalStateException; /** * Send some ArrayBufferView data across the WebSocket. * * @param data the data to send. * @throws IllegalStateException if the WebSocket is not open. */ public abstract void send( @Nonnull ArrayBufferView data ) throws IllegalStateException; /** * Return the amount buffered on underlying WebSocket. * * @return the amount buffered on underlying WebSocket. * @throws IllegalStateException if the WebSocket is not open. */ public abstract int getBufferedAmount() throws IllegalStateException; /** * Return the protocol used by the underlying WebSocket. * * @return the protocol used by the underlying WebSocket. * @throws IllegalStateException if the WebSocket is not open. */ public abstract String getProtocol() throws IllegalStateException; /** * Return the url that the underlying WebSOcket is connected to. * * @return the url that the underlying WebSOcket is connected to. * @throws IllegalStateException if the WebSocket is not open. */ public abstract String getURL() throws IllegalStateException; /** * Return the extensions that the underlying WebSocket is connected using. * * @return the extensions that the underlying WebSocket is connected using. * @throws IllegalStateException if the WebSocket is not open. */ public abstract String getExtensions() throws IllegalStateException; /** * Return the state of the WebSocket. * * @return the state of the WebSocket. */ public abstract ReadyState getReadyState(); /** * Set the type of the binary messages that the WebSocket will receive. * Note: At this stage only ARRAYBUFFER is supported. * * @param binaryType the type of the binary messages that the WebSocket will receive. * @throws IllegalStateException if the WebSocket is not open. */ public abstract void setBinaryType( @Nonnull BinaryType binaryType ) throws IllegalStateException; /** * Return the type of the binary messages that the WebSocket will receive. * * @return the type of the binary messages that the WebSocket will receive. * @throws IllegalStateException if the WebSocket is not open. */ public abstract BinaryType getBinaryType() throws IllegalStateException; /** * Return the listener associated with the WebSocket. * * @return the listener associated with the WebSocket. */ @Nonnull public final WebSocketListener getListener() { return _listener; } /** * Set the listener to receive messages from the WebSocket. * * @param listener the listener to receive messages from the WebSocket. */ public final void setListener( @Nullable final WebSocketListener listener ) { _listener = null == listener ? NullWebSocketListener.LISTENER : listener; } /** * Fire a Connected event. */ protected final void onOpen() { getListener().onOpen( this ); } /** * Fire a Close event. */ protected final void onClose( final boolean wasClean, final int code, @Nullable final String reason ) { getListener().onClose( this, wasClean, code, reason ); } /** * Fire a Message event. */ protected final void onMessage( final String data ) { getListener().onMessage( this, data ); } /** * Fire a Message event. */ protected final void onMessage( final ArrayBuffer data ) { getListener().onMessage( this, data ); } /** * Fire an Error event. */ protected final void onError() { getListener().onError( this ); } /** * Detector for browser support. */ private static class SupportDetector { public boolean isSupported() { return Html5WebSocket.isSupported(); } public Factory newFactory() { return new Html5WebSocket.Factory(); } } /** * Detector for browsers without WebSocket support. */ @SuppressWarnings( "unused" ) private static class NoSupportDetector extends SupportDetector { @Override public boolean isSupported() { return false; } @Override public Factory newFactory() { return null; } } private static SupportDetector getSupportDetector() { if ( null == g_supportDetector ) { g_supportDetector = com.google.gwt.core.shared.GWT.create( SupportDetector.class ); } return g_supportDetector; } }