diff options
-rwxr-xr-x | client/src/com/vaadin/DefaultWidgetSet.gwt.xml | 4 | ||||
-rw-r--r-- | client/src/com/vaadin/client/communication/DefaultReconnectDialog.java | 85 | ||||
-rw-r--r-- | client/src/com/vaadin/client/communication/DefaultReconnectDialog.ui.xml (renamed from client/src/com/vaadin/client/communication/ReconnectDialog.ui.xml) | 0 | ||||
-rw-r--r-- | client/src/com/vaadin/client/communication/ReconnectDialog.java | 58 | ||||
-rw-r--r-- | client/src/com/vaadin/client/communication/ReconnectingCommunicationProblemHandler.java | 381 | ||||
-rw-r--r-- | client/src/com/vaadin/client/communication/ServerCommunicationHandler.java | 3 | ||||
-rw-r--r-- | shared/src/com/vaadin/shared/ui/ui/UIState.java | 1 | ||||
-rw-r--r-- | uitest/src/com/vaadin/tests/application/CommErrorEmulatorUI.java | 16 |
8 files changed, 438 insertions, 110 deletions
diff --git a/client/src/com/vaadin/DefaultWidgetSet.gwt.xml b/client/src/com/vaadin/DefaultWidgetSet.gwt.xml index 01cbdc3222..03d0950126 100755 --- a/client/src/com/vaadin/DefaultWidgetSet.gwt.xml +++ b/client/src/com/vaadin/DefaultWidgetSet.gwt.xml @@ -26,6 +26,10 @@ class="com.vaadin.client.metadata.ConnectorBundleLoader" /> </generate-with> + <replace-with + class="com.vaadin.client.communication.DefaultReconnectDialog"> + <when-type-is class="com.vaadin.client.communication.ReconnectDialog" /> + </replace-with> <!-- Since 7.2. Compile all permutations (browser support) into one Javascript file. Speeds up compilation and does not make the Javascript significantly diff --git a/client/src/com/vaadin/client/communication/DefaultReconnectDialog.java b/client/src/com/vaadin/client/communication/DefaultReconnectDialog.java new file mode 100644 index 0000000000..60ecce8ae0 --- /dev/null +++ b/client/src/com/vaadin/client/communication/DefaultReconnectDialog.java @@ -0,0 +1,85 @@ +/* + * Copyright 2000-2014 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 com.google.gwt.core.shared.GWT; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.event.shared.HandlerRegistration; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.ui.HTMLPanel; +import com.google.gwt.user.client.ui.Label; +import com.vaadin.client.ApplicationConnection; +import com.vaadin.client.WidgetUtil; +import com.vaadin.client.ui.VOverlay; + +/** + * The default implementation of the reconnect dialog + * + * @since 7.6 + * @author Vaadin Ltd + */ +public class DefaultReconnectDialog extends VOverlay implements ReconnectDialog { + interface MyUiBinder extends UiBinder<HTMLPanel, DefaultReconnectDialog> { + } + + private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + + private static final String STYLE_RECONNECTING = "active"; + + @UiField + public Label label; + + private HandlerRegistration clickHandler = null; + + public DefaultReconnectDialog() { + super(false, true); + addStyleName("v-reconnect-dialog"); + setWidget(uiBinder.createAndBindUi(this)); + } + + @Override + public void setText(String text) { + label.setText(text); + } + + @Override + public void setReconnecting(boolean reconnecting) { + setStyleName(STYLE_RECONNECTING, reconnecting); + + // Click to refresh after giving up + if (!reconnecting) { + clickHandler = addDomHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + // refresh + WidgetUtil.redirect(null); + } + }, ClickEvent.getType()); + } else { + if (clickHandler != null) { + clickHandler.removeHandler(); + } + } + } + + @Override + public void show(ApplicationConnection connection) { + setOwner(connection.getUIConnector().getWidget()); + show(); + } +} diff --git a/client/src/com/vaadin/client/communication/ReconnectDialog.ui.xml b/client/src/com/vaadin/client/communication/DefaultReconnectDialog.ui.xml index 885588f8a5..885588f8a5 100644 --- a/client/src/com/vaadin/client/communication/ReconnectDialog.ui.xml +++ b/client/src/com/vaadin/client/communication/DefaultReconnectDialog.ui.xml diff --git a/client/src/com/vaadin/client/communication/ReconnectDialog.java b/client/src/com/vaadin/client/communication/ReconnectDialog.java index b69f51c165..21f47ecbf6 100644 --- a/client/src/com/vaadin/client/communication/ReconnectDialog.java +++ b/client/src/com/vaadin/client/communication/ReconnectDialog.java @@ -15,35 +15,51 @@ */ package com.vaadin.client.communication; -import com.google.gwt.core.shared.GWT; -import com.google.gwt.uibinder.client.UiBinder; -import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.user.client.ui.HTMLPanel; -import com.google.gwt.user.client.ui.Label; -import com.vaadin.client.ui.VOverlay; +import com.vaadin.client.ApplicationConnection; /** - * + * Interface which must be implemented by the reconnect dialog * * @since 7.6 * @author Vaadin Ltd */ -public class ReconnectDialog extends VOverlay { - interface MyUiBinder extends UiBinder<HTMLPanel, ReconnectDialog> { - } +public interface ReconnectDialog { + + /** + * Sets the main text shown in the dialog + * + * @param text + * the text to show + */ + void setText(String text); - private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class); + /** + * Sets the reconnecting state, which is true if we are trying to + * re-establish a connection with the server. + * + * @param reconnecting + * true if we are trying to re-establish the server connection, + * false if we have given up + */ + void setReconnecting(boolean reconnecting); - @UiField - public Label label; + /** + * Checks if the reconnect dialog is visible to the user + * + * @return true if the user can see the dialog, false otherwise + */ + boolean isVisible(); - public ReconnectDialog() { - super(false, true); - addStyleName("v-reconnect-dialog"); - setWidget(uiBinder.createAndBindUi(this)); - } + /** + * Shows the dialog to the user + * + * @param connection + * the application connection this is related to + */ + void show(ApplicationConnection connection); - public void setText(String text) { - label.setText(text); - } + /** + * Hides the dialog from the user + */ + void hide(); } diff --git a/client/src/com/vaadin/client/communication/ReconnectingCommunicationProblemHandler.java b/client/src/com/vaadin/client/communication/ReconnectingCommunicationProblemHandler.java index 7f09c502d8..33f7395536 100644 --- a/client/src/com/vaadin/client/communication/ReconnectingCommunicationProblemHandler.java +++ b/client/src/com/vaadin/client/communication/ReconnectingCommunicationProblemHandler.java @@ -24,6 +24,8 @@ import com.google.gwt.regexp.shared.MatchResult; import com.google.gwt.regexp.shared.RegExp; import com.google.gwt.user.client.Timer; import com.vaadin.client.ApplicationConnection; +import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent; +import com.vaadin.client.ApplicationConnection.ApplicationStoppedHandler; import com.vaadin.client.WidgetUtil; import com.vaadin.shared.ui.ui.UIState.ReconnectDialogConfigurationState; @@ -45,21 +47,78 @@ import elemental.json.JsonObject; public class ReconnectingCommunicationProblemHandler implements CommunicationProblemHandler { - private static final String STYLE_RECONNECTING = "active"; private ApplicationConnection connection; private ReconnectDialog reconnectDialog = GWT.create(ReconnectDialog.class); private int reconnectAttempt = 0; private Type reconnectionCause = null; - private enum Type { - HEARTBEAT, MESSAGE + private Timer scheduledReconnect; + private Timer dialogShowTimer = new Timer() { + + @Override + public void run() { + showDialog(); + } + + }; + + protected enum Type { + HEARTBEAT(0), PUSH(1), XHR(2); + + private int priority; + + private Type(int priority) { + this.priority = priority; + } + + public boolean isMessage() { + return this == PUSH || this == XHR; + } + + /** + * Checks if this type is of higher priority than the given type + * + * @param type + * the type to compare to + * @return true if this type has higher priority than the given type, + * false otherwise + */ + public boolean isHigherPriorityThan(Type type) { + return priority > type.priority; + } } @Override public void setConnection(ApplicationConnection connection) { this.connection = connection; + + connection.addHandler(ApplicationStoppedEvent.TYPE, + new ApplicationStoppedHandler() { + @Override + public void onApplicationStopped( + ApplicationStoppedEvent event) { + if (isReconnecting()) { + giveUp(); + } + if (scheduledReconnect != null + && scheduledReconnect.isRunning()) { + scheduledReconnect.cancel(); + } + } + + }); }; + /** + * Checks if we are currently trying to reconnect + * + * @return true if we have noted a problem and are trying to re-establish + * server connection, false otherwise + */ + private boolean isReconnecting() { + return reconnectionCause != null; + } + private static Logger getLogger() { return Logger.getLogger(ReconnectingCommunicationProblemHandler.class .getName()); @@ -76,8 +135,9 @@ public class ReconnectingCommunicationProblemHandler implements @Override public void xhrException(CommunicationProblemEvent event) { - getLogger().warning("xhrException"); - handleRecoverableError(Type.MESSAGE, event.getPayload()); + debug("xhrException"); + endRequest(); + handleRecoverableError(Type.XHR, event.getPayload()); } @Override @@ -95,6 +155,10 @@ public class ReconnectingCommunicationProblemHandler implements // Session expired getConnection().showSessionExpiredError(null); stopApplication(); + } else if (response.getStatusCode() == Response.SC_NOT_FOUND) { + // UI closed, do nothing as the UI will react to this + // Should not trigger reconnect dialog as this will prevent user + // input } else { handleRecoverableError(Type.HEARTBEAT, null); } @@ -102,61 +166,204 @@ public class ReconnectingCommunicationProblemHandler implements @Override public void heartbeatOk() { - getLogger().warning("heartbeatOk"); - resolveTemporaryError(Type.HEARTBEAT, true); + debug("heartbeatOk"); + if (isReconnecting()) { + resolveTemporaryError(Type.HEARTBEAT); + } } - protected void handleRecoverableError(Type type, final JsonObject payload) { - getLogger().warning("handleTemporaryError(" + type + ")"); + private void debug(String msg) { + if (false) { + getLogger().warning(msg); + } + } - reconnectAttempt++; - reconnectionCause = type; - if (!reconnectDialog.isAttached()) { - reconnectDialog.setStyleName(STYLE_RECONNECTING, true); - reconnectDialog.setOwner(getConnection().getUIConnector() - .getWidget()); - reconnectDialog.show(); + /** + * Called whenever an error occurs in communication which should be handled + * by showing the reconnect dialog and retrying communication until + * successful again + * + * @param type + * The type of failure detected + * @param payload + * The message which did not reach the server, or null if no + * message was involved (heartbeat or push connection failed) + */ + protected void handleRecoverableError(Type type, final JsonObject payload) { + debug("handleTemporaryError(" + type + ")"); + if (!connection.isApplicationRunning()) { + return; } - if (payload != null) { - getConnection().getServerCommunicationHandler().endRequest(); + + if (!isReconnecting()) { + // First problem encounter + reconnectionCause = type; + getLogger().warning("Reconnecting because of " + type + " failure"); + // Precaution only as there should never be a dialog at this point + // and no timer running + stopDialogTimer(); + if (isDialogVisible()) { + hideDialog(); + } + + // Show dialog after grace period, still continue to try to + // reconnect even before it is shown + dialogShowTimer.schedule(getConfiguration().dialogGracePeriod); + } else { + // We are currently trying to reconnect + // Priority is HEARTBEAT -> PUSH -> XHR + // If a higher priority issues is resolved, we can assume the lower + // one will be also + if (type.isHigherPriorityThan(reconnectionCause)) { + getLogger().warning( + "Now reconnecting because of " + type + " failure"); + reconnectionCause = type; + } } - if (reconnectAttempt >= getConfiguration().reconnectAttempts) { - // Max attempts reached - reconnectDialog.setText(getDialogTextGaveUp(reconnectAttempt)); - reconnectDialog.setStyleName(STYLE_RECONNECTING, false); + if (reconnectionCause != type) { + return; + } - getConnection().setApplicationRunning(false); + reconnectAttempt++; + getLogger().info( + "Reconnect attempt " + reconnectAttempt + " for " + type); + if (reconnectAttempt >= getConfiguration().reconnectAttempts) { + // Max attempts reached, stop trying + giveUp(); } else { - reconnectDialog.setText(getDialogText(reconnectAttempt)); + updateDialog(); + scheduleReconnect(payload); + } + } - // Here and not in timer to avoid TB for getting in between - if (payload != null) { - getConnection().getServerCommunicationHandler().startRequest(); - } + /** + * Called after a problem occurred. + * + * This method is responsible for re-sending the payload to the server (if + * not null) or re-send a heartbeat request at some point + * + * @param payload + * the payload that did not reach the server, null if the problem + * was detected by a heartbeat + */ + protected void scheduleReconnect(final JsonObject payload) { + // Here and not in timer to avoid TB for getting in between + if (payload != null) { + getConnection().getServerCommunicationHandler().startRequest(); + } - // Reconnect - new Timer() { + if (reconnectAttempt == 1) { + // Try once immediately + doReconnect(payload); + } else { + scheduledReconnect = new Timer() { @Override public void run() { - if (payload != null) { - getLogger().info( - "Re-sending last message to the server..."); - getConnection().getServerCommunicationHandler().send( - payload); - } else { - // Use heartbeat - getLogger().info( - "Trying to re-establish server connection..."); - getConnection().getHeartbeat().send(); - } + scheduledReconnect = null; + doReconnect(payload); } - }.schedule(getConfiguration().reconnectInterval); + }; + scheduledReconnect.schedule(getConfiguration().reconnectInterval); } } /** + * Re-sends the payload to the server (if not null) or re-sends a heartbeat + * request immediately + * + * @param payload + * the payload that did not reach the server, null if the problem + * was detected by a heartbeat + */ + protected void doReconnect(JsonObject payload) { + if (!connection.isApplicationRunning()) { + // This should not happen as nobody should call this if the + // application has been stopped + getLogger() + .warning( + "Trying to reconnect after application has been stopped. Giving up"); + return; + } + if (payload != null) { + getLogger().info("Re-sending last message to the server..."); + getConnection().getServerCommunicationHandler().send(payload); + } else { + // Use heartbeat + getLogger().info("Trying to re-establish server connection..."); + getConnection().getHeartbeat().send(); + } + } + + /** + * Called whenever a reconnect attempt fails to allow updating of dialog + * contents + */ + protected void updateDialog() { + reconnectDialog.setText(getDialogText(reconnectAttempt)); + } + + /** + * Called when we should give up trying to reconnect and let the user decide + * how to continue + * + */ + protected void giveUp() { + stopDialogTimer(); + if (!isDialogVisible()) { + // It SHOULD always be visible at this point, unless you have a + // really strange configuration (grace time longer than total + // reconnect time) + showDialog(); + } + reconnectDialog.setText(getDialogTextGaveUp(reconnectAttempt)); + reconnectDialog.setReconnecting(false); + + // Stopping the application stops heartbeats and push + connection.setApplicationRunning(false); + } + + /** + * Ensures the reconnect dialog does not popup some time from now + */ + private void stopDialogTimer() { + if (dialogShowTimer.isRunning()) { + dialogShowTimer.cancel(); + } + } + + /** + * Checks if the reconnect dialog is visible to the user + * + * @return true if the user can see the dialog, false otherwise + */ + protected boolean isDialogVisible() { + return reconnectDialog.isVisible(); + } + + /** + * Called when the reconnect dialog should be shown. This is typically when + * N seconds has passed since a problem with the connection has been + * detected + */ + protected void showDialog() { + reconnectDialog.setReconnecting(true); + reconnectDialog.show(connection); + + // We never want to show loading indicator and reconnect dialog at the + // same time + connection.getLoadingIndicator().hide(); + } + + /** + * Called when the reconnect dialog should be hidden. + */ + protected void hideDialog() { + reconnectDialog.hide(); + } + + /** * Gets the text to show in the reconnect dialog after giving up (reconnect * limit reached) * @@ -187,7 +394,9 @@ public class ReconnectingCommunicationProblemHandler implements @Override public void xhrInvalidContent(CommunicationProblemEvent event) { - getLogger().warning("xhrInvalidContent"); + debug("xhrInvalidContent"); + endRequest(); + String responseText = event.getResponse().getText(); /* * A servlet filter or equivalent may have intercepted the request and @@ -214,7 +423,9 @@ public class ReconnectingCommunicationProblemHandler implements @Override public void xhrInvalidStatusCode(CommunicationProblemEvent event) { - getLogger().warning("xhrInvalidStatusCode"); + debug("xhrInvalidStatusCode"); + endRequest(); + Response response = event.getResponse(); int statusCode = response.getStatusCode(); getLogger().warning("Server returned " + statusCode + " for xhr"); @@ -228,22 +439,23 @@ public class ReconnectingCommunicationProblemHandler implements // proxy between the client and the server and e.g. restart the // server // 5xx codes may or may not be temporary - handleRecoverableError(Type.MESSAGE, event.getPayload()); + handleRecoverableError(Type.XHR, event.getPayload()); } } + /** + * @since + */ + private void endRequest() { + getConnection().getServerCommunicationHandler().endRequest(); + } + protected void handleUnauthorized(CommunicationProblemEvent event) { /* * Authorization has failed (401). Could be that the session has timed * out. */ connection.showAuthenticationError(""); - endRequestAndStopApplication(); - } - - private void endRequestAndStopApplication() { - connection.getServerCommunicationHandler().endRequest(); - stopApplication(); } @@ -253,10 +465,6 @@ public class ReconnectingCommunicationProblemHandler implements connection.setApplicationRunning(false); } - /** - * @since - * @param event - */ private void handleUnrecoverableCommunicationError(String details, CommunicationProblemEvent event) { Response response = event.getResponse(); @@ -266,46 +474,38 @@ public class ReconnectingCommunicationProblemHandler implements } connection.handleCommunicationError(details, statusCode); - endRequestAndStopApplication(); + stopApplication(); } @Override public void xhrOk() { - getLogger().warning("xhrOk"); - resolveTemporaryError(Type.MESSAGE, true); + debug("xhrOk"); + if (isReconnecting()) { + resolveTemporaryError(Type.XHR); + } } - private void resolveTemporaryError(Type type, boolean success) { - getLogger().warning("resolveTemporaryError(" + type + ")"); + private void resolveTemporaryError(Type type) { + debug("resolveTemporaryError(" + type + ")"); - if (reconnectionCause == null) { - // Not trying to reconnect - return; - } - if (reconnectionCause == Type.MESSAGE && type == Type.HEARTBEAT) { - // If a heartbeat goes through while we are trying to re-send an - // XHR, we wait for the XHR to go through to avoid removing the - // reconnect dialog and then possible showing it again + if (reconnectionCause != type) { + // Waiting for some other problem to be resolved return; } reconnectionCause = null; - if (reconnectDialog.isAttached()) { - reconnectDialog.hide(); - } - - if (success && reconnectAttempt != 0) { - getLogger().info("Re-established connection to server"); - reconnectAttempt = 0; - } + hideDialog(); + getLogger().info("Re-established connection to server"); } @Override public void pushOk(PushConnection pushConnection) { - getLogger().warning("pushOk()"); - resolveTemporaryError(Type.MESSAGE, true); + debug("pushOk()"); + if (isReconnecting()) { + resolveTemporaryError(Type.PUSH); + } } @Override @@ -316,28 +516,37 @@ public class ReconnectingCommunicationProblemHandler implements @Override public void pushNotConnected(JsonObject payload) { - getLogger().warning("pushNotConnected()"); - handleRecoverableError(Type.MESSAGE, payload); + debug("pushNotConnected()"); + endRequest(); + handleRecoverableError(Type.PUSH, payload); } @Override public void pushReconnectPending(PushConnection pushConnection) { - getLogger().warning( - "pushReconnectPending(" + pushConnection.getTransportType() - + ")"); + debug("pushReconnectPending(" + pushConnection.getTransportType() + ")"); getLogger().info("Reopening push connection"); + if (pushConnection.isBidirectional()) { + // Lost connection for a connection which will tell us when the + // connection is available again + handleRecoverableError(Type.PUSH, null); + } else { + // Lost connection for a connection we do not necessarily know when + // it is available again (long polling behind proxy). Do nothing and + // show reconnect dialog if the user does something and the XHR + // fails + } } @Override public void pushError(PushConnection pushConnection) { - getLogger().warning("pushError()"); + debug("pushError()"); connection.handleCommunicationError("Push connection using " + pushConnection.getTransportType() + " failed!", -1); } @Override public void pushClientTimeout(PushConnection pushConnection) { - getLogger().warning("pushClientTimeout()"); + debug("pushClientTimeout()"); // TODO Reconnect, allowing client timeout to be set // https://dev.vaadin.com/ticket/18429 connection @@ -348,7 +557,7 @@ public class ReconnectingCommunicationProblemHandler implements @Override public void pushClosed(PushConnection pushConnection) { - getLogger().warning("pushClosed()"); + debug("pushClosed()"); getLogger().info("Push connection closed"); } diff --git a/client/src/com/vaadin/client/communication/ServerCommunicationHandler.java b/client/src/com/vaadin/client/communication/ServerCommunicationHandler.java index ba1e0497cc..fb7e5bb83b 100644 --- a/client/src/com/vaadin/client/communication/ServerCommunicationHandler.java +++ b/client/src/com/vaadin/client/communication/ServerCommunicationHandler.java @@ -245,8 +245,7 @@ public class ServerCommunicationHandler { } // After sendInvocationsToServer() there may be a new active // request, so we must set hasActiveRequest to false before, not after, - // the call. Active requests used to be tracked with an integer counter, - // so setting it after used to work but not with the #8505 changes. + // the call. hasActiveRequest = false; if (connection.isApplicationRunning()) { diff --git a/shared/src/com/vaadin/shared/ui/ui/UIState.java b/shared/src/com/vaadin/shared/ui/ui/UIState.java index 1a2e1e4f46..0e21324cc2 100644 --- a/shared/src/com/vaadin/shared/ui/ui/UIState.java +++ b/shared/src/com/vaadin/shared/ui/ui/UIState.java @@ -130,6 +130,7 @@ public class UIState extends TabIndexState { public String dialogTextGaveUp = "Server connection lost."; public int reconnectAttempts = 10000; public int reconnectInterval = 5000; + public int dialogGracePeriod = 1000; } public static class LocaleServiceState implements Serializable { diff --git a/uitest/src/com/vaadin/tests/application/CommErrorEmulatorUI.java b/uitest/src/com/vaadin/tests/application/CommErrorEmulatorUI.java index 6b1e13cd5c..9649b346b6 100644 --- a/uitest/src/com/vaadin/tests/application/CommErrorEmulatorUI.java +++ b/uitest/src/com/vaadin/tests/application/CommErrorEmulatorUI.java @@ -185,12 +185,26 @@ public class CommErrorEmulatorUI extends AbstractTestUIWithLog { } }); + final TextField reconnectDialogGracePeriod = new TextField( + "Reconnect dialog grace period (ms)"); + reconnectDialogGracePeriod.setConverter(Integer.class); + reconnectDialogGracePeriod + .setConvertedValue(getState().reconnectDialog.dialogGracePeriod); + reconnectDialogGracePeriod + .addValueChangeListener(new ValueChangeListener() { + @Override + public void valueChange(ValueChangeEvent event) { + getState().reconnectDialog.dialogGracePeriod = (Integer) reconnectDialogGracePeriod + .getConvertedValue(); + } + }); + VerticalLayout vl = new VerticalLayout(); vl.setMargin(true); vl.setSpacing(true); p.setContent(vl); vl.addComponents(reconnectDialogMessage, reconnectDialogGaveUpMessage, - reconnectDialogReconnectAttempts, + reconnectDialogGracePeriod, reconnectDialogReconnectAttempts, reconnectDialogReconnectInterval); return p; } |