]> source.dussan.org Git - vaadin-framework.git/commitdiff
Merge communication problem handlers (#11733,#17075)
authorArtur Signell <artur@vaadin.com>
Sat, 25 Apr 2015 12:58:27 +0000 (15:58 +0300)
committerArtur Signell <artur@vaadin.com>
Mon, 13 Jul 2015 14:19:09 +0000 (17:19 +0300)
Change-Id: I91d3d73cb10c10a9306b67e6d99e4405f56bc275

client/src/com/vaadin/client/communication/CommunicationProblemHandler.java
client/src/com/vaadin/client/communication/DefaultCommunicationProblemHandler.java [deleted file]
client/src/com/vaadin/client/communication/Heartbeat.java
client/src/com/vaadin/client/communication/ReconnectingCommunicationProblemHandler.java

index 3d3921ee10a29d1d97744cc240372f1813640029..d47846060e6e76fc480363516c5ca3336af70593 100644 (file)
@@ -51,10 +51,8 @@ public interface CommunicationProblemHandler {
      *            The heartbeat request
      * @param exception
      *            The exception which occurred
-     * @return true if a new heartbeat should be sent, false if no further
-     *         heartbeats should be sent
      */
-    boolean heartbeatException(Request request, Throwable exception);
+    void heartbeatException(Request request, Throwable exception);
 
     /**
      * Called when a heartbeat request returns a status code other than OK (200)
@@ -63,10 +61,8 @@ public interface CommunicationProblemHandler {
      *            The heartbeat request
      * @param response
      *            The heartbeat response
-     * @return true if a new heartbeat should be sent, false if no further
-     *         heartbeats should be sent
      */
-    boolean heartbeatInvalidStatusCode(Request request, Response response);
+    void heartbeatInvalidStatusCode(Request request, Response response);
 
     /**
      * Called when a {@link Heartbeat} request succeeds
diff --git a/client/src/com/vaadin/client/communication/DefaultCommunicationProblemHandler.java b/client/src/com/vaadin/client/communication/DefaultCommunicationProblemHandler.java
deleted file mode 100644 (file)
index c2f25cf..0000000
+++ /dev/null
@@ -1,263 +0,0 @@
-/*
- * 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;
-
-import elemental.json.JsonObject;
-
-/**
- * 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;
-    }
-
-    protected ApplicationConnection getConnection() {
-        return 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);
-        }
-
-    }
-
-    @Override
-    public void pushInvalidContent(PushConnection pushConnection, String message) {
-        // Do nothing for now. Should likely do the same as xhrInvalidContent
-    }
-
-    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) {
-    }
-
-    @Override
-    public void pushNotConnected(JsonObject payload) {
-    }
-}
index eb39622a1adf233aa75bd745c07f4705d3cb841c..3b6c9dce6d09826e739f16503e73c46b49a726fd 100644 (file)
@@ -95,28 +95,25 @@ public class Heartbeat {
             public void onResponseReceived(Request request, Response response) {
                 int status = response.getStatusCode();
 
-                boolean reschedule = true;
                 if (status == Response.SC_OK) {
                     connection.getCommunicationProblemHandler().heartbeatOk();
                 } else {
-                    reschedule = connection.getCommunicationProblemHandler()
+                    // Handler should stop the application if heartbeat should
+                    // no longer be sent
+                    connection.getCommunicationProblemHandler()
                             .heartbeatInvalidStatusCode(request, response);
                 }
 
-                if (reschedule) {
-                    schedule();
-                }
+                schedule();
             }
 
             @Override
             public void onError(Request request, Throwable exception) {
-                boolean reschedule = connection
-                        .getCommunicationProblemHandler().heartbeatException(
-                                request, exception);
-
-                if (reschedule) {
-                    schedule();
-                }
+                // Handler should stop the application if heartbeat should no
+                // longer be sent
+                connection.getCommunicationProblemHandler().heartbeatException(
+                        request, exception);
+                schedule();
             }
         };
 
index efb583ca57afc561c7bd6dc40e4cb51e9b7676fe..558cce0252f1d4241a723c221f297b4d9a446250 100644 (file)
  */
 package com.vaadin.client.communication;
 
+import java.util.logging.Logger;
+
 import com.google.gwt.core.shared.GWT;
 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.google.gwt.user.client.ui.PopupPanel.PositionCallback;
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.WidgetUtil;
 
 import elemental.json.JsonObject;
 
-//FIXME This is just a test and should be merged with DCPH
-public class ReconnectingCommunicationProblemHandler extends
-        DefaultCommunicationProblemHandler {
+/**
+ * Default implementation of the communication problem handler.
+ * <p>
+ * Handles temporary errors by showing a reconnect dialog to the user while
+ * trying to re-establish the connection to the server and re-send the pending
+ * message.
+ * <p>
+ * Handles permanent errors by showing a critical system notification to the
+ * user
+ * 
+ * @since 7.6
+ * @author Vaadin Ltd
+ */
+public class ReconnectingCommunicationProblemHandler implements
+        CommunicationProblemHandler {
+    private ApplicationConnection connection;
+    private ReconnectDialog reconnectDialog = GWT.create(ReconnectDialog.class);
+    private int reconnectAttempt = 0;
+    private Type reconnectionCause = null;
 
     private enum Type {
         HEARTBEAT, MESSAGE
     }
 
-    ReconnectDialog reconnectDialog = GWT.create(ReconnectDialog.class);
-    int reconnectAttempt = 0;
-    private Type reconnectionCause = null;
+    @Override
+    public void setConnection(ApplicationConnection connection) {
+        this.connection = connection;
+    };
+
+    private static Logger getLogger() {
+        return Logger.getLogger(ReconnectingCommunicationProblemHandler.class
+                .getName());
+    }
+
+    /**
+     * Returns the connection this handler is connected to
+     * 
+     * @return the connection for this handler
+     */
+    protected ApplicationConnection getConnection() {
+        return connection;
+    }
 
     @Override
     public void xhrException(CommunicationProblemEvent event) {
         getLogger().warning("xhrException");
-        handleTemporaryError(Type.MESSAGE, event.getPayload());
+        handleRecoverableError(Type.MESSAGE, event.getPayload());
     }
 
     @Override
-    public boolean heartbeatException(Request request, Throwable exception) {
-        handleTemporaryError(Type.HEARTBEAT, null);
-        return true;
+    public void heartbeatException(Request request, Throwable exception) {
+        getLogger().severe("Heartbeat exception: " + exception.getMessage());
+        handleRecoverableError(Type.HEARTBEAT, null);
     }
 
     @Override
-    public boolean heartbeatInvalidStatusCode(Request request, Response response) {
+    public void heartbeatInvalidStatusCode(Request request, Response response) {
+        int statusCode = response.getStatusCode();
+        getLogger().warning("Heartbeat request returned " + statusCode);
+
         if (response.getStatusCode() == Response.SC_GONE) {
             // Session expired
-            resolveTemporaryError(Type.HEARTBEAT, false);
-            return super.heartbeatInvalidStatusCode(request, response);
+            getConnection().showSessionExpiredError(null);
+            stopApplication();
+        } else {
+            handleRecoverableError(Type.HEARTBEAT, null);
         }
-
-        handleTemporaryError(Type.HEARTBEAT, null);
-        return true;
     }
 
     @Override
     public void heartbeatOk() {
+        getLogger().warning("heartbeatOk");
         resolveTemporaryError(Type.HEARTBEAT, true);
     }
 
-    private void handleTemporaryError(Type type, final JsonObject payload) {
+    protected void handleRecoverableError(Type type, final JsonObject payload) {
         getLogger().warning("handleTemporaryError(" + type + ")");
 
         reconnectAttempt++;
         reconnectionCause = type;
         if (!reconnectDialog.isAttached()) {
-            // FIXME
             reconnectDialog.setStyleName("active", true);
             reconnectDialog.setOwner(getConnection().getUIConnector()
                     .getWidget());
             reconnectDialog.setPopupPositionAndShow(new PositionCallback() {
                 @Override
                 public void setPosition(int offsetWidth, int offsetHeight) {
-                    // FIXME
                     reconnectDialog.setPopupPosition(0, 0);
                 }
             });
@@ -87,10 +125,8 @@ public class ReconnectingCommunicationProblemHandler extends
         }
 
         if (reconnectAttempt >= getMaxReconnectAttempts()) {
-            // FIXME Remove
             reconnectDialog.setText("Server connection lost. Gave up after "
                     + reconnectAttempt + " attempts.");
-            // FIXME
             reconnectDialog.setStyleName("active", false);
 
             getConnection().setApplicationRunning(false);
@@ -102,7 +138,6 @@ public class ReconnectingCommunicationProblemHandler extends
 
             // Here and not in timer to avoid TB for getting in between
             if (payload != null) {
-                // FIXME: Not like this
                 getConnection().getServerCommunicationHandler().startRequest();
             }
 
@@ -147,16 +182,86 @@ public class ReconnectingCommunicationProblemHandler extends
     @Override
     public void xhrInvalidContent(CommunicationProblemEvent event) {
         getLogger().warning("xhrInvalidContent");
-        super.xhrInvalidContent(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);
+        }
+
+    }
+
+    @Override
+    public void pushInvalidContent(PushConnection pushConnection, String message) {
+        // Do nothing for now. Should likely do the same as xhrInvalidContent
+    }
 
     @Override
     public void xhrInvalidStatusCode(CommunicationProblemEvent event) {
-        getLogger().info(
-                "Server returned " + event.getResponse().getStatusCode()
-                        + " for xhr request");
         getLogger().warning("xhrInvalidStatusCode");
-        handleTemporaryError(Type.MESSAGE, event.getPayload());
+        Response response = event.getResponse();
+        int statusCode = response.getStatusCode();
+        getLogger().warning("Server returned " + statusCode + " for xhr");
+
+        if (statusCode == 401) {
+            // Authentication/authorization failed, no need to re-try
+            handleUnauthorized(event);
+            return;
+        } else {
+            // 404, 408 and other 4xx codes CAN be temporary when you have a
+            // 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());
+        }
+    }
+
+    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();
+    }
+
+    private void stopApplication() {
+        // Consider application not running any more and prevent all
+        // future requests
+        connection.setApplicationRunning(false);
+    }
+
+    /**
+     * @since
+     * @param 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
@@ -193,13 +298,52 @@ public class ReconnectingCommunicationProblemHandler extends
 
     @Override
     public void pushOk(PushConnection pushConnection) {
-        super.pushOk(pushConnection);
+        getLogger().warning("pushOk()");
         resolveTemporaryError(Type.MESSAGE, true);
     }
 
+    @Override
+    public void pushScriptLoadError(String resourceUrl) {
+        connection.handleCommunicationError(resourceUrl
+                + " could not be loaded. Push will not work.", 0);
+    }
+
     @Override
     public void pushNotConnected(JsonObject payload) {
-        super.pushNotConnected(payload);
-        handleTemporaryError(Type.MESSAGE, payload);
+        getLogger().warning("pushNotConnected()");
+        handleRecoverableError(Type.MESSAGE, payload);
+    }
+
+    @Override
+    public void pushReconnectPending(PushConnection pushConnection) {
+        getLogger().warning(
+                "pushReconnectPending(" + pushConnection.getTransportType()
+                        + ")");
+        getLogger().info("Reopening push connection");
+    }
+
+    @Override
+    public void pushError(PushConnection pushConnection) {
+        getLogger().warning("pushError()");
+        connection.handleCommunicationError("Push connection using "
+                + pushConnection.getTransportType() + " failed!", -1);
     }
+
+    @Override
+    public void pushClientTimeout(PushConnection pushConnection) {
+        getLogger().warning("pushClientTimeout()");
+        // TODO Reconnect, allowing client timeout to be set
+        // https://dev.vaadin.com/ticket/18429
+        connection
+                .handleCommunicationError(
+                        "Client unexpectedly disconnected. Ensure client timeout is disabled.",
+                        -1);
+    }
+
+    @Override
+    public void pushClosed(PushConnection pushConnection) {
+        getLogger().warning("pushClosed()");
+        getLogger().info("Push connection closed");
+    }
+
 }