All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.github.czyzby.websocket.impl.GwtWebSocket Maven / Gradle / Ivy

package com.github.czyzby.websocket.impl;

import com.github.czyzby.websocket.data.WebSocketCloseCode;
import com.github.czyzby.websocket.data.WebSocketException;
import com.github.czyzby.websocket.data.WebSocketState;
import com.google.gwt.typedarrays.client.ArrayBufferNative;
import com.google.gwt.typedarrays.client.Int8ArrayNative;
import com.google.gwt.typedarrays.shared.ArrayBuffer;
import com.google.gwt.typedarrays.shared.Int8Array;

/** Default web socket implementation for GWT applications. Implementation loosely based on
 * com.sksamuel.gwt.websockets.Websocket class - improved with binary data support and current state reporting.
 *
 * @author MJ
 * @see gwt-websockets */
public class GwtWebSocket extends AbstractWebSocket {
    public GwtWebSocket(final String url) {
        super(url);
    }

    /** @return true if web sockets are supported by the browser. */
    public static native boolean areWebSocketsSupported() /*-{
                                                          return ("WebSocket" in window);
                                                          }-*/;

    @Override
    public void connect() throws WebSocketException {
        if (isOpen() || isConnecting()) {
            close(WebSocketCloseCode.AWAY);
        }
        try {
            open(super.getUrl());
        } catch (final Throwable exception) {
            throw new WebSocketException("Unable to open the web socket.", exception);
        }
    }

    /** @param url used to create the web socket. */
    protected void open(final String url) {
        if (url == null) {
            throw new WebSocketException("URL cannot be null.");
        }
        try {
            createWebSocket(url);
        } catch (final Throwable exception) {
            throw new WebSocketException("Unable to connect.", exception);
        }
    }

    /** @param url used to create the native web socket. */
    protected native void createWebSocket(String url)/*-{
                                                     var self = this;
                                                     if(self.ws) {
                                                     self.ws.close(1001);
                                                     }
                                                     self.ws = new WebSocket(url);
                                                     self.ws.onopen = function() { [email protected]::onOpen()(); };
                                                     self.ws.binaryType = 'arraybuffer';
                                                     self.ws.onclose = function(event) { [email protected]::onClose(ILjava/lang/String;)(event.code, event.reason); };
                                                     self.ws.onerror = function(error) { [email protected]::onError(Ljava/lang/String;Ljava/lang/String;)(error.type,error.toString()); };
                                                     self.ws.onmessage = function(msg) {
                                                     if (typeof(msg.data) == 'string' ) {
                                                     [email protected]::onMessage(Ljava/lang/String;)(msg.data);
                                                     }else{
                                                     [email protected]::onMessage(Lcom/google/gwt/typedarrays/shared/ArrayBuffer;)(msg.data);
                                                     }
                                                     }
                                                     }-*/;

    /** Invoked by native listener. */
    protected void onOpen() {
        postOpenEvent();
    }

    /** Invoked by native listener.
     *
     * @param closeCode see {@link WebSocketCloseCode}.
     * @param reason optional closing reason. */
    protected void onClose(final int closeCode, final String reason) {
        postCloseEvent(WebSocketCloseCode.getByCodeOrElse(closeCode, WebSocketCloseCode.ABNORMAL), reason);
    }

    /** Invoked by native listener.
     *
     * @param type reported type of error.
     * @param message full message of the error. */
    protected void onError(final String type, final String message) {
        postErrorEvent(
                new WebSocketException("An error occurred. Error type: " + type + ", error event message: " + message));
    }

    /** Invoked by native listener.
     *
     * @param arrayBuffer received binary frame. */
    protected void onMessage(final ArrayBuffer arrayBuffer) {
        if (arrayBuffer != null && arrayBuffer.byteLength() > 0) {
            final byte[] message = toByteArray(arrayBuffer);
            if (message.length > 0) {
                postMessageEvent(message);
            }
        }
    }

    /** @param arrayBuffer will be converted to byte array.
     * @return byte array with values stored in the buffer. */
    protected static byte[] toByteArray(final ArrayBuffer arrayBuffer) {
        final Int8Array array = Int8ArrayNative.create(arrayBuffer);
        final int length = array.byteLength();
        final byte[] byteArray = new byte[length];
        for (int index = 0; index < length; index++) {
            byteArray[index] = array.get(index);
        }
        return byteArray;
    }

    /** Invoked by native listener.
     *
     * @param message received text message. */
    protected void onMessage(final String message) {
        if (message != null && message.length() > 0) {
            postMessageEvent(message);
        }
    }

    @Override
    public WebSocketState getState() {
        try {
            return WebSocketState.getById(getStateId());
        } catch (final Throwable exception) {
            // Might be thrown if invalid state, for some reason.
            postErrorEvent(exception);
            return WebSocketState.CLOSED;
        }
    }

    /** @return current ready state of the socket.
     * @see WebSocketState */
    protected native int getStateId()/*-{
                                     if(this.ws) {
                                     return this.ws.readyState;
                                     }
                                     return 3;
                                     }-*/;

    @Override
    public void close(final WebSocketCloseCode code, final String reason) throws WebSocketException {
        try {
            close(code.getCode(), reason);
        } catch (final Throwable exception) {
            throw new WebSocketException("Unable to close the web socket.", exception);
        }
    }

    /** Closes the native web socket.
     *
     * @param code see {@link WebSocketCloseCode}.
     * @param reason optional closing reason. */
    protected native void close(int code, String reason)/*-{
                                                        if(this.ws){
                                                        this.ws.close(code,reason);
                                                        }
                                                        }-*/;

    @Override
    public void sendBinary(final byte[] message) {
        final ArrayBuffer arrayBuffer = ArrayBufferNative.create(message.length);
        final Int8Array array = Int8ArrayNative.create(arrayBuffer);
        array.set(message);
        try {
            sendArrayBuffer(arrayBuffer);
        } catch (final Throwable exception) {
            throw new WebSocketException(exception);
        }
    }

    /** @param message will be sent using native web socket. */
    protected native void sendArrayBuffer(ArrayBuffer message)/*-{
                                                              if(this.ws) {
                                                              this.ws.send(message);
                                                              }
                                                              }-*/;

    @Override
    public void sendString(final String message) {
        try {
            sendText(message);
        } catch (final Throwable exception) {
            throw new WebSocketException(exception);
        }
    }

    /** @param message will be sent using native web socket. */
    protected native void sendText(String message)/*-{
                                                  if(this.ws) {
                                                  this.ws.send(message);
                                                  }
                                                  }-*/;

    @Override
    public boolean isSupported() {
        return areWebSocketsSupported();
    }

    @Override
    public String getUrl() {
        final String url = getWebSocketUrl();
        return url == null ? super.getUrl() : url;
    }

    /** @return current URL stored by native web socket or null. */
    public native String getWebSocketUrl()/*-{
                                          if(this.ws) {
                                          return this.ws.url;
                                          }
                                          return null;
                                          }-*/;
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy