]> source.dussan.org Git - vaadin-framework.git/commitdiff
Initial version of the reconnect dialog and a problem handler which uses it (#11733)
authorArtur Signell <artur@vaadin.com>
Tue, 21 Apr 2015 14:37:22 +0000 (17:37 +0300)
committerArtur Signell <artur@vaadin.com>
Mon, 13 Jul 2015 14:19:09 +0000 (17:19 +0300)
Limitations
* Does not take Push into account
* Hard coded parameters
* Only theme for Valo

Change-Id: Iddb12d20391bcd30dc7289b7ea694ac3fbbd116d

WebContent/VAADIN/themes/valo/shared/_global.scss
WebContent/VAADIN/themes/valo/shared/_reconnect-dialog.scss [new file with mode: 0644]
client/src/com/vaadin/client/ApplicationConnection.java
client/src/com/vaadin/client/communication/DefaultCommunicationProblemHandler.java
client/src/com/vaadin/client/communication/ReconnectDialog.java [new file with mode: 0644]
client/src/com/vaadin/client/communication/ReconnectDialog.ui.xml [new file with mode: 0644]
client/src/com/vaadin/client/communication/ReconnectingCommunicationProblemHandler.java [new file with mode: 0644]
uitest/src/com/vaadin/tests/requesthandlers/CommunicationError.java

index b4e85641195b49a43f21fad67ec7c2fb47871feb..39b5a4e7d9a139f6d2b47df81e84af037dd5041b 100644 (file)
@@ -2,6 +2,7 @@
 @import "contextmenu";
 @import "overlay";
 @import "tooltip";
+@import "reconnect-dialog";
 
 
 /*
@@ -374,6 +375,7 @@ $valo-shared-pathPrefix: null;
 
   @include valo-contextmenu;
 
+  @include valo-reconnect-dialog;
 }
 
 
diff --git a/WebContent/VAADIN/themes/valo/shared/_reconnect-dialog.scss b/WebContent/VAADIN/themes/valo/shared/_reconnect-dialog.scss
new file mode 100644 (file)
index 0000000..94f5f8b
--- /dev/null
@@ -0,0 +1,18 @@
+@mixin valo-reconnect-dialog {
+  .v-reconnect-dialog {
+    color: white;
+    @include valo-notification-bar-style;
+    @include valo-notification-system-style;
+    text-align: center;
+    .spinner {
+      @include valo-spinner;
+      display: inline-block;
+      margin-top: 10px;
+      visibility: hidden;
+    }
+
+    &.active .spinner {
+      visibility: visible;
+    }
+  }
+}
\ No newline at end of file
index b3bcbbb3ab003de114dfc3729aff09a228b220c0..65afa9b08bba9fc3567d4b807697f0bafc7b08be 100644 (file)
@@ -46,8 +46,8 @@ 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.ReconnectingCommunicationProblemHandler;
 import com.vaadin.client.communication.RpcManager;
 import com.vaadin.client.communication.ServerCommunicationHandler;
 import com.vaadin.client.communication.ServerMessageHandler;
@@ -366,7 +366,7 @@ public class ApplicationConnection implements HasHandlers {
         serverRpcQueue = GWT.create(ServerRpcQueue.class);
         serverRpcQueue.setConnection(this);
         communicationProblemHandler = GWT
-                .create(DefaultCommunicationProblemHandler.class);
+                .create(ReconnectingCommunicationProblemHandler.class);
         communicationProblemHandler.setConnection(this);
         serverMessageHandler = GWT.create(ServerMessageHandler.class);
         serverMessageHandler.setConnection(this);
index 8b97b0a4c749e6b254eabc509b8b963f4a52beb5..196e1d56ea38040fc6b53ad1fead1a7e8f1bf190 100644 (file)
@@ -44,6 +44,10 @@ public class DefaultCommunicationProblemHandler implements
         this.connection = connection;
     }
 
+    protected ApplicationConnection getConnection() {
+        return connection;
+    }
+
     public static Logger getLogger() {
         return Logger.getLogger(DefaultCommunicationProblemHandler.class
                 .getName());
diff --git a/client/src/com/vaadin/client/communication/ReconnectDialog.java b/client/src/com/vaadin/client/communication/ReconnectDialog.java
new file mode 100644 (file)
index 0000000..b69f51c
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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.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;
+
+/**
+ * 
+ * 
+ * @since 7.6
+ * @author Vaadin Ltd
+ */
+public class ReconnectDialog extends VOverlay {
+    interface MyUiBinder extends UiBinder<HTMLPanel, ReconnectDialog> {
+    }
+
+    private static MyUiBinder uiBinder = GWT.create(MyUiBinder.class);
+
+    @UiField
+    public Label label;
+
+    public ReconnectDialog() {
+        super(false, true);
+        addStyleName("v-reconnect-dialog");
+        setWidget(uiBinder.createAndBindUi(this));
+    }
+
+    public void setText(String text) {
+        label.setText(text);
+    }
+}
diff --git a/client/src/com/vaadin/client/communication/ReconnectDialog.ui.xml b/client/src/com/vaadin/client/communication/ReconnectDialog.ui.xml
new file mode 100644 (file)
index 0000000..885588f
--- /dev/null
@@ -0,0 +1,12 @@
+<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'
+       xmlns:g='urn:import:com.google.gwt.user.client.ui'>
+
+       <g:HTMLPanel>
+
+               <g:Label ui:field="label" styleName="text"
+                       text="Server connection lost, trying to reconnect..." />
+               <div class="spinner" />
+       </g:HTMLPanel>
+
+</ui:UiBinder>
+
diff --git a/client/src/com/vaadin/client/communication/ReconnectingCommunicationProblemHandler.java b/client/src/com/vaadin/client/communication/ReconnectingCommunicationProblemHandler.java
new file mode 100644 (file)
index 0000000..bcdf12f
--- /dev/null
@@ -0,0 +1,181 @@
+/*
+ * 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.http.client.Request;
+import com.google.gwt.http.client.Response;
+import com.google.gwt.user.client.Timer;
+import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
+
+import elemental.json.JsonObject;
+
+//FIXME This is just a test and should be merged with DCPH
+public class ReconnectingCommunicationProblemHandler extends
+        DefaultCommunicationProblemHandler {
+
+    private enum Type {
+        HEARTBEAT, XHR;
+    }
+
+    ReconnectDialog reconnectDialog = GWT.create(ReconnectDialog.class);
+    int reconnectAttempt = 0;
+    private Type reconnectionCause = null;;
+
+    @Override
+    public void xhrException(CommunicationProblemEvent event) {
+        handleTemporaryError(Type.XHR, event.getPayload());
+    }
+
+    @Override
+    public boolean heartbeatException(Request request, Throwable exception) {
+        handleTemporaryError(Type.HEARTBEAT, null);
+        return true;
+    }
+
+    @Override
+    public boolean heartbeatInvalidStatusCode(Request request, Response response) {
+        if (response.getStatusCode() == Response.SC_GONE) {
+            // Session expired
+            resolveTemporaryError(Type.HEARTBEAT, false);
+            return super.heartbeatInvalidStatusCode(request, response);
+        }
+
+        handleTemporaryError(Type.HEARTBEAT, null);
+        return true;
+    }
+
+    @Override
+    public void heartbeatOk() {
+        resolveTemporaryError(Type.HEARTBEAT, true);
+    }
+
+    private void handleTemporaryError(Type type, final JsonObject payload) {
+        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);
+                }
+            });
+        }
+        if (payload != null) {
+            getConnection().getServerCommunicationHandler().endRequest();
+        }
+
+        if (reconnectAttempt >= getMaxReconnectAttempts()) {
+            // FIXME Remove
+            reconnectDialog.setText("Server connection lost. Gave up after "
+                    + reconnectAttempt + " attempts.");
+            // FIXME
+            reconnectDialog.setStyleName("active", false);
+
+            getConnection().setApplicationRunning(false);
+
+        } else {
+            reconnectDialog
+                    .setText("Server connection lost, trying to reconnect... Attempt "
+                            + reconnectAttempt);
+
+            // Here and not in timer to avoid TB for getting in between
+            if (payload != null) {
+                // FIXME: Not like this
+                getConnection().getServerCommunicationHandler().startRequest();
+            }
+
+            // Reconnect
+            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();
+                    }
+                }
+            }.schedule(getReconnectInterval());
+        }
+    }
+
+    /**
+     * @since
+     * @return
+     */
+    private int getMaxReconnectAttempts() {
+        // FIXME Parameter
+        return 15;
+    }
+
+    /**
+     * @since
+     * @return
+     */
+    private int getReconnectInterval() {
+        // FIXME Parameter
+        return 5000;
+    }
+
+    @Override
+    public void xhrInvalidContent(CommunicationProblemEvent event) {
+        super.xhrInvalidContent(event);
+    };
+
+    @Override
+    public void xhrInvalidStatusCode(CommunicationProblemEvent event) {
+        handleTemporaryError(Type.XHR, event.getPayload());
+    }
+
+    @Override
+    public void xhrOk() {
+        resolveTemporaryError(Type.XHR, true);
+    }
+
+    private void resolveTemporaryError(Type cause, boolean success) {
+        if (reconnectionCause == null) {
+            // Not trying to reconnect
+            return;
+        }
+        if (reconnectionCause != cause) {
+            // If a heartbeat goes through while we are trying to re-send an
+            // XHR, we wait for the XHR to go through
+            return;
+        }
+
+        reconnectionCause = null;
+        if (reconnectDialog.isAttached()) {
+            reconnectDialog.hide();
+        }
+
+        if (success && reconnectAttempt != 0) {
+            getLogger().info("Re-established connection to server");
+            reconnectAttempt = 0;
+        }
+
+    }
+}
index 31ec7658eeacedd01ae572d5c531e13133d55b37..26f3dff1a2867fb3df81d564914e3ee3a2f78156 100644 (file)
@@ -15,6 +15,9 @@
  */
 package com.vaadin.tests.requesthandlers;
 
+import java.io.IOException;
+import java.io.PrintWriter;
+
 import com.vaadin.launcher.ApplicationRunnerServlet;
 import com.vaadin.server.CustomizedSystemMessages;
 import com.vaadin.server.SystemMessages;
@@ -69,7 +72,17 @@ public class CommunicationError extends UIProvider {
 
                         @Override
                         public void buttonClick(ClickEvent event) {
-                            VaadinService.getCurrentResponse().setStatus(400);
+                            try {
+                                // An unparseable response will cause
+                                // communication error
+                                PrintWriter writer = VaadinService
+                                        .getCurrentResponse().getWriter();
+                                writer.write("for(;;)[{FOOBAR}]");
+                                writer.flush();
+                                writer.close();
+                            } catch (IOException e) {
+                                e.printStackTrace();
+                            }
                         }
                     });
             addComponent(button);