summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorArtur Signell <artur@vaadin.com>2015-04-25 15:49:35 +0300
committerArtur Signell <artur@vaadin.com>2015-07-13 17:19:08 +0300
commit132ca1e1b68bf813364a5f87e9afa5997804c07c (patch)
tree4a7320a380ecae83610c2e18431f0f960404f900 /client
parentd93527f25759e3a9aeb80a0c6684657a193e6a9e (diff)
downloadvaadin-framework-132ca1e1b68bf813364a5f87e9afa5997804c07c.tar.gz
vaadin-framework-132ca1e1b68bf813364a5f87e9afa5997804c07c.zip
Extract CommunicationProblemHandler methods to an interface (#11733)
Change-Id: Id44eed32a91721a67859d8daedefd3c7a17d61dc
Diffstat (limited to 'client')
-rw-r--r--client/src/com/vaadin/client/ApplicationConnection.java3
-rw-r--r--client/src/com/vaadin/client/communication/CommunicationProblemHandler.java351
-rw-r--r--client/src/com/vaadin/client/communication/DefaultCommunicationProblemHandler.java248
3 files changed, 346 insertions, 256 deletions
diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java
index b3bf626de7..b3bcbbb3ab 100644
--- a/client/src/com/vaadin/client/ApplicationConnection.java
+++ b/client/src/com/vaadin/client/ApplicationConnection.java
@@ -46,6 +46,7 @@ import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent;
import com.vaadin.client.ResourceLoader.ResourceLoadEvent;
import com.vaadin.client.ResourceLoader.ResourceLoadListener;
import com.vaadin.client.communication.CommunicationProblemHandler;
+import com.vaadin.client.communication.DefaultCommunicationProblemHandler;
import com.vaadin.client.communication.Heartbeat;
import com.vaadin.client.communication.RpcManager;
import com.vaadin.client.communication.ServerCommunicationHandler;
@@ -365,7 +366,7 @@ public class ApplicationConnection implements HasHandlers {
serverRpcQueue = GWT.create(ServerRpcQueue.class);
serverRpcQueue.setConnection(this);
communicationProblemHandler = GWT
- .create(CommunicationProblemHandler.class);
+ .create(DefaultCommunicationProblemHandler.class);
communicationProblemHandler.setConnection(this);
serverMessageHandler = GWT.create(ServerMessageHandler.class);
serverMessageHandler.setConnection(this);
diff --git a/client/src/com/vaadin/client/communication/CommunicationProblemHandler.java b/client/src/com/vaadin/client/communication/CommunicationProblemHandler.java
index 49650f7efd..82bd4403b9 100644
--- a/client/src/com/vaadin/client/communication/CommunicationProblemHandler.java
+++ b/client/src/com/vaadin/client/communication/CommunicationProblemHandler.java
@@ -15,316 +15,157 @@
*/
package com.vaadin.client.communication;
-import java.util.logging.Logger;
-
import com.google.gwt.http.client.Request;
import com.google.gwt.http.client.Response;
-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.WidgetUtil;
/**
- * TODO
+ * Interface for handling problems which occur during communications with the
+ * server.
+ *
+ * The handler is responsible for handling any problem in XHR, heartbeat and
+ * push connections in a way it sees fit. The default implementation used is
+ * {@link DefaultCommunicationProblemHandler}, which considers all problems
+ * terminal
*
- * @since
+ * @since 7.6
* @author Vaadin Ltd
*/
-public class CommunicationProblemHandler {
-
- private ApplicationConnection connection;
+public interface CommunicationProblemHandler {
/**
- * Sets the application connection this queue is connected to
+ * Sets the application connection this handler is connected to. Called
+ * internally by the framework.
*
* @param connection
- * the application connection this queue is connected to
- */
- public void setConnection(ApplicationConnection connection) {
- this.connection = connection;
- }
-
- public static Logger getLogger() {
- return Logger.getLogger(CommunicationProblemHandler.class.getName());
- }
-
- /**
- * @param payload
- * @param communicationProblemEvent
- */
- public void xhrException(CommunicationProblemEvent event) {
- handleUnrecoverableCommunicationError(
- event.getException().getMessage(), event);
-
- }
-
- /**
- * @param event
- * @param retry
- */
- public void xhrInvalidStatusCode(final CommunicationProblemEvent event) {
- Response response = event.getResponse();
- int statusCode = response.getStatusCode();
- if (statusCode == 0) {
- handleNoConnection(event);
- } else if (statusCode == 401) {
- handleAuthorizationFailed(event);
- } else if (statusCode == 503) {
- handleServiceUnavailable(event);
- } else if ((statusCode / 100) == 4) {
- // Handle all 4xx errors the same way as (they are
- // all permanent errors)
- String msg = "UIDL could not be read from server."
- + " Check servlets mappings. Error code: " + statusCode;
- handleUnrecoverableCommunicationError(msg, event);
- } else if ((statusCode / 100) == 5) {
- // Something's wrong on the server, there's nothing the
- // client can do except maybe try again.
- String msg = "Server error. Error code: " + statusCode;
- handleUnrecoverableCommunicationError(msg, event);
- return;
- }
-
- }
-
- /**
- * @since
- * @param event
- */
- private void handleServiceUnavailable(final CommunicationProblemEvent event) {
- /*
- * We'll assume msec instead of the usual seconds. If there's no
- * Retry-After header, handle the error like a 500, as per RFC 2616
- * section 10.5.4.
- */
- String delay = event.getResponse().getHeader("Retry-After");
- if (delay != null) {
- getLogger().warning("503, retrying in " + delay + "msec");
- (new Timer() {
- @Override
- public void run() {
- // send does not call startRequest so we do
- // not call endRequest before it
- getServerCommunicationHandler().send(event.getPayload());
- }
- }).schedule(Integer.parseInt(delay));
- return;
- } else {
- String msg = "Server error. Error code: "
- + event.getResponse().getStatusCode();
- handleUnrecoverableCommunicationError(msg, event);
- }
-
- }
-
- /**
- * @since
- * @param event
- */
- private void handleAuthorizationFailed(CommunicationProblemEvent event) {
- /*
- * Authorization has failed (401). Could be that the session has timed
- * out and the container is redirecting to a login page.
- */
- connection.showAuthenticationError("");
- endRequestAndStopApplication();
- }
-
- /**
- * @since
- */
- private void endRequestAndStopApplication() {
- getServerCommunicationHandler().endRequest();
-
- // Consider application not running any more and prevent all
- // future requests
- connection.setApplicationRunning(false);
- }
-
- /**
- * @since
- * @param event
- * @param retry
+ * the application connection this handler is connected to
*/
- private void handleNoConnection(final CommunicationProblemEvent event) {
- handleUnrecoverableCommunicationError(
- "Invalid status code 0 (server down?)", event);
- }
-
- private void handleUnrecoverableCommunicationError(String details,
- CommunicationProblemEvent event) {
- Response response = event.getResponse();
- int statusCode = -1;
- if (response != null) {
- statusCode = response.getStatusCode();
- }
- connection.handleCommunicationError(details, statusCode);
-
- endRequestAndStopApplication();
- }
+ void setConnection(ApplicationConnection connection);
/**
- * @since
- * @param event
- */
- public void xhrInvalidContent(CommunicationProblemEvent event) {
- String responseText = event.getResponse().getText();
- /*
- * A servlet filter or equivalent may have intercepted the request and
- * served non-UIDL content (for instance, a login page if the session
- * has expired.) If the response contains a magic substring, do a
- * synchronous refresh. See #8241.
- */
- MatchResult refreshToken = RegExp.compile(
- ApplicationConnection.UIDL_REFRESH_TOKEN
- + "(:\\s*(.*?))?(\\s|$)").exec(responseText);
- if (refreshToken != null) {
- WidgetUtil.redirect(refreshToken.getGroup(2));
- } else {
- handleUnrecoverableCommunicationError(
- "Invalid JSON response from server: " + responseText, event);
- }
-
- }
-
- private ServerCommunicationHandler getServerCommunicationHandler() {
- return connection.getServerCommunicationHandler();
- }
-
- /**
- * Called when a heartbeat request returns a status code other than 200
+ * Called when an exception occurs during a {@link Heartbeat} request
*
* @param request
* The heartbeat request
- * @param response
- * The heartbeat response
+ * @param exception
+ * The exception which occurred
* @return true if a new heartbeat should be sent, false if no further
* heartbeats should be sent
*/
- public boolean heartbeatInvalidStatusCode(Request request, Response response) {
- int status = response.getStatusCode();
- int interval = connection.getHeartbeat().getInterval();
- if (status == 0) {
- getLogger().warning(
- "Failed sending heartbeat, server is unreachable, retrying in "
- + interval + "secs.");
- } else if (status == Response.SC_GONE) {
- // FIXME Stop application?
- connection.showSessionExpiredError(null);
- // If session is expired break the loop
- return false;
- } else if (status >= 500) {
- getLogger().warning(
- "Failed sending heartbeat, see server logs, retrying in "
- + interval + "secs.");
- } else {
- getLogger()
- .warning(
- "Failed sending heartbeat to server. Error code: "
- + status);
- }
-
- return true;
- }
+ boolean heartbeatException(Request request, Throwable exception);
/**
- * Called when an exception occurs during a heartbeat request
+ * Called when a heartbeat request returns a status code other than OK (200)
*
* @param request
* The heartbeat request
- * @param exception
- * The exception which occurred
+ * @param response
+ * The heartbeat response
* @return true if a new heartbeat should be sent, false if no further
* heartbeats should be sent
*/
- public boolean heartbeatException(Request request, Throwable exception) {
- getLogger().severe(
- "Exception sending heartbeat: " + exception.getMessage());
- return true;
- }
+ boolean heartbeatInvalidStatusCode(Request request, Response response);
/**
- * @since
- * @param response
+ * Called when a {@link Heartbeat} request succeeds
*/
- public void pushError(PushConnection pushConnection) {
- /*
- * Push error indicates a fatal error we cannot (or should not) recover
- * from by reconnecting or similar.
- *
- * Atmosphere calls onError
- *
- * 1. if maxReconnectAttemps is reached
- *
- * 2. if neither the transport nor the fallback transport can be used
- *
- * (also for server response codes != 200 if reconnectOnServerError is
- * set to false but we do not set that to false)
- */
- connection.handleCommunicationError("Push connection using "
- + pushConnection.getTransportType() + " failed!", -1);
- }
+ void heartbeatOk();
/**
- * @since
- * @param response
+ * Called when the push connection to the server is closed. This might
+ * result in the push connection trying a fallback connection method, trying
+ * to reconnect to the server or might just be an indication that the
+ * connection was intentionally closed ("unsubscribe"),
+ *
+ * @param pushConnection
+ * The push connection which was closed
*/
- public void pushClientTimeout(PushConnection pushConnection) {
- connection
- .handleCommunicationError(
- "Client unexpectedly disconnected. Ensure client timeout is disabled.",
- -1);
-
- }
+ void pushClosed(PushConnection pushConnection);
/**
- * @since
- * @param resourceUrl
+ * Called when a client side timeout occurs before a push connection to the
+ * server completes.
+ *
+ * The client side timeout causes a disconnection of the push connection and
+ * no reconnect will be attempted after this method is called,
+ *
+ * @param pushConnection
+ * The push connection which timed out
*/
- public void pushScriptLoadError(String resourceUrl) {
- connection.handleCommunicationError(resourceUrl
- + " could not be loaded. Push will not work.", 0);
-
- }
+ void pushClientTimeout(PushConnection pushConnection);
/**
- * @since
+ * Called when a fatal error fatal error occurs in the push connection.
+ *
+ * The push connection will not try to recover from this situation itself
+ * and typically the problem handler should not try to do automatic recovery
+ * either. The cause can be e.g. maximum number of reconnection attempts
+ * have been reached, neither the selected transport nor the fallback
+ * transport can be used or similar.
+ *
+ * @param pushConnection
+ * The push connection where the error occurred
*/
- public void heartbeatOk() {
- getLogger().fine("Heartbeat response OK");
- }
+ void pushError(PushConnection pushConnection);
/**
- * @since
+ * Called when the push connection has lost the connection to the server and
+ * will proceed to try to re-establish the connection
+ *
+ * @param pushConnection
+ * The push connection which will be reconnected
*/
- public void xhrOk() {
+ void pushReconnectPending(PushConnection pushConnection);
- }
+ /**
+ * Called when the push connection to the server has been established.
+ *
+ * @param pushConnection
+ * The push connection which was established
+ */
+ void pushOk(PushConnection pushConnection);
/**
- * @since
- * @param response
+ * Called when the required push script could not be loaded
+ *
+ * @param resourceUrl
+ * The URL which was used for loading the script
*/
- public void pushClosed(PushConnection pushConnection) {
- // TODO Auto-generated method stub
+ void pushScriptLoadError(String resourceUrl);
- }
+ /**
+ * Called when an exception occurs during an XmlHttpRequest request to the
+ * server.
+ *
+ * @param communicationProblemEvent
+ * An event containing what was being sent to the server and what
+ * exception occurred
+ */
+ void xhrException(CommunicationProblemEvent communicationProblemEvent);
/**
- * @since
- * @param response
+ * Called when invalid content (not JSON) was returned from the server as
+ * the result of an XmlHttpRequest request
+ *
+ * @param communicationProblemEvent
+ * An event containing what was being sent to the server and what
+ * was returned
*/
- public void pushReconnectPending(PushConnection pushConnection) {
- // TODO Auto-generated method stub
+ void xhrInvalidContent(CommunicationProblemEvent communicationProblemEvent);
- }
+ /**
+ * Called when invalid status code (not 200) was returned by the server as
+ * the result of an XmlHttpRequest.
+ *
+ * @param communicationProblemEvent
+ * An event containing what was being sent to the server and what
+ * was returned
+ */
+ void xhrInvalidStatusCode(CommunicationProblemEvent problemEvent);
/**
- * @since
+ * Called whenever a XmlHttpRequest to the server completes successfully
*/
- public void pushOk(PushConnection pushConnection) {
- // TODO Auto-generated method stub
+ void xhrOk();
- }
}
diff --git a/client/src/com/vaadin/client/communication/DefaultCommunicationProblemHandler.java b/client/src/com/vaadin/client/communication/DefaultCommunicationProblemHandler.java
new file mode 100644
index 0000000000..8b97b0a4c7
--- /dev/null
+++ b/client/src/com/vaadin/client/communication/DefaultCommunicationProblemHandler.java
@@ -0,0 +1,248 @@
+/*
+ * 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 java.util.logging.Logger;
+
+import com.google.gwt.http.client.Request;
+import com.google.gwt.http.client.Response;
+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.WidgetUtil;
+
+/**
+ * Default implementation of the communication problem handler.
+ *
+ * Implements error handling by assuming all problems are terminal and simply
+ * showing a notification to the user
+ *
+ * @since 7.6
+ * @author Vaadin Ltd
+ */
+public class DefaultCommunicationProblemHandler implements
+ CommunicationProblemHandler {
+
+ private ApplicationConnection connection;
+
+ @Override
+ public void setConnection(ApplicationConnection connection) {
+ this.connection = connection;
+ }
+
+ public static Logger getLogger() {
+ return Logger.getLogger(DefaultCommunicationProblemHandler.class
+ .getName());
+ }
+
+ @Override
+ public void xhrException(CommunicationProblemEvent event) {
+ handleUnrecoverableCommunicationError(
+ event.getException().getMessage(), event);
+
+ }
+
+ @Override
+ public void xhrInvalidStatusCode(final CommunicationProblemEvent event) {
+ Response response = event.getResponse();
+ int statusCode = response.getStatusCode();
+ if (statusCode == 0) {
+ handleNoConnection(event);
+ } else if (statusCode == 401) {
+ handleAuthorizationFailed(event);
+ } else if (statusCode == 503) {
+ handleServiceUnavailable(event);
+ } else if ((statusCode / 100) == 4) {
+ // Handle all 4xx errors the same way as (they are
+ // all permanent errors)
+ String msg = "UIDL could not be read from server."
+ + " Check servlets mappings. Error code: " + statusCode;
+ handleUnrecoverableCommunicationError(msg, event);
+ } else if ((statusCode / 100) == 5) {
+ // Something's wrong on the server, there's nothing the
+ // client can do except maybe try again.
+ String msg = "Server error. Error code: " + statusCode;
+ handleUnrecoverableCommunicationError(msg, event);
+ return;
+ }
+
+ }
+
+ private void handleServiceUnavailable(final CommunicationProblemEvent event) {
+ /*
+ * We'll assume msec instead of the usual seconds. If there's no
+ * Retry-After header, handle the error like a 500, as per RFC 2616
+ * section 10.5.4.
+ */
+ String delay = event.getResponse().getHeader("Retry-After");
+ if (delay != null) {
+ getLogger().warning("503, retrying in " + delay + "msec");
+ (new Timer() {
+ @Override
+ public void run() {
+ // send does not call startRequest so we do
+ // not call endRequest before it
+ getServerCommunicationHandler().send(event.getPayload());
+ }
+ }).schedule(Integer.parseInt(delay));
+ return;
+ } else {
+ String msg = "Server error. Error code: "
+ + event.getResponse().getStatusCode();
+ handleUnrecoverableCommunicationError(msg, event);
+ }
+
+ }
+
+ private void handleAuthorizationFailed(CommunicationProblemEvent event) {
+ /*
+ * Authorization has failed (401). Could be that the session has timed
+ * out and the container is redirecting to a login page.
+ */
+ connection.showAuthenticationError("");
+ endRequestAndStopApplication();
+ }
+
+ private void endRequestAndStopApplication() {
+ getServerCommunicationHandler().endRequest();
+
+ // Consider application not running any more and prevent all
+ // future requests
+ connection.setApplicationRunning(false);
+ }
+
+ private void handleNoConnection(final CommunicationProblemEvent event) {
+ handleUnrecoverableCommunicationError(
+ "Invalid status code 0 (server down?)", event);
+
+ }
+
+ private void handleUnrecoverableCommunicationError(String details,
+ CommunicationProblemEvent event) {
+ Response response = event.getResponse();
+ int statusCode = -1;
+ if (response != null) {
+ statusCode = response.getStatusCode();
+ }
+ connection.handleCommunicationError(details, statusCode);
+
+ endRequestAndStopApplication();
+ }
+
+ @Override
+ public void xhrInvalidContent(CommunicationProblemEvent event) {
+ String responseText = event.getResponse().getText();
+ /*
+ * A servlet filter or equivalent may have intercepted the request and
+ * served non-UIDL content (for instance, a login page if the session
+ * has expired.) If the response contains a magic substring, do a
+ * synchronous refresh. See #8241.
+ */
+ MatchResult refreshToken = RegExp.compile(
+ ApplicationConnection.UIDL_REFRESH_TOKEN
+ + "(:\\s*(.*?))?(\\s|$)").exec(responseText);
+ if (refreshToken != null) {
+ WidgetUtil.redirect(refreshToken.getGroup(2));
+ } else {
+ handleUnrecoverableCommunicationError(
+ "Invalid JSON response from server: " + responseText, event);
+ }
+
+ }
+
+ private ServerCommunicationHandler getServerCommunicationHandler() {
+ return connection.getServerCommunicationHandler();
+ }
+
+ @Override
+ public boolean heartbeatInvalidStatusCode(Request request, Response response) {
+ int status = response.getStatusCode();
+ int interval = connection.getHeartbeat().getInterval();
+ if (status == 0) {
+ getLogger().warning(
+ "Failed sending heartbeat, server is unreachable, retrying in "
+ + interval + "secs.");
+ } else if (status == Response.SC_GONE) {
+ // FIXME Stop application?
+ connection.showSessionExpiredError(null);
+ // If session is expired break the loop
+ return false;
+ } else if (status >= 500) {
+ getLogger().warning(
+ "Failed sending heartbeat, see server logs, retrying in "
+ + interval + "secs.");
+ } else {
+ getLogger()
+ .warning(
+ "Failed sending heartbeat to server. Error code: "
+ + status);
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean heartbeatException(Request request, Throwable exception) {
+ getLogger().severe(
+ "Exception sending heartbeat: " + exception.getMessage());
+ return true;
+ }
+
+ @Override
+ public void pushError(PushConnection pushConnection) {
+ connection.handleCommunicationError("Push connection using "
+ + pushConnection.getTransportType() + " failed!", -1);
+ }
+
+ @Override
+ public void pushClientTimeout(PushConnection pushConnection) {
+ connection
+ .handleCommunicationError(
+ "Client unexpectedly disconnected. Ensure client timeout is disabled.",
+ -1);
+
+ }
+
+ @Override
+ public void pushScriptLoadError(String resourceUrl) {
+ connection.handleCommunicationError(resourceUrl
+ + " could not be loaded. Push will not work.", 0);
+
+ }
+
+ @Override
+ public void heartbeatOk() {
+ getLogger().fine("Heartbeat response OK");
+ }
+
+ @Override
+ public void xhrOk() {
+
+ }
+
+ @Override
+ public void pushClosed(PushConnection pushConnection) {
+ }
+
+ @Override
+ public void pushReconnectPending(PushConnection pushConnection) {
+ }
+
+ @Override
+ public void pushOk(PushConnection pushConnection) {
+ }
+}