diff options
author | Leif Åstrand <leif@vaadin.com> | 2013-04-22 13:19:31 +0300 |
---|---|---|
committer | Vaadin Code Review <review@vaadin.com> | 2013-04-22 12:10:27 +0000 |
commit | 2c46baf7206d3735d737b8bda08596abe2fd649b (patch) | |
tree | 2deeed465c161a545af052c1e49bb09e2343cf51 | |
parent | 3cf35ba23128490994c9fa4a2f6d4475ceea932a (diff) | |
download | vaadin-framework-2c46baf7206d3735d737b8bda08596abe2fd649b.tar.gz vaadin-framework-2c46baf7206d3735d737b8bda08596abe2fd649b.zip |
Add PushConnection interface (#11655)
* Add PushConnection interface and rename old class to
AtmospherePushConnection
* Define deferred binding to use AtmospherePushConnection by default
* Redesign connection and disconnection workflow to better cope with
situations where connection is quickly toggled
Change-Id: I9b9427c2df40d446a25895eb39e7b166cb929a85
5 files changed, 468 insertions, 362 deletions
diff --git a/client/src/com/vaadin/Vaadin.gwt.xml b/client/src/com/vaadin/Vaadin.gwt.xml index 11197bffc5..6529743503 100644 --- a/client/src/com/vaadin/Vaadin.gwt.xml +++ b/client/src/com/vaadin/Vaadin.gwt.xml @@ -39,6 +39,10 @@ class="com.vaadin.client.metadata.ConnectorBundleLoader" /> </generate-with> + <replace-with class="com.vaadin.client.communication.AtmospherePushConnection"> + <when-type-is class="com.vaadin.client.communication.PushConnection" /> + </replace-with> + <!-- Set vaadin.profiler to true to include profiling support in the module --> <define-property name="vaadin.profiler" values="true,false" /> <set-property name="vaadin.profiler" value="false" /> diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java index 179a19ed85..93a2e90c07 100644 --- a/client/src/com/vaadin/client/ApplicationConnection.java +++ b/client/src/com/vaadin/client/ApplicationConnection.java @@ -2431,7 +2431,7 @@ public class ApplicationConnection { private void doSendPendingVariableChanges() { if (applicationRunning) { - if (hasActiveRequest()) { + if (hasActiveRequest() || (push != null && !push.isActive())) { // skip empty queues if there are pending bursts to be sent if (pendingInvocations.size() > 0 || pendingBursts.size() == 0) { pendingBursts.add(pendingInvocations); @@ -3402,32 +3402,31 @@ public class ApplicationConnection { */ public void setPushEnabled(boolean enabled) { if (enabled && push == null) { - - final PushConnection push = GWT.create(PushConnection.class); + push = GWT.create(PushConnection.class); push.init(this); - - push.runWhenAtmosphereLoaded(new Command() { + } else if (!enabled && push != null && push.isActive()) { + push.disconnect(new Command() { @Override public void execute() { - ApplicationConnection.this.push = push; - - final String pushUri = addGetParameters( - translateVaadinUri(ApplicationConstants.APP_PROTOCOL_PREFIX - + ApplicationConstants.PUSH_PATH + '/'), - UIConstants.UI_ID_PARAMETER + "=" - + getConfiguration().getUIId()); + push = null; + /* + * If push has been enabled again while we were waiting for + * the old connection to disconnect, now is the right time + * to open a new connection + */ + if (uIConnector.getState().pushMode.isEnabled()) { + setPushEnabled(true); + } - Scheduler.get().scheduleDeferred(new Command() { - @Override - public void execute() { - push.connect(pushUri); - } - }); + /* + * Send anything that was enqueued while we waited for the + * connection to close + */ + if (pendingInvocations.size() > 0) { + sendPendingVariableChanges(); + } } }); - } else if (!enabled && push != null) { - push.disconnect(); - push = null; } } diff --git a/client/src/com/vaadin/client/communication/AtmospherePushConnection.java b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java new file mode 100644 index 0000000000..d3321a41a7 --- /dev/null +++ b/client/src/com/vaadin/client/communication/AtmospherePushConnection.java @@ -0,0 +1,401 @@ +/* + * Copyright 2000-2013 Vaadin Ltd. + * + * 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 com.vaadin.client.communication; + +import java.util.ArrayList; + +import com.google.gwt.core.client.JavaScriptObject; +import com.google.gwt.core.client.Scheduler; +import com.google.gwt.user.client.Command; +import com.vaadin.client.ApplicationConnection; +import com.vaadin.client.ResourceLoader; +import com.vaadin.client.ResourceLoader.ResourceLoadEvent; +import com.vaadin.client.ResourceLoader.ResourceLoadListener; +import com.vaadin.client.VConsole; +import com.vaadin.shared.ApplicationConstants; +import com.vaadin.shared.ui.ui.UIConstants; + +/** + * The default {@link PushConnection} implementation that uses Atmosphere for + * handling the communication channel. + * + * @author Vaadin Ltd + * @since 7.1 + */ +public class AtmospherePushConnection implements PushConnection { + + protected enum State { + /** + * Opening request has been sent, but still waiting for confirmation + */ + CONNECT_PENDING, + + /** + * Connection is open and ready to use. + */ + CONNECTED, + + /** + * Connection was disconnected while the connection was pending. Wait + * for the connection to get established before closing it. No new + * messages are accepted, but pending messages will still be delivered. + */ + DISCONNECT_PENDING, + + /** + * Connection has been disconnected and should not be used any more. + */ + DISCONNECTED; + } + + private ApplicationConnection connection; + + private JavaScriptObject socket; + + private ArrayList<String> messageQueue = new ArrayList<String>(); + + private State state = State.CONNECT_PENDING; + + private AtmosphereConfiguration config; + + private String uri; + + /** + * Keeps track of the disconnect confirmation command for cases where + * pending messages should be pushed before actually disconnecting. + */ + private Command pendingDisconnectCommand; + + public AtmospherePushConnection() { + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.client.communication.PushConenction#init(com.vaadin.client + * .ApplicationConnection) + */ + @Override + public void init(final ApplicationConnection connection) { + this.connection = connection; + + runWhenAtmosphereLoaded(new Command() { + @Override + public void execute() { + Scheduler.get().scheduleDeferred(new Command() { + @Override + public void execute() { + connect(); + } + }); + } + }); + } + + private void connect() { + String baseUrl = connection + .translateVaadinUri(ApplicationConstants.APP_PROTOCOL_PREFIX + + ApplicationConstants.PUSH_PATH + '/'); + String extraParams = UIConstants.UI_ID_PARAMETER + "=" + + connection.getConfiguration().getUIId(); + + // uri is needed to identify the right connection when closing + uri = ApplicationConnection.addGetParameters(baseUrl, extraParams); + + VConsole.log("Establishing push connection"); + socket = doConnect(uri, getConfig()); + } + + @Override + public boolean isActive() { + switch (state) { + case CONNECT_PENDING: + case CONNECTED: + return true; + default: + return false; + } + } + + /* + * (non-Javadoc) + * + * @see + * com.vaadin.client.communication.PushConenction#push(java.lang.String) + */ + @Override + public void push(String message) { + switch (state) { + case CONNECT_PENDING: + assert isActive(); + VConsole.log("Queuing push message: " + message); + messageQueue.add(message); + break; + case CONNECTED: + assert isActive(); + VConsole.log("Sending push message: " + message); + doPush(socket, message); + break; + case DISCONNECT_PENDING: + case DISCONNECTED: + throw new IllegalStateException("Can not push after disconnecting"); + } + } + + protected AtmosphereConfiguration getConfig() { + if (config == null) { + config = createConfig(); + } + return config; + } + + protected void onOpen(AtmosphereResponse response) { + VConsole.log("Push connection established using " + + response.getTransport()); + for (String message : messageQueue) { + doPush(socket, message); + } + messageQueue.clear(); + + switch (state) { + case CONNECT_PENDING: + state = State.CONNECTED; + break; + case DISCONNECT_PENDING: + // Set state to connected to make disconnect close the connection + state = State.CONNECTED; + assert pendingDisconnectCommand != null; + disconnect(pendingDisconnectCommand); + break; + case CONNECTED: + // IE likes to open the same connection multiple times, just ignore + break; + default: + throw new IllegalStateException( + "Got onOpen event when conncetion state is " + state + + ". This should never happen."); + } + } + + /* + * (non-Javadoc) + * + * @see com.vaadin.client.communication.PushConenction#disconnect() + */ + @Override + public void disconnect(Command command) { + assert command != null; + + switch (state) { + case CONNECT_PENDING: + // Make the connection callback initiate the disconnection again + state = State.DISCONNECT_PENDING; + pendingDisconnectCommand = command; + break; + case CONNECTED: + // Normal disconnect + VConsole.log("Closing push connection"); + doDisconnect(uri); + state = State.DISCONNECTED; + command.execute(); + break; + case DISCONNECT_PENDING: + case DISCONNECTED: + throw new IllegalStateException("Can not disconnect more than once"); + } + } + + protected void onMessage(AtmosphereResponse response) { + String message = response.getResponseBody(); + if (message.startsWith("for(;;);")) { + VConsole.log("Received push message: " + message); + // "for(;;);[{json}]" -> "{json}" + message = message.substring(9, message.length() - 1); + connection.handlePushMessage(message); + } + } + + /** + * Called if the transport mechanism cannot be used and the fallback will be + * tried + */ + protected void onTransportFailure() { + VConsole.log("Push connection using primary method (" + + getConfig().getTransport() + ") failed. Trying with " + + getConfig().getFallbackTransport()); + } + + /** + * Called if the push connection fails. Atmosphere will automatically retry + * the connection until successful. + * + */ + protected void onError() { + VConsole.error("Push connection using " + + getConfig().getTransport() + + " failed!"); + } + + public static abstract class AbstractJSO extends JavaScriptObject { + protected AbstractJSO() { + + } + + protected final native String getStringValue(String key) + /*-{ + return this[key]; + }-*/; + + protected final native void setStringValue(String key, String value) + /*-{ + this[key] = value; + }-*/; + + protected final native int getIntValue(String key) + /*-{ + return this[key]; + }-*/; + + protected final native void setIntValue(String key, int value) + /*-{ + this[key] = value; + }-*/; + + } + + public static class AtmosphereConfiguration extends AbstractJSO { + + protected AtmosphereConfiguration() { + super(); + } + + public final String getTransport() { + return getStringValue("transport"); + } + + public final String getFallbackTransport() { + return getStringValue("fallbackTransport"); + } + + public final void setTransport(String transport) { + setStringValue("transport", transport); + } + + public final void setFallbackTransport(String fallbackTransport) { + setStringValue("fallbackTransport", fallbackTransport); + } + } + + public static class AtmosphereResponse extends AbstractJSO { + + protected AtmosphereResponse() { + + } + + public final String getResponseBody() { + return getStringValue("responseBody"); + } + + public final String getState() { + return getStringValue("state"); + } + + public final String getError() { + return getStringValue("error"); + } + + public final String getTransport() { + return getStringValue("transport"); + } + + } + + protected native AtmosphereConfiguration createConfig() + /*-{ + return { + transport: 'websocket', + fallbackTransport: 'streaming', + contentType: 'application/json; charset=UTF-8', + reconnectInterval: '5000', + trackMessageLength: true + }; + }-*/; + + private native JavaScriptObject doConnect(String uri, + JavaScriptObject config) + /*-{ + var self = this; + + config.url = uri; + config.onOpen = $entry(function(response) { + self.@com.vaadin.client.communication.AtmospherePushConnection::onOpen(*)(response); + }); + config.onMessage = $entry(function(response) { + self.@com.vaadin.client.communication.AtmospherePushConnection::onMessage(*)(response); + }); + config.onError = $entry(function(response) { + self.@com.vaadin.client.communication.AtmospherePushConnection::onError()(response); + }); + config.onTransportFailure = $entry(function(reason,request) { + self.@com.vaadin.client.communication.AtmospherePushConnection::onTransportFailure(*)(reason); + }); + + return $wnd.jQueryVaadin.atmosphere.subscribe(config); + }-*/; + + private native void doPush(JavaScriptObject socket, String message) + /*-{ + socket.push(message); + }-*/; + + private static native void doDisconnect(String url) + /*-{ + $wnd.jQueryVaadin.atmosphere.unsubscribeUrl(url); + }-*/; + + private static native boolean isAtmosphereLoaded() + /*-{ + return $wnd.jQueryVaadin != undefined; + }-*/; + + private void runWhenAtmosphereLoaded(final Command command) { + + if (isAtmosphereLoaded()) { + command.execute(); + } else { + VConsole.log("Loading " + ApplicationConstants.VAADIN_PUSH_JS); + ResourceLoader.get().loadScript( + connection.getConfiguration().getVaadinDirUrl() + + ApplicationConstants.VAADIN_PUSH_JS, + new ResourceLoadListener() { + @Override + public void onLoad(ResourceLoadEvent event) { + VConsole.log(ApplicationConstants.VAADIN_PUSH_JS + + " loaded"); + command.execute(); + } + + @Override + public void onError(ResourceLoadEvent event) { + VConsole.error(event.getResourceUrl() + + " could not be loaded. Push will not work."); + } + }); + } + } +} diff --git a/client/src/com/vaadin/client/communication/PushConnection.java b/client/src/com/vaadin/client/communication/PushConnection.java index b872460de4..33b1c31411 100644 --- a/client/src/com/vaadin/client/communication/PushConnection.java +++ b/client/src/com/vaadin/client/communication/PushConnection.java @@ -1,31 +1,7 @@ -/* - * Copyright 2000-2013 Vaadin Ltd. - * - * 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 com.vaadin.client.communication; -import java.util.ArrayList; - -import com.google.gwt.core.client.JavaScriptObject; import com.google.gwt.user.client.Command; import com.vaadin.client.ApplicationConnection; -import com.vaadin.client.ResourceLoader; -import com.vaadin.client.ResourceLoader.ResourceLoadEvent; -import com.vaadin.client.ResourceLoader.ResourceLoadListener; -import com.vaadin.client.VConsole; -import com.vaadin.shared.ApplicationConstants; /** * Represents the client-side endpoint of a bidirectional ("push") communication @@ -37,337 +13,63 @@ import com.vaadin.shared.ApplicationConstants; * @author Vaadin Ltd * @since 7.1 */ -public class PushConnection { - - protected enum State { - /** - * Connection is newly created and has not yet been started. - */ - NEW, - - /** - * Opening request has been sent, but still waiting for confirmation - */ - CONNECT_PENDING, - - /** - * Connection is open and ready to use. - */ - CONNECTED, - - /** - * Connection was disconnected while the connection was pending. Wait - * for the connection to get established before closing it. No new - * messages are accepted, but pending messages will still be delivered. - */ - DISCONNECT_PENDING, - - /** - * Connection has been disconnected and should not be used any more. - */ - DISCONNECTED; - } - - private ApplicationConnection connection; - - private JavaScriptObject socket; - - private ArrayList<String> messageQueue = new ArrayList<String>(); - - private State state = State.NEW; - - private AtmosphereConfiguration config; - - private String uri; - - public PushConnection() { - } +public interface PushConnection { /** - * Two-phase construction to allow using GWT.create() + * Two-phase construction to allow using GWT.create(). * * @param connection * The ApplicationConnection */ - public void init(ApplicationConnection connection) { - this.connection = connection; - } - - public void connect(String uri) { - if (state != State.NEW) { - throw new IllegalStateException( - "Connection has already been connected."); - } - - state = State.CONNECT_PENDING; - // uri is needed to identify the right connection when closing - this.uri = uri; - VConsole.log("Establishing push connection"); - socket = doConnect(uri, getConfig()); - } - - public void push(String message) { - switch (state) { - case CONNECT_PENDING: - VConsole.log("Queuing push message: " + message); - messageQueue.add(message); - break; - case CONNECTED: - VConsole.log("Sending push message: " + message); - doPush(socket, message); - break; - case NEW: - throw new IllegalStateException("Can not push before connecting"); - case DISCONNECT_PENDING: - case DISCONNECTED: - throw new IllegalStateException("Can not push after disconnecting"); - } - } - - protected AtmosphereConfiguration getConfig() { - if (config == null) { - config = createConfig(); - } - return config; - } - - protected void onOpen(AtmosphereResponse response) { - VConsole.log("Push connection established using " - + response.getTransport()); - for (String message : messageQueue) { - doPush(socket, message); - } - messageQueue.clear(); - - switch (state) { - case CONNECT_PENDING: - state = State.CONNECTED; - break; - case DISCONNECT_PENDING: - // Set state to connected to make disconnect close the connection - state = State.CONNECTED; - disconnect(); - break; - case CONNECTED: - // IE likes to open the same connection multiple times, just ignore - break; - default: - throw new IllegalStateException( - "Got onOpen event when conncetion state is " + state - + ". This should never happen."); - } - } + public void init(ApplicationConnection connection); /** - * Closes the push connection. - */ - public void disconnect() { - switch (state) { - case NEW: - // Nothing to close up, just update state - state = State.DISCONNECTED; - break; - case CONNECT_PENDING: - // Wait until connection is established before closing it - state = State.DISCONNECT_PENDING; - break; - case CONNECTED: - // Normal disconnect - VConsole.log("Closing push connection"); - doDisconnect(uri); - state = State.DISCONNECTED; - break; - case DISCONNECT_PENDING: - case DISCONNECTED: - // Nothing more to do - break; - } - } - - protected void onMessage(AtmosphereResponse response) { - String message = response.getResponseBody(); - if (message.startsWith("for(;;);")) { - VConsole.log("Received push message: " + message); - // "for(;;);[{json}]" -> "{json}" - message = message.substring(9, message.length() - 1); - connection.handlePushMessage(message); - } - } - - /** - * Called if the transport mechanism cannot be used and the fallback will be - * tried + * Pushes a message to the server. Will throw an exception if the connection + * is not active (see {@link #isActive()}). + * <p> + * Implementation detail: The implementation is responsible for queuing + * messages that are pushed after {@link #init(ApplicationConnection)} has + * been called but before the connection has internally been set up and then + * replay those messages in the original order when the connection has been + * established. + * + * @param message + * the message to push + * @throws IllegalStateException + * if this connection is not active + * + * @see #isActive() */ - protected void onTransportFailure() { - VConsole.log("Push connection using primary method (" - + getConfig().getTransport() + ") failed. Trying with " - + getConfig().getFallbackTransport()); - } + public void push(String message); /** - * Called if the push connection fails. Atmosphere will automatically retry - * the connection until successful. + * Checks whether this push connection is in a state where it can push + * messages to the server. A connection is active until + * {@link #disconnect(Command)} has been called. * + * @return <code>true</code> if this connection can accept new messages; + * <code>false</code> if this connection is disconnected or + * disconnecting. */ - protected void onError() { - VConsole.error("Push connection using " - + getConfig().getTransport() - + " failed!"); - } - - public static abstract class AbstractJSO extends JavaScriptObject { - protected AbstractJSO() { - - } - - protected final native String getStringValue(String key) - /*-{ - return this[key]; - }-*/; - - protected final native void setStringValue(String key, String value) - /*-{ - this[key] = value; - }-*/; - - protected final native int getIntValue(String key) - /*-{ - return this[key]; - }-*/; - - protected final native void setIntValue(String key, int value) - /*-{ - this[key] = value; - }-*/; - - } - - public static class AtmosphereConfiguration extends AbstractJSO { - - protected AtmosphereConfiguration() { - super(); - } - - public final String getTransport() { - return getStringValue("transport"); - } - - public final String getFallbackTransport() { - return getStringValue("fallbackTransport"); - } - - public final void setTransport(String transport) { - setStringValue("transport", transport); - } - - public final void setFallbackTransport(String fallbackTransport) { - setStringValue("fallbackTransport", fallbackTransport); - } - } - - public static class AtmosphereResponse extends AbstractJSO { - - protected AtmosphereResponse() { - - } - - public final String getResponseBody() { - return getStringValue("responseBody"); - } - - public final String getState() { - return getStringValue("state"); - } - - public final String getError() { - return getStringValue("error"); - } - - public final String getTransport() { - return getStringValue("transport"); - } - - } - - protected native AtmosphereConfiguration createConfig() - /*-{ - return { - transport: 'websocket', - fallbackTransport: 'streaming', - contentType: 'application/json; charset=UTF-8', - reconnectInterval: '5000', - trackMessageLength: true - }; - }-*/; - - private native JavaScriptObject doConnect(String uri, - JavaScriptObject config) - /*-{ - var self = this; - - config.url = uri; - config.onOpen = $entry(function(response) { - self.@com.vaadin.client.communication.PushConnection::onOpen(*)(response); - }); - config.onMessage = $entry(function(response) { - self.@com.vaadin.client.communication.PushConnection::onMessage(*)(response); - }); - config.onError = $entry(function(response) { - self.@com.vaadin.client.communication.PushConnection::onError()(response); - }); - config.onTransportFailure = $entry(function(reason,request) { - self.@com.vaadin.client.communication.PushConnection::onTransportFailure(*)(reason); - }); - - return $wnd.jQueryVaadin.atmosphere.subscribe(config); - }-*/; - - private native void doPush(JavaScriptObject socket, String message) - /*-{ - socket.push(message); - }-*/; - - private static native void doDisconnect(String url) - /*-{ - $wnd.jQueryVaadin.atmosphere.unsubscribeUrl(url); - }-*/; - - private static native boolean isAtmosphereLoaded() - /*-{ - return $wnd.jQueryVaadin != undefined; - }-*/; + public boolean isActive(); /** - * Runs the provided command when the Atmosphere javascript has been loaded. - * If the script has already been loaded, the command is run immediately. + * Closes the push connection. To ensure correct message delivery order, new + * messages should not be sent using any other channel until it has been + * confirmed that all messages pending for this connection have been + * delivered. The provided command callback is invoked when messages can be + * passed using some other communication channel. + * <p> + * After this method has been called, {@link #isActive()} returns + * <code>false</code>. Calling this method for a connection that is no + * longer active will throw an exception. * * @param command - * the command to run when Atmosphere has been loaded. + * callback command invoked when the connection has been properly + * disconnected + * @throws IllegalStateException + * if this connection is not active */ - public void runWhenAtmosphereLoaded(final Command command) { - assert command != null; - - if (isAtmosphereLoaded()) { - command.execute(); - } else { - VConsole.log("Loading " + ApplicationConstants.VAADIN_PUSH_JS); - ResourceLoader.get().loadScript( - connection.getConfiguration().getVaadinDirUrl() - + ApplicationConstants.VAADIN_PUSH_JS, - new ResourceLoadListener() { - @Override - public void onLoad(ResourceLoadEvent event) { - VConsole.log(ApplicationConstants.VAADIN_PUSH_JS - + " loaded"); - command.execute(); - } + public void disconnect(Command command); - @Override - public void onError(ResourceLoadEvent event) { - VConsole.log(event.getResourceUrl() - + " could not be loaded. Push will not work."); - } - }); - } - } -} +}
\ No newline at end of file diff --git a/uitest/src/com/vaadin/tests/widgetset/client/TestingPushConnection.java b/uitest/src/com/vaadin/tests/widgetset/client/TestingPushConnection.java index 8453daabcd..8d00598907 100644 --- a/uitest/src/com/vaadin/tests/widgetset/client/TestingPushConnection.java +++ b/uitest/src/com/vaadin/tests/widgetset/client/TestingPushConnection.java @@ -2,9 +2,9 @@ package com.vaadin.tests.widgetset.client; import com.google.gwt.user.client.Window; import com.vaadin.client.ApplicationConnection; -import com.vaadin.client.communication.PushConnection; +import com.vaadin.client.communication.AtmospherePushConnection; -public class TestingPushConnection extends PushConnection { +public class TestingPushConnection extends AtmospherePushConnection { private String transport; |