Package org.jboss.errai.bus.client.framework

Source Code of org.jboss.errai.bus.client.framework.ClientMessageBusImpl

/*
* Copyright 2011 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;

import static org.jboss.errai.bus.client.json.JSONUtilCli.decodePayload;
import static org.jboss.errai.bus.client.protocols.BusCommands.RemoteSubscribe;
import static org.jboss.errai.common.client.protocols.MessageParts.PriorityProcessing;
import static org.jboss.errai.common.client.protocols.MessageParts.ReplyTo;
import static org.jboss.errai.common.client.protocols.MessageParts.Subject;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Set;

import junit.framework.AssertionFailedError;

import org.jboss.errai.bus.client.ErraiBus;
import org.jboss.errai.bus.client.api.Message;
import org.jboss.errai.bus.client.api.MessageCallback;
import org.jboss.errai.bus.client.api.MessageListener;
import org.jboss.errai.bus.client.api.PreInitializationListener;
import org.jboss.errai.bus.client.api.SessionExpirationListener;
import org.jboss.errai.bus.client.api.SubscribeListener;
import org.jboss.errai.bus.client.api.UnsubscribeListener;
import org.jboss.errai.bus.client.api.base.Capabilities;
import org.jboss.errai.bus.client.api.base.DefaultErrorCallback;
import org.jboss.errai.bus.client.api.base.MessageBuilder;
import org.jboss.errai.bus.client.api.base.NoSubscribersToDeliverTo;
import org.jboss.errai.bus.client.api.base.TransportIOException;
import org.jboss.errai.bus.client.json.JSONUtilCli;
import org.jboss.errai.bus.client.protocols.BusCommands;
import org.jboss.errai.bus.client.util.BusTools;
import org.jboss.errai.common.client.api.ResourceProvider;
import org.jboss.errai.common.client.api.extension.InitVotes;
import org.jboss.errai.common.client.framework.Assert;
import org.jboss.errai.common.client.protocols.MessageParts;
import org.jboss.errai.common.client.util.LogUtil;
import org.jboss.errai.marshalling.client.api.MarshallerFramework;

import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.RunAsyncCallback;
import com.google.gwt.dom.client.Style;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.RequestBuilder;
import com.google.gwt.http.client.RequestCallback;
import com.google.gwt.http.client.RequestException;
import com.google.gwt.http.client.RequestTimeoutException;
import com.google.gwt.http.client.Response;
import com.google.gwt.http.client.URL;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.AbsolutePanel;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HasVerticalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.VerticalPanel;

/**
* The default client <tt>MessageBus</tt> implementation.  This bus runs in the browser and automatically federates
* with the server immediately upon initialization.
*
* @author Mike Brock
*/
public class ClientMessageBusImpl implements ClientMessageBus {
  private String clientId;
  private String sessionId;

  /* The encoded URL to be used for the bus */
  String OUT_SERVICE_ENTRY_POINT = "out.erraiBus";
  String IN_SERVICE_ENTRY_POINT = "in.erraiBus";

  /* ArrayList of all subscription listeners */
  private List<SubscribeListener> onSubscribeHooks;

  /* ArrayList of all unsubscription listeners */
  private List<UnsubscribeListener> onUnsubscribeHooks;

  /* Used to build the HTTP POST request */
  private RequestBuilder sendBuilder;

  private volatile boolean cometChannelOpen = true;
  private volatile boolean webSocketOpen = false;
  private String webSocketUrl;
  private String webSocketToken;
  private Object webSocketChannel;

  public final MessageCallback remoteCallback = new RemoteMessageCallback();

  private RequestCallback receiveCommCallback = new NoPollRequestCallback();

  private Map<String, List<MessageCallback>> subscriptions =
          new HashMap<String, List<MessageCallback>>();

  private Map<String, List<MessageCallback>> localSubscriptions =
          new HashMap<String, List<MessageCallback>>();

  private Map<String, MessageCallback> remotes;

  private List<SessionExpirationListener> sessionExpirationListeners
          = new ArrayList<SessionExpirationListener>();

  private List<PreInitializationListener> preInitializationListeners
          = new ArrayList<PreInitializationListener>();

  /* Map of subjects to references registered in this session */
  private Map<String, List<Object>> registeredInThisSession = new HashMap<String, List<Object>>();

  /* A list of {@link Runnable} initialization tasks to be executed after the bus has successfully finished it's
* initialization and is now communicating with the remote bus. */
  private List<Runnable> postInitTasks = new ArrayList<Runnable>();
  private List<Message> deferredMessages = new ArrayList<Message>();
  private Queue<Message> toSendBuffer = new LinkedList<Message>();

  /* True if the client's message bus has been initialized */
  private boolean initialized = false;
  private boolean reinit = false;
  private boolean postInit = false;

  /**
   * The unique ID that will sent with the next request.
   * <p/>
   * IMPORTANT: only access this member via {@link #getNextRequestNumber()}.
   */
  private int txNumber = 0;
  private int rxNumber = 0;
  private long lastTx = System.currentTimeMillis();


  private boolean disconnected = false;


  private BusErrorDialog errorDialog;

  static {
    MarshallerFramework.initializeDefaultSessionProvider();
  }

  private List<MessageInterceptor> interceptorStack = new LinkedList<MessageInterceptor>();

  private LogAdapter logAdapter = new LogAdapter() {
    @Override
    public void warn(String message) {
      GWT.log("WARN: " + message, null);
    }

    @Override
    public void info(String message) {
      GWT.log("INFO: " + message, null);
    }

    @Override
    public void debug(String message) {
      GWT.log("DEBUG: " + message, null);
    }

    @Override
    public void error(String message, Throwable t) {
      showError(message, t);
    }
  };

  public ClientMessageBusImpl() {
    init();
  }

  private void createRequestBuilders() {
    sendBuilder = getSendBuilder();

    logAdapter.debug("Connecting Errai at URL " + sendBuilder.getUrl());
  }

  private RequestBuilder getSendBuilder() {
    String endpoint = OUT_SERVICE_ENTRY_POINT;

    RequestBuilder builder = new RequestBuilder(
            RequestBuilder.POST,
            URL.encode(endpoint) + "?z=" + getNextRequestNumber()
    );

    builder.setHeader("Content-Type", "application/json");
    builder.setHeader(ClientMessageBus.REMOTE_QUEUE_ID_HEADER, clientId);

    return builder;
  }

  private RequestBuilder getRecvBuilder() {
    String endpoint = IN_SERVICE_ENTRY_POINT;

    RequestBuilder builder = new RequestBuilder(
            RequestBuilder.GET,
            URL.encode(endpoint) + "?z=" + getNextRequestNumber()
    );

    builder.setHeader("Content-Type", "application/json");
    builder.setHeader(ClientMessageBus.REMOTE_QUEUE_ID_HEADER, clientId);
    return builder;
  }

  /**
   * Removes all subscriptions attached to the specified subject
   *
   * @param subject - the subject to have all it's subscriptions removed
   */
  @Override
  public void unsubscribeAll(String subject) {
    fireAllUnSubscribeListeners(subject);
    removeSubscriptionTopic(subject);
  }

  /**
   * Add a subscription for the specified subject
   *
   * @param subject  - the subject to add a subscription for
   * @param callback - function called when the message is dispatched
   */
  @Override
  public Subscription subscribe(final String subject, final MessageCallback callback) {
    return _subscribe(subject, callback, false);
  }

  @Override
  public Subscription subscribeLocal(final String subject, final MessageCallback callback) {
    return _subscribe(subject, callback, true);
  }

  private Subscription _subscribe(final String subject, final MessageCallback callback, final boolean local) {
    if (BuiltInServices.ServerBus.name().equals(subject) && subscriptions.containsKey(BuiltInServices.ServerBus.name()))
      return null;

    if (!postInit) {
      final DeferredSubscription deferredSubscription = new DeferredSubscription();

      addPostInitTask(new Runnable() {
        @Override
        public void run() {
          deferredSubscription.attachSubscription(_subscribe(subject, callback, local));
        }
      });

      return deferredSubscription;
    }

    fireAllSubscribeListeners(subject, local, directSubscribe(subject, callback, local));

    return new Subscription() {
      @Override
      public void remove() {
        List<MessageCallback> cbs = local ? localSubscriptions.get(subject) : subscriptions.get(subject);
        if (cbs != null) {
          cbs.remove(callback);
        }
      }
    };
  }


  private boolean directSubscribe(final String subject, final MessageCallback callback, boolean local) {
    boolean isNew = !isSubscribed(subject);

    MessageCallback cb = new MessageCallback() {
      @Override
      public void callback(Message message) {
        try {
          executeInterceptorStack(true, message);
          callback.callback(message);
        }
        catch (Exception e) {
          logError("receiver '" + subject + "' threw an exception", decodeCommandMessage(message), e);
        }
      }
    };

    if (local) {
      addLocalSubscriptionEntry(subject, cb);
    }
    else {
      addSubscriptionEntry(subject, cb);
    }

    return isNew;
  }

  /**
   * Fire listeners to notify that a new subscription has been registered on the bus.
   *
   * @param subject - new subscription registered
   * @param local   -
   * @param isNew   -
   */
  private void fireAllSubscribeListeners(String subject, boolean local, boolean isNew) {
    Iterator<SubscribeListener> iter = onSubscribeHooks.iterator();
    SubscriptionEvent evt = new SubscriptionEvent(false, false, local, isNew, 1, "InBrowser", subject);

    while (iter.hasNext()) {
      iter.next().onSubscribe(evt);

      if (evt.isDisposeListener()) {
        iter.remove();
        evt.setDisposeListener(false);
      }
    }
  }

  /**
   * Fire listeners to notify that a subscription has been unregistered from the bus
   *
   * @param subject - subscription unregistered
   */
  private void fireAllUnSubscribeListeners(String subject) {
    Iterator<UnsubscribeListener> iter = onUnsubscribeHooks.iterator();
    SubscriptionEvent evt = new SubscriptionEvent(false, "InBrowser", 0, false, subject);

    while (iter.hasNext()) {
      iter.next().onUnsubscribe(evt);
      if (evt.isDisposeListener()) {
        iter.remove();
        evt.setDisposeListener(false);
      }
    }
  }

  private static int conversationCounter = 0;

  /**
   * Have a single two-way conversation
   *
   * @param message  - The message to be sent in the conversation
   * @param callback - The function to be called when the message is received
   */
  @Override
  public void conversationWith(final Message message, final MessageCallback callback) {
    final String tempSubject = "temp:Reply:" + (++conversationCounter);

    message.set(ReplyTo, tempSubject);

    subscribe(tempSubject, new MessageCallback() {
      @Override
      public void callback(Message message) {
        unsubscribeAll(tempSubject);
        callback.callback(message);
      }
    });

    send(message);
  }

  /**
   * Globally send message to all receivers.
   *
   * @param message - The message to be sent.
   */
  @Override
  public void sendGlobal(Message message) {
    send(message);
  }

  /**
   * Sends the specified message, and notifies the listeners.
   *
   * @param message       - the message to be sent
   * @param fireListeners - true if the appropriate listeners should be fired
   */
  @Override
  public void send(Message message, boolean fireListeners) {
    // TODO: fire listeners?
    send(message);
  }

  private static final ResourceProvider<RequestDispatcher> dispatcherProvider
          = new ResourceProvider<RequestDispatcher>() {
    @Override
    public RequestDispatcher get() {
      return ErraiBus.getDispatcher();
    }
  };


  /**
   * Sends the message using it's encoded subject. If the bus has not been initialized, it will be added to
   * <tt>postInitTasks</tt>.
   *
   * @param message -
   * @throws RuntimeException - if message does not contain a ToSubject field or if the message's callback throws
   *                          an error.
   */
  @Override
  public void send(final Message message) {
    message.setResource(RequestDispatcher.class.getName(), dispatcherProvider);
    message.setResource("Session", JSONUtilCli.getClientSession());

    executeInterceptorStack(false, message);

    message.commit();
    try {
      if (message.hasPart(MessageParts.ToSubject)) {
        if (!initialized) {
          deferredMessages.add(message);
        }
        else {
          if (!hasListeners(message.getSubject())) {
            throw new NoSubscribersToDeliverTo(message.getSubject());
          }

          directStore(message);
        }
      }
      else {
        throw new RuntimeException("Cannot send message using this method" +
                " if the message does not contain a ToSubject field.");
      }
    }
    catch (RuntimeException e) {
      callErrorHandler(message, e);
      throw e;
    }
  }

  private boolean hasListeners(String subject) {
    return subscriptions.containsKey(subject)
            || remotes.containsKey(subject)
            || localSubscriptions.containsKey(subject);
  }

  private void callErrorHandler(final Message message, final Throwable t) {
    if (message.getErrorCallback() != null) {
      message.getErrorCallback().error(message, t);
    }
    logError(t.getMessage(), "none", t);
  }

  private void directStore(final Message message) {
    String subject = message.getSubject();

    if (remotes.containsKey(subject)) {
      remotes.get(subject).callback(message);
    }
    else if (subscriptions.containsKey(subject)) {
      deliverToSubscriptions(subscriptions, subject, message);
    }
    else if (localSubscriptions.containsKey(subject)) {
      deliverToSubscriptions(localSubscriptions, subject, message);
    }
    else {
      throw new NoSubscribersToDeliverTo(subject);
    }
  }

  boolean txActive = false;

  private boolean throttleOutgoing() {
    return (System.currentTimeMillis() - lastTx) < 150;
  }


  /**
   * Add message to the queue that remotely transmits messages to the server.
   * All messages in the queue are then sent.
   *
   * @param message -
   */
  private void encodeAndTransmit(final Message message) {
    if (initialized && !message.hasPart(MessageParts.PriorityProcessing)
            && (!toSendBuffer.isEmpty() || throttleOutgoing())) {
      toSendBuffer.offer(message);
      if (!txActive && toSendBuffer.size() == 1) {
        new Timer() {
          @Override
          public void run() {
            if (!initialized) return;
            transmitRemote(BusTools.encodeMessages(toSendBuffer), new ArrayList<Message>(toSendBuffer));
          }
        }.schedule(150);
      }
    }
    else {
      transmitRemote(BusTools.encodeMessage(message), Collections.singletonList(message));
    }
  }

  private void addSubscriptionEntry(String subject, MessageCallback reference) {
    _addCallbackEntry(subscriptions, subject, reference);
  }


  private void addLocalSubscriptionEntry(String subject, MessageCallback reference) {
    _addCallbackEntry(localSubscriptions, subject, reference);
  }

  private static void _addCallbackEntry(Map<String, List<MessageCallback>> subscriptions, String subject,
                                        MessageCallback reference) {
    if (!subscriptions.containsKey(subject)) {
      subscriptions.put(subject, new ArrayList<MessageCallback>());
    }

    if (!subscriptions.get(subject).contains(reference)) {
      subscriptions.get(subject).add(reference);
    }
  }


  private void removeSubscriptionTopic(String subject) {
    subscriptions.remove(subject);
  }

  // TODO delete this method
  private void resubscribeShadowSubcriptions() {
    for (Map.Entry<String, List<MessageCallback>> entry : subscriptions.entrySet()) {
      for (MessageCallback callback : entry.getValue()) {
        _subscribe(entry.getKey(), callback, false);
      }
    }
  }

  private static void deliverToSubscriptions(Map<String, List<MessageCallback>> subscriptions,
                                             String subject, Message message) {
    for (MessageCallback cb : subscriptions.get(subject)) {
      cb.callback(message);
    }
  }

  /**
   * Checks if subject is already listed in the subscriptions map
   *
   * @param subject - subject to look for
   * @return true if the subject is already subscribed
   */
  @Override
  public boolean isSubscribed(String subject) {
    return subscriptions.containsKey(subject);
  }

  /**
   * Retrieve all registrations that have occured during the current capture context.
   * <p/>
   * The Map returned has the subject of the registrations as the key, and Sets of registration objects as the
   * value of the Map.
   *
   * @return A map of registrations captured in the current capture context.
   */
  @Override
  public Map<String, List<Object>> getCapturedRegistrations() {
    return registeredInThisSession;
  }

  /**
   * Marks the beginning of a new capture context.<p/>  From this point, the message is called forward, all
   * registration events which occur will be captured.
   */
  @Override
  public void beginCapture() {
    registeredInThisSession = new HashMap<String, List<Object>>();
  }

  /**
   * End the current capturing context.
   */
  @Override
  public void endCapture() {
    registeredInThisSession = null;
  }

  /**
   * Transmits JSON string containing message, using the <tt>sendBuilder</tt>
   *
   * @param message    - JSON string representation of message
   * @param txMessages - Messages reference.
   */
  private void transmitRemote(final String message, final List<Message> txMessages) {
    if (message == null) return;
  //  System.out.println("TX: " + message);
    try {
      txActive = true;

      if (webSocketOpen) {
        if (ClientWebSocketChannel.transmitToSocket(webSocketChannel, message)) {
          return;
        }
        else {
          LogUtil.log("websocket channel is closed. falling back to comet");

          //disconnected.
          webSocketOpen = false;
          webSocketChannel = null;
          cometChannelOpen = true;

          if (receiveCommCallback instanceof LongPollRequestCallback) {
            ((LongPollRequestCallback) receiveCommCallback).schedule();
          }
        }
      }

      try {
        sendBuilder.sendRequest(message, new RequestCallback() {

          @Override
          public void onResponseReceived(Request request, Response response) {
            switch (response.getStatusCode()) {
              case 1:
              case 404:
              case 408:
              case 502:
              case 503:
              case 504: {
                // Sending the message failed.
                // Although the response may still be valid
                // Handle it gracefully
                //noinspection ThrowableInstanceNeverThrown

                TransportIOException tioe = new TransportIOException(response.getText(), response.getStatusCode(),
                        "Failure communicating with server");

                for (Message txM : txMessages) {
                  callErrorHandler(txM, tioe);
                }
                return;
              }
            }

            /**
             * If the server bus returned us some client-destined messages
             * in response to our send, handle them now.
             */
            try {
              procIncomingPayload(response);
            }
            catch (AssertionFailedError e) {
              throw e;
            }
            catch (Throwable e) {
              for (Message txM : txMessages) {
                callErrorHandler(txM, e);
              }
            }
            finally {
              lastTx = System.currentTimeMillis();
            }
          }

          @Override
          public void onError(Request request, Throwable exception) {
            for (Message txM : txMessages) {
              if (txM.getErrorCallback() == null || txM.getErrorCallback().error(txM, exception)) {
                logError("Failed to communicate with remote bus", "", exception);
              }
            }
          }
        });
      }
      catch (Exception e) {
        for (Message txM : txMessages) {
          callErrorHandler(txM, e);
        }
      }
    }
    finally {
      txActive = false;
    }
  }

  boolean rxActive = false;

  private void performPoll() {
    try {
      if (rxActive || !cometChannelOpen) return;
      rxActive = true;
      getRecvBuilder().sendRequest(null, receiveCommCallback);
    }
    catch (RequestTimeoutException e) {
      statusCode = 1;
      receiveCommCallback.onError(null, e);
    }
    catch (Throwable t) {
      DefaultErrorCallback.INSTANCE.error(null, t);
    }
    finally {
      rxActive = false;
    }
  }

  @Override
  public void stop(boolean sendDisconnect) {
    try {
      if (sendDisconnect && isRemoteCommunicationEnabled()) {
        sendBuilder.setHeader("phase", "disconnect");

        Message m = MessageBuilder.createMessage()
                .toSubject(BuiltInServices.ServerBus.name())
                .command(BusCommands.Disconnect).getMessage()
                .set(MessageParts.PriorityProcessing, "1");

        encodeAndTransmit(m);
      }

      unsubscribeAll(BuiltInServices.ClientBus.name());
      subscriptions.clear();
    }
    finally {
      this.lastTx = 0;
      this.toSendBuffer.clear();
      this.txActive = false;
      this.rxActive = false;

      this.remotes.clear();
      this.disconnected = true;
      this.initialized = false;
      this.postInit = false;
      this.sendBuilder = null;
      this.postInitTasks.clear();

      InitVotes.reset();
    }
  }

  public class RemoteMessageCallback implements MessageCallback {
    @Override
    public void callback(Message message) {
      encodeAndTransmit(message);
    }
  }

  private void initFields() {
    initialized = false;
    disconnected = false;

    clientId = String.valueOf(com.google.gwt.user.client.Random.nextInt(99999))
            + "-" + (System.currentTimeMillis() % com.google.gwt.user.client.Random.nextInt(99999));

    IN_SERVICE_ENTRY_POINT = "in." + clientId + ".erraiBus";
    OUT_SERVICE_ENTRY_POINT = "out." + clientId + ".erraiBus";

    onSubscribeHooks = new ArrayList<SubscribeListener>();
    onUnsubscribeHooks = new ArrayList<UnsubscribeListener>();
    remotes = new HashMap<String, MessageCallback>();
  }

  public void setInitialized(boolean initialized) {
    this.initialized = initialized;
  }

  public boolean isReinit() {
    return this.reinit;
  }

  private void setReinit(boolean reinit) {
    this.reinit = reinit;
  }

  private void registerInitVoteCallbacks() {
    InitVotes.waitFor(ClientMessageBusImpl.class);
    InitVotes.waitFor(RpcProxyLoader.class);

    InitVotes.registerOneTimeInitCallback(new Runnable() {
      @Override
      public void run() {
        completeInit();
      }
    });
  }


  /**
   * Initializes the message bus, by subscribing to the ClientBus (to receive subscription messages) and the
   * ClientErrorBus to dispatch errors when called.
   */
  @Override
  public void init() {
    declareDebugFunction();
    if (!reinit) {
      registerInitVoteCallbacks();
    }

    if (sendBuilder == null) {
      if (!GWT.isScript()) {   // Hosted Mode
        initFields();
        createRequestBuilders();

        if (isReinit()) {
          setReinit(true);
          init();
          setReinit(false);
          return;
        }
        else {
          init();
          return;
        }
      }
      else {
        initFields();
        createRequestBuilders();
      }
    }

    if (sendBuilder == null) {
      return;
    }

    if (reinit) {
      resubscribeShadowSubcriptions();
    }

    /**
     * Fire initialization listeners now.
     */
    for (PreInitializationListener listener : preInitializationListeners) {
      listener.beforeInitialization();
    }

    if (isRemoteCommunicationEnabled()) {
      remoteSubscribe(BuiltInServices.ServerEchoService.name());
    }

    directSubscribe(BuiltInServices.ClientBus.name(), new MessageCallback() {
      @Override
      @SuppressWarnings({"unchecked"})
      public void callback(final Message message) {
        switch (BusCommands.valueOf(message.getCommandType())) {
          case RemoteSubscribe:
            if (message.hasPart(MessageParts.SubjectsList)) {
              LogUtil.log("remote services available: " + message.get(List.class, MessageParts.SubjectsList));

              for (String subject : (List<String>) message.get(List.class, MessageParts.SubjectsList)) {
                remoteSubscribe(subject);
              }
            }
            else {
              String subject = message.get(String.class, Subject);
              remoteSubscribe(subject);
            }
            break;

          case RemoteUnsubscribe:
            unsubscribeAll(message.get(String.class, Subject));
            break;

          case CapabilitiesNotice:
            LogUtil.log("received capabilities notice from server. supported capabilities of remote: "
                    + message.get(String.class, MessageParts.CapabilitiesFlags));

            String[] capabilites = message.get(String.class, MessageParts.CapabilitiesFlags).split(",");

            for (String capability : capabilites) {
              switch (Capabilities.valueOf(capability)) {
                case WebSockets:
                  webSocketUrl = message.get(String.class, MessageParts.WebSocketURL);
                  webSocketToken = message.get(String.class, MessageParts.WebSocketToken);

                  LogUtil.log("attempting web sockets connection at URL: " + webSocketUrl);

                  Object o = ClientWebSocketChannel.attemptWebSocketConnect(ClientMessageBusImpl.this, webSocketUrl);

                  if (o instanceof String) {
                    LogUtil.log("could not use web sockets. reason: " + o);
                  }
                  break;
                case LongPollAvailable:

                  LogUtil.log("initializing long poll subsystem");
                  receiveCommCallback = new LongPollRequestCallback();
                  break;
                case NoLongPollAvailable:
                  receiveCommCallback = new ShortPollRequestCallback();
                  if (message.hasPart(MessageParts.PollFrequency)) {
                    POLL_FREQUENCY = message.get(Integer.class, MessageParts.PollFrequency);
                  }
                  else {
                    POLL_FREQUENCY = 500;
                  }
                  break;
              }
            }

            break;

          case RemoteMonitorAttach:
            break;

          case FinishStateSync:
            if (isInitialized()) {
              return;
            }

            new Timer() {
              @Override
              public void run() {
                LogUtil.log("received FinishStateSync message. preparing to bring up the federation");

                List<String> subjects = new ArrayList<String>();
                for (String s : subscriptions.keySet()) {
                  if (s.startsWith("local:")) continue;
                  if (!remotes.containsKey(s)) subjects.add(s);
                }

                sessionId = message.get(String.class, MessageParts.ConnectionSessionKey);

                remoteSubscribe(BuiltInServices.ServerBus.name());

                MessageBuilder.createMessage()
                        .toSubject(BuiltInServices.ServerBus.name())
                        .command(RemoteSubscribe)
                        .with(MessageParts.SubjectsList, subjects)
                        .with(PriorityProcessing, "1")
                        .noErrorHandling()
                        .sendNowWith(ClientMessageBusImpl.this);


                MessageBuilder.createMessage()
                        .toSubject(BuiltInServices.ServerBus.name())
                        .command(BusCommands.FinishStateSync)
                        .with(PriorityProcessing, "1")
                        .noErrorHandling().sendNowWith(ClientMessageBusImpl.this);

                /**
                 * ... also send RemoteUnsubscribe signals.
                 */
                addSubscribeListener(new SubscribeListener() {
                  @Override
                  public void onSubscribe(SubscriptionEvent event) {
                    if (event.isLocalOnly() || event.getSubject().startsWith("local:")
                            || remotes.containsKey(event.getSubject())) {
                      return;
                    }

                    if (event.isNew()) {
                      MessageBuilder.getMessageProvider().get().command(RemoteSubscribe)
                              .toSubject(BuiltInServices.ServerBus.name())
                              .set(Subject, event.getSubject())
                              .set(PriorityProcessing, "1")
                              .sendNowWith(ClientMessageBusImpl.this);
                    }
                  }
                });

                addUnsubscribeListener(new UnsubscribeListener() {
                  @Override
                  public void onUnsubscribe(SubscriptionEvent event) {
                    MessageBuilder.getMessageProvider().get().command(BusCommands.RemoteUnsubscribe)
                            .toSubject(BuiltInServices.ServerBus.name())
                            .set(Subject, event.getSubject())
                            .set(PriorityProcessing, "1")
                            .sendNowWith(ClientMessageBusImpl.this);
                  }
                });

                subscribe(DefaultErrorCallback.CLIENT_ERROR_SUBJECT, new MessageCallback() {
                  @Override
                  public void callback(Message message) {
                    String errorTo = message.get(String.class, MessageParts.ErrorTo);
                    if (errorTo == null) {
                      logError(message.get(String.class, MessageParts.ErrorMessage),
                              message.get(String.class, MessageParts.AdditionalDetails), null);
                    }
                    else {
                      message.toSubject(errorTo);
                      message.sendNowWith(ClientMessageBusImpl.this);
                    }
                  }
                });

                InitVotes.voteFor(ClientMessageBusImpl.class);


                // end of FinishStateSync Timer
              }
            }.schedule(5);


            break;

          case SessionExpired:
            if (isReinit()) {
              showError("Session was terminated and could not be re-established", null);
              return;
            }

            if (!isInitialized()) return;

            for (SessionExpirationListener listener : sessionExpirationListeners) {
              listener.onSessionExpire();
            }

            stop(false);

            setReinit(true);

            init();
            setReinit(false);

            break;

          case WebsocketChannelVerify:
            LogUtil.log("received verification token for websocket connection");
            MessageBuilder.createMessage()
                    .toSubject(BuiltInServices.ServerBus.name())
                    .command(BusCommands.WebsocketChannelVerify)
                    .copy(MessageParts.WebSocketToken, message)
                    .done().sendNowWith(ClientMessageBusImpl.this);
            break;

          case WebsocketChannelOpen:

            cometChannelOpen = false;
            webSocketOpen = true;

            // send final message to open the channel
            ClientWebSocketChannel.transmitToSocket(webSocketChannel, getWebSocketNegotiationString());

            LogUtil.log("web socket channel successfully negotiated. comet channel deactivated.");

            break;

          case WebsocketNegotiationFailed:
            webSocketChannel = null;
            logError("failed to connect to websocket: server rejected request",
                    message.get(String.class, MessageParts.ErrorMessage), null);
            break;

          case Disconnect:
            stop(false);

            if (message.hasPart("Reason")) {
              logError("The bus was disconnected by the server", "Reason: "
                      + message.get(String.class, "Reason"), null);
            }
            break;
        }
      }
    }, false);

    /**
     * Send initial message to connect to the queue, to establish an HTTP session. Otherwise, concurrent
     * requests will result in multiple sessions being created.  Which is bad.  Avoid this at all costs.
     * Please.
     */
    if (!sendInitialMessage()) {
      logError("Could not connect to remote bus", "", null);
    }
  }

  private void completeInit() {
    if (!postInit && !initialized) {
      postInit = true;

      LogUtil.log("received final vote for initialization ...");

      if (!postInitTasks.isEmpty())
        LogUtil.log("executing " + postInitTasks.size() + " post init task(s)");

      do {
        for (Runnable runnable : new ArrayList<Runnable>(postInitTasks)) {
          postInitTasks.remove(runnable);
          runnable.run();
        }
      }
      while (!postInitTasks.isEmpty());


      sendAllDeferred();
      postInitTasks.clear();

      setInitialized(true);

      LogUtil.log("bus federation complete. now operating normally.");
    }
  }

  private void remoteSubscribe(String subject) {
    remotes.put(subject, remoteCallback);
    //  addShadowSubscription(subject, remoteCallback);
  }

  Set<String> getRemoteSubscriptions() {
    if (remotes == null)
      return null;

    return remotes.keySet();
  }

  private void sendAllDeferred() {
    if (!deferredMessages.isEmpty())
      LogUtil.log("transmitting deferred messages now ...");

    for (Iterator<Message> iter = deferredMessages.iterator(); iter.hasNext(); ) {
      Message m = iter.next();
      if (m.hasPart(MessageParts.PriorityProcessing)) {
        directStore(m);
        iter.remove();
      }
    }

    for (Iterator<Message> iter = deferredMessages.iterator(); iter.hasNext(); ) {
      directStore(iter.next());
      iter.remove();
    }
  }

  /**
   * Sends the initial message to connect to the queue, to establish an HTTP session. Otherwise, concurrent
   * requests will result in multiple sessions being created.
   *
   * @return true if initial message was sent successfully.
   */
  private boolean sendInitialMessage() {
    if (!isRemoteCommunicationEnabled()) {
      LogUtil.log("Initializing client bus in offline mode (erraiBusRemoteCommunicationEnabled was set to false)!");
      InitVotes.voteFor(ClientMessageBusImpl.class);
      InitVotes.voteFor(RpcProxyLoader.class);
      return true;
    }

    try {
      LogUtil.log("sending initial handshake to remote bus");

      String initialMessage = "{\"CommandType\":\"ConnectToQueue\",\"ToSubject\":\"ServerBus\"," +
              " \"PriorityProcessing\":\"1\"}";

      RequestBuilder initialRequest = getSendBuilder();
      initialRequest.setHeader("phase", "connection");

      initialRequest.sendRequest(initialMessage, new RequestCallback() {
        @Override
        public void onResponseReceived(Request request, Response response) {
          try {
            LogUtil.log("received response from initial handshake.");
            procIncomingPayload(response);
            initializeMessagingBus();
          }
          catch (Exception e) {
            e.printStackTrace();
            logError("Error attaching to bus", e.getMessage() + "<br/>Message Contents:<br/>"
                    + response.getText(), e);
          }
        }

        @Override
        public void onError(Request request, Throwable exception) {
          logError("Could not connect to remote bus", "", exception);
        }
      });
    }
    catch (RequestException e) {
      e.printStackTrace();
      return false;
    }
    return true;
  }

  /**
   * Returns true if client message bus is initialized.
   *
   * @return true if client message bus is initialized.
   */
  @Override
  public boolean isInitialized() {
    return this.initialized;
  }

  int maxRetries = 5;
  int retries = 0;
  int timeout = 2000;
  int statusCode = 0;
  DialogBox timeoutDB;
  Label timeoutMessage;

  private void createConnectAttemptGUI() {
    timeoutDB = new DialogBox();
    timeoutMessage = new Label();
    timeoutDB.add(timeoutMessage);
    RootPanel.get().add(timeoutDB);
    timeoutDB.show();
    timeoutDB.center();
  }

  private void clearConnectAttemptGUI() {
    timeoutDB.hide();
    RootPanel.get().remove(timeoutDB);
    timeoutDB = null;
    timeoutMessage = null;
    retries = 0;
  }

  protected class LongPollRequestCallback implements RequestCallback {
    @Override
    public void onError(Request request, Throwable throwable) {
      switch (statusCode) {
        case 1:
        case 408:
        case 502:
        case 504:
          if (retries != maxRetries) {
            if (timeoutDB == null) {
              createConnectAttemptGUI();
            }

            logAdapter.warn("Attempting reconnection -- Retries: " + (maxRetries - retries));
            timeoutMessage.setText("Connection Interrupted -- Retries: " + (maxRetries - retries));
            retries++;
            new Timer() {
              @Override
              public void run() {
                performPoll();
              }
            }.schedule(timeout);

            statusCode = 0;
            return;
          }
          else {
            timeoutMessage.setText("Connection re-attempt failed!");
          }
      }

      DefaultErrorCallback.INSTANCE.error(null, throwable);
    }

    @Override
    public void onResponseReceived(Request request, Response response) {
      if (response.getStatusCode() != 200) {
        switch (statusCode = response.getStatusCode()) {
          case 200:
          case 300:
          case 301:
          case 302:
          case 303:
          case 304:
          case 305:
          case 307:
            break;
          default:
            onError(request, new TransportIOException("Unexpected response code", statusCode, response.getStatusText()));
            return;
        }
      }

      if (retries != 0) {
        clearConnectAttemptGUI();
      }

      try {
        procIncomingPayload(response);
        schedule();
      }
      catch (Throwable e) {
        logError("Errai MessageBus Disconnected Due to Fatal Error",
                response.getText(), e);
      }
    }

    public void schedule() {
      if (!cometChannelOpen) return;
      new Timer() {
        @Override
        public void run() {
          performPoll();
        }
      }.schedule(25);
    }
  }

  protected class NoPollRequestCallback extends LongPollRequestCallback {
    @Override
    public void schedule() {
      performPoll();
    }
  }

  protected class ShortPollRequestCallback extends LongPollRequestCallback {
    public ShortPollRequestCallback() {
    }

    @Override
    public void schedule() {
      new Timer() {
        @Override
        public void run() {
          performPoll();
        }
      }.schedule(POLL_FREQUENCY);
    }
  }

  public static int POLL_FREQUENCY = 250;

  /**
   * Initializes the message bus by setting up the <tt>recvBuilder</tt> to accept responses. Also, initializes the
   * incoming timer to ensure the client's polling with the server is active.
   */
  private void initializeMessagingBus() {
    GWT.runAsync(new RunAsyncCallback() {
      @Override
      public void onFailure(Throwable reason) {
        showError("failed to load RPC script from server", reason);
      }

      @Override
      public void onSuccess() {
        if (disconnected) {
          return;
        }

        RpcProxyLoader loader = GWT.create(RpcProxyLoader.class);
        loader.loadProxies(ClientMessageBusImpl.this);
        InitVotes.voteFor(RpcProxyLoader.class);

        final Timer initialPollTimer = new Timer() {
          @Override
          public void run() {
            performPoll();
          }
        };

        initialPollTimer.schedule(10);
      }
    });

  }

  /**
   * Add runnable tasks to be run after the message bus is initialized.
   *
   * @param run a {@link Runnable} task.
   */
  @Override
  public void addPostInitTask(Runnable run) {
    if (isInitialized() || postInit) {
      run.run();
      return;
    }
    postInitTasks.add(run);
  }

  @Override
  public void addSessionExpirationListener(SessionExpirationListener listener) {
    sessionExpirationListeners.add(Assert.notNull(listener));
  }

  @Override
  public void addPreInitializationListener(PreInitializationListener listener) {
    preInitializationListeners.add(Assert.notNull(listener));
  }

  /**
   * Do-nothing function, should eventually be able to add a global listener to receive all messages. Though global
   * message dispatches the message to all listeners attached.
   *
   * @param listener - listener to accept all messages dispatched
   */
  @Override
  public void addGlobalListener(MessageListener listener) {
  }

  /**
   * Adds a subscription listener, so it is possible to add subscriptions to the client.
   *
   * @param listener - subscription listener
   */
  @Override
  public void addSubscribeListener(SubscribeListener listener) {
    this.onSubscribeHooks.add(Assert.notNull(listener));
  }

  /**
   * Adds an unsubscription listener, so it is possible for applications to remove subscriptions from the client
   *
   * @param listener - unsubscription listener
   */
  @Override
  public void addUnsubscribeListener(UnsubscribeListener listener) {
    this.onUnsubscribeHooks.add(listener);
  }

  private static String decodeCommandMessage(Message msg) {
    StringBuilder decode = new StringBuilder(
            "<table><thead style='font-weight:bold;'><tr><td>Field</td><td>Value</td></tr></thead><tbody>");

    for (Map.Entry<String, Object> entry : msg.getParts().entrySet()) {
      decode.append("<tr><td>").append(entry.getKey()).append("</td><td>").append(entry.getValue()).append("</td></tr>");
    }

    return decode.append("</tbody></table>").toString();
  }


  private void logError(String message, String additionalDetails, Throwable e) {
    logAdapter.error(message + " -- Additional Details: " + additionalDetails, e);
  }

  private void ensureInitErrorDialog() {
    if (errorDialog == null) {
      errorDialog = new BusErrorDialog();
    }
  }

  private void showError(final String message, final Throwable e) {
    GWT.runAsync(new RunAsyncCallback() {
      @Override
      public void onFailure(Throwable reason) {
        LogUtil.nativeLog("could not load error dialog: " + reason);
      }

      @Override
      public void onSuccess() {
        ensureInitErrorDialog();
        errorDialog.addError(message, "", e);
      }
    });


    if (LogUtil.isNativeJavaScriptLoggerSupported()) {
      LogUtil.nativeLog(message);
    }
  }

  /**
   * Process the incoming payload and push all the incoming messages onto the bus.
   *
   * @param response -
   * @throws Exception -
   */
  private void procIncomingPayload(Response response) throws Exception {
    procPayload(response.getText());
  }

  public void procPayload(String text) {
    try {
      for (MarshalledMessage m : decodePayload(text)) {
        rxNumber++;
        _store(m.getSubject(), JSONUtilCli.decodeCommandMessage(m.getMessage()));
      }
    }
    catch (RuntimeException e) {
      e.printStackTrace();
      logError("Error delivering message into bus", text, e);
    }
  }

  @Override
  public void attachMonitor(BusMonitor monitor) {
  }

  @Override
  public void setLogAdapter(LogAdapter logAdapter) {
    this.logAdapter = logAdapter;
  }

  @Override
  public LogAdapter getLogAdapter() {
    return logAdapter;
  }

  private boolean executeInterceptorStack(boolean inbound, Message message) {
    boolean validMessage = true;
    for (MessageInterceptor intcp : interceptorStack) {
      if (inbound)
        validMessage = intcp.processInbound(message);
      else
        validMessage = intcp.processOutbound(message);

      if (!validMessage) // brute force for now
        throw new RuntimeException("Interceptor " + intcp.getClass() + " invalidates message");
    }

    return validMessage;
  }

  public void addInterceptor(MessageInterceptor interceptor) {
    interceptorStack.add(interceptor);
  }

  @Override
  public Set<String> getAllRegisteredSubjects() {
    return Collections.unmodifiableSet(subscriptions.keySet());
  }

  public void _store(String subject, Message msg) {
    if (subscriptions.containsKey(subject)) {
      for (MessageCallback cb : subscriptions.get(subject)) {
        cb.callback(msg);
      }
    }
  }

  public int getNextRequestNumber() {
    if (txNumber == Integer.MAX_VALUE) {
      txNumber = 0;
    }
    return txNumber++;
  }

  private String getWebSocketNegotiationString() {
    return "{\"" + MessageParts.CommandType.name() + "\":\"" + BusCommands.ConnectToQueue.name()
            + "\", \"" + MessageParts.ConnectionSessionKey + "\":\"" + sessionId + "\"" +
            ",\"" + MessageParts.WebSocketToken + "\":\"" + webSocketToken + "\"}";
  }

  public void attachWebSocketChannel(Object o) {
    LogUtil.log("web socket opened. sending negotiation message.");
    ClientWebSocketChannel.transmitToSocket(o, getWebSocketNegotiationString());
    webSocketChannel = o;
  }

  /**
   * The built-in, default error dialog.
   */
  class BusErrorDialog extends DialogBox {
    boolean showErrors = !GWT.isProdMode();
    Panel contentPanel = new AbsolutePanel();

    public BusErrorDialog() {
      setModal(true);

      VerticalPanel panel = new VerticalPanel();

      HorizontalPanel titleBar = new HorizontalPanel();
      titleBar.getElement().getStyle().setProperty("backgroundColor", "#A9A9A9");
      titleBar.getElement().getStyle().setWidth(100, Style.Unit.PCT);
      titleBar.getElement().getStyle().setProperty("borderBottom", "1px solid black");
      titleBar.getElement().getStyle().setProperty("marginBottom", "5px");

      Label titleBarLabel = new Label("An Error Occurred in the Bus");
      titleBarLabel.getElement().getStyle().setFontSize(10, Style.Unit.PT);
      titleBarLabel.getElement().getStyle().setFontWeight(Style.FontWeight.BOLDER);
      titleBarLabel.getElement().getStyle().setColor("white");

      titleBar.add(titleBarLabel);
      titleBar.setCellVerticalAlignment(titleBarLabel, HasVerticalAlignment.ALIGN_MIDDLE);

      HorizontalPanel buttonPanel = new HorizontalPanel();

      CheckBox showFurtherErrors = new CheckBox();
      showFurtherErrors.setValue(showErrors);
      showFurtherErrors.setText("Show further errors");
      showFurtherErrors.getElement().getStyle().setFontSize(10, Style.Unit.PT);
      showFurtherErrors.getElement().getStyle().setColor("white");

      showFurtherErrors.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
        @Override
        public void onValueChange(ValueChangeEvent<Boolean> booleanValueChangeEvent) {
          showErrors = booleanValueChangeEvent.getValue();
        }
      });

      Button disconnectFromServer = new Button("Disconnect Bus");
      disconnectFromServer.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
          if (Window.confirm("Are you sure you want to disconnect and de-federate the local bus from the server bus? "
                  + "This will permanently kill your session. You will need to refresh to reconnect. OK will proceed. Click "
                  + "Cancel to abort this operation")) {
            stop(true);
          }
        }
      });

      Button clearErrors = new Button("Clear Log");
      clearErrors.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
          contentPanel.clear();
        }
      });

      Button closeButton = new Button("Dismiss Error");
      closeButton.addClickHandler(new ClickHandler() {
        @Override
        public void onClick(ClickEvent event) {
          errorDialog.hide();
        }
      });

      buttonPanel.add(showFurtherErrors);
      buttonPanel.add(disconnectFromServer);
      buttonPanel.add(clearErrors);
      buttonPanel.add(closeButton);

      buttonPanel.setCellVerticalAlignment(showFurtherErrors, HasVerticalAlignment.ALIGN_MIDDLE);

      titleBar.add(buttonPanel);
      titleBar.setCellHorizontalAlignment(buttonPanel, HasHorizontalAlignment.ALIGN_RIGHT);

      panel.add(titleBar);

      Style s = panel.getElement().getStyle();

      s.setProperty("border", "1px");
      s.setProperty("borderStyle", "solid");
      s.setProperty("borderColor", "black");
      s.setProperty("backgroundColor", "#ede0c3");

      resize();

      panel.add(contentPanel);
      add(panel);

      getElement().getStyle().setZIndex(16777271);
    }

    public void addError(String message, String additionalDetails, Throwable e) {
      if (!showErrors) return;


      contentPanel.add(new HTML("<strong style='background:red;color:white;'>" + message + "</strong>"));

      StringBuilder buildTrace = new StringBuilder("<tt style=\"font-size:11px;\"><pre>");
      if (e != null) {
        e.printStackTrace();
        buildTrace.append(e.getClass().getName()).append(": ").append(e.getMessage()).append("<br/>");
        for (StackTraceElement ste : e.getStackTrace()) {
          buildTrace.append("  ").append(ste.toString()).append("<br/>");
        }
      }
      buildTrace.append("</pre>");

      contentPanel.add(new HTML(buildTrace.toString() + "<br/><strong>Additional Details:</strong>" + additionalDetails + "</tt>"));

      if (!isShowing()) {
        resize();
        show();
        center();
        setModal(true);
      }
    }

    private void resize() {
      contentPanel.setWidth(Window.getClientWidth() * 0.90 + "px");
      contentPanel.setHeight(Window.getClientHeight() * 0.90 + "px");
      contentPanel.getElement().getStyle().setProperty("overflow", "auto");
    }
  }


  private static native void declareDebugFunction() /*-{
    $wnd.errai_status = function () {
      @org.jboss.errai.bus.client.framework.ClientMessageBusImpl::_displayStatusToLog()();
    };

    $wnd.errai_list_services = function () {
      @org.jboss.errai.bus.client.framework.ClientMessageBusImpl::_listAvailableServicesToLog()();
    };

    $wnd.errai_show_error_console = function () {
      @org.jboss.errai.bus.client.framework.ClientMessageBusImpl::_showErrorConsole()();
    }

  }-*/;

  /**
   * Debugging functions.
   */
  private static void _displayStatusToLog() {
    ClientMessageBusImpl bus = (ClientMessageBusImpl) ErraiBus.get();

    LogUtil.nativeLog("ErraiBus Status");
    LogUtil.nativeLog("------------------------------------------------");
    LogUtil.nativeLog("Bus State              : " + (bus.initialized ? "Online/Federated" : "Disconnected"));
    LogUtil.nativeLog("");
    LogUtil.nativeLog("Comet Channel          : " + (bus.cometChannelOpen ? "Active" : "Offline"));
    LogUtil.nativeLog("  Endpoint (RX)        : " + (bus.getRecvBuilder().getUrl()));
    LogUtil.nativeLog("  Endpoint (TX)        : " + (bus.getSendBuilder().getUrl()));
    LogUtil.nativeLog("");
    LogUtil.nativeLog("WebSocket Channel      : " + (bus.webSocketOpen ? "Active" : "Offline"));
    LogUtil.nativeLog("  Endpoint (RX/TX)     : " + (bus.webSocketUrl));
    LogUtil.nativeLog("");
    LogUtil.nativeLog("Total TXs              : " + (bus.txNumber));
    LogUtil.nativeLog("Total RXs              : " + (bus.rxNumber));
    LogUtil.nativeLog("");
    LogUtil.nativeLog("Endpoints");
    LogUtil.nativeLog("  Remote (total)       : " + (bus.remotes.size()));
    LogUtil.nativeLog("  Local (total)        : " + (bus.subscriptions.size()));
    LogUtil.nativeLog("------------------------------------------------");
  }

  private static void _listAvailableServicesToLog() {
    ClientMessageBusImpl bus = (ClientMessageBusImpl) ErraiBus.get();

    LogUtil.nativeLog("Service and Routing Table");
    LogUtil.nativeLog("------------------------------------------------");
    LogUtil.nativeLog("[REMOTES]");

    for (String remoteName : bus.remotes.keySet()) {
      LogUtil.nativeLog(remoteName);
    }

    LogUtil.nativeLog("[LOCALS]");

    for (String localName : bus.subscriptions.keySet()) {
      LogUtil.nativeLog(localName + " (" + bus.subscriptions.get(localName).size() + ")");
    }

    LogUtil.nativeLog("------------------------------------------------");
  }

  private static void _showErrorConsole() {
    GWT.runAsync(new RunAsyncCallback() {
      @Override
      public void onFailure(Throwable reason) {
        LogUtil.nativeLog("could not load script to display error dialog: " + reason);
      }

      @Override
      public void onSuccess() {
        ClientMessageBusImpl bus = (ClientMessageBusImpl) ErraiBus.get();
        bus.ensureInitErrorDialog();
        bus.errorDialog.show();
      }
    });
  }

  /**
   * Checks whether remote bus communication is enabled.
   * <p/>
   * The JavaScript variable <code>erraiBusRemoteCommunicationEnabled</code> can be used
   * to control this value. If the variable is not present in the window object, the default
   * value <code>true</code> is returned.
   *
   * @return true if remote communication enabled, otherwise false.
   */
  public native boolean isRemoteCommunicationEnabled() /*-{
    if ($wnd.erraiBusRemoteCommunicationEnabled === undefined || $wnd.erraiBusRemoteCommunicationEnabled.length === 0) {
      return true;
    }
    else {
      return $wnd.erraiBusRemoteCommunicationEnabled;
    }
  }-*/;
}
TOP

Related Classes of org.jboss.errai.bus.client.framework.ClientMessageBusImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.