org.jboss.errai.bus.client.framework.transports.SSEHandler.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.errai.bus.client.framework.transports.SSEHandler.java

Source

/*
 * Copyright 2012 JBoss, by Red Hat, Inc
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jboss.errai.bus.client.framework.transports;

import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.Timer;
import org.jboss.errai.bus.client.api.base.MessageBuilder;
import org.jboss.errai.bus.client.api.messaging.Message;
import org.jboss.errai.bus.client.api.messaging.MessageCallback;
import org.jboss.errai.bus.client.framework.BusState;
import org.jboss.errai.bus.client.framework.ClientMessageBusImpl;
import org.jboss.errai.bus.client.util.BusToolsCli;
import org.jboss.errai.common.client.util.LogUtil;

import java.util.Collection;
import java.util.Collections;
import java.util.List;

/**
 * @author Mike Brock
 */
public class SSEHandler implements TransportHandler, TransportStatistics {
    private static final String SSE_AGENT_SERVICE = "SSEAgent";

    private final ClientMessageBusImpl clientMessageBus;
    private final MessageCallback messageCallback;

    private final HttpPollingHandler pollingHandler;
    private String sseEntryPoint;

    private int rxCount;
    private long connectedTime = -1;

    private boolean stopped;
    private boolean connected;
    private int retries;

    private boolean configured;
    private boolean hosed;

    private String unsupportedReason = UNSUPPORTED_MESSAGE_NO_SERVER_SUPPORT;

    private final Timer initialTimeoutTimer = new Timer() {
        @Override
        public void run() {
            if (!connected) {
                notifyDisconnected();
            }
        }
    };

    private Object sseChannel;

    public SSEHandler(final MessageCallback messageCallback, final ClientMessageBusImpl clientMessageBus) {
        this.clientMessageBus = clientMessageBus;
        this.messageCallback = messageCallback;
        this.pollingHandler = HttpPollingHandler.newNoPollingInstance(messageCallback, clientMessageBus);

        clientMessageBus.subscribe(SSE_AGENT_SERVICE, new MessageCallback() {
            @Override
            public void callback(final Message message) {
                notifyConnected();
            }
        });
    }

    @Override
    public void configure(final Message capabilitiesMessage) {
        configured = true;

        if (!isSSESupported()) {
            hosed = true;
            unsupportedReason = UNSUPPORTED_MESSAGE_NO_SERVER_SUPPORT;
            LogUtil.log("this browser does not support SSE");
            return;
        }

        this.sseEntryPoint = URL
                .encode(clientMessageBus.getApplicationLocation(clientMessageBus.getInServiceEntryPoint()))
                + "?z=0000&sse=1&clientId=" + URL.encodePathSegment(clientMessageBus.getClientId());

    }

    @Override
    public void start() {
        stopped = false;
        if (connected) {
            LogUtil.log("did not start SSE handler: already started.");
            return;
        }
        sseChannel = attemptSSEChannel(clientMessageBus, sseEntryPoint);

        // time out after 2 seconds and attempt reconnect. (note: this is really to deal with a bug a firefox).
        initialTimeoutTimer.cancel();
        initialTimeoutTimer.schedule(2500);
    }

    @Override
    public Collection<Message> stop(final boolean stopAllCurrentRequests) {
        stopped = true;
        disconnect(sseChannel);
        sseChannel = null;
        return pollingHandler.stop(stopAllCurrentRequests);
    }

    @Override
    public void transmit(final List<Message> txMessages) {
        this.pollingHandler.transmit(txMessages);
    }

    @Override
    public void handleProtocolExtension(final Message message) {
    }

    @Override
    public boolean isUsable() {
        return !hosed && configured;
    }

    private void handleReceived(final String json) {
        rxCount++;
        BusToolsCli.decodeToCallback(json, messageCallback);
    }

    private static native void disconnect(Object channel) /*-{
                                                          channel.close();
                                                          }-*/;

    private native boolean isSSESupported() /*-{
                                            return !!window.EventSource;
                                            }-*/;

    private native Object attemptSSEChannel(final ClientMessageBusImpl bus, final String sseAddress) /*-{
                                                                                                     var thisRef = this;
                                                                                                         
                                                                                                     var errorHandler = function (e) {
                                                                                                     if (e.srcElement.readyState === EventSource.CLOSED) {
                                                                                                     thisRef.@org.jboss.errai.bus.client.framework.transports.SSEHandler::notifyDisconnected()();
                                                                                                     }
                                                                                                     };
                                                                                                         
                                                                                                     var openHandler = function () {
                                                                                                     thisRef.@org.jboss.errai.bus.client.framework.transports.SSEHandler::verifyConnected()();
                                                                                                     };
                                                                                                         
                                                                                                     var sseSource = new EventSource(sseAddress);
                                                                                                     sseSource.addEventListener('message', function (e) {
                                                                                                     thisRef.@org.jboss.errai.bus.client.framework.transports.SSEHandler::handleReceived(Ljava/lang/String;)(e.data);
                                                                                                     }, false);
                                                                                                         
                                                                                                     sseSource.onerror = errorHandler;
                                                                                                     sseSource.onopen = openHandler;
                                                                                                         
                                                                                                     return sseSource;
                                                                                                     }-*/;

    private void verifyConnected() {
        transmit(Collections.singletonList(MessageBuilder.createMessage().toSubject("ServerEchoService")
                .signalling().done().repliesToSubject(SSE_AGENT_SERVICE).getMessage()));
    }

    private void notifyConnected() {
        initialTimeoutTimer.cancel();

        if (connected) {
            return;
        }
        connected = true;

        connectedTime = System.currentTimeMillis();
        LogUtil.log("SSE channel opened.");
        retries = 0;

        if (clientMessageBus.getState() == BusState.CONNECTION_INTERRUPTED)
            clientMessageBus.setState(BusState.CONNECTED);
    }

    private void notifyDisconnected() {
        connected = false;

        initialTimeoutTimer.cancel();
        LogUtil.log("SSE channel disconnected.");
        connectedTime = -1;
        clientMessageBus.setState(BusState.CONNECTION_INTERRUPTED);

        disconnect(sseChannel);

        if (!stopped) {
            retries++;
            new Timer() {
                @Override
                public void run() {
                    LogUtil.log("attempting reconnection ... ");

                    transmit(Collections.singletonList(MessageBuilder.createMessage().toSubject("SSEAgent")
                            .signalling().done().repliesToSubject("ClientBus").getMessage()));

                    pollingHandler.performPoll();
                    start();
                }

            }.schedule(retries * 1000);
        }
    }

    @Override
    public String toString() {
        return "SSE";
    }

    @Override
    public TransportStatistics getStatistics() {
        return this;
    }

    @Override
    public String getTransportDescription() {
        return "HTTP + Server-Sent Events";
    }

    @Override
    public String getUnsupportedDescription() {
        return unsupportedReason;
    }

    @Override
    public int getMessagesSent() {
        return pollingHandler.getMessagesSent();
    }

    @Override
    public int getMessagesReceived() {
        return rxCount;
    }

    @Override
    public long getConnectedTime() {
        return connectedTime;
    }

    @Override
    public int getMeasuredLatency() {
        return pollingHandler.getMeasuredLatency();
    }

    @Override
    public long getLastTransmissionTime() {
        return pollingHandler.getLastTransmissionTime();
    }

    @Override
    public boolean isFullDuplex() {
        return false;
    }

    @Override
    public String getRxEndpoint() {
        return clientMessageBus.getInServiceEntryPoint();
    }

    @Override
    public String getTxEndpoint() {
        return clientMessageBus.getOutServiceEntryPoint();
    }

    @Override
    public int getPendingMessages() {
        return pollingHandler.getStatistics().getPendingMessages();
    }
}