]> source.dussan.org Git - vaadin-framework.git/commitdiff
Separate heartbeat functionality from ApplicationConnection
authorArtur Signell <artur@vaadin.com>
Thu, 23 Jan 2014 21:10:21 +0000 (23:10 +0200)
committerVaadin Code Review <review@vaadin.com>
Tue, 4 Feb 2014 10:41:29 +0000 (10:41 +0000)
* Correctly cancels the timer if the interval is updated
* Listens to ApplicationStoppedEvents and disables the heartbeat (#13249)

Change-Id: I5f4f778583954a1dd22ffeb39ef3b8fa07b85a1c

client/src/com/vaadin/client/ApplicationConnection.java
client/src/com/vaadin/client/communication/Heartbeat.java [new file with mode: 0644]

index 8a3841b1734f0bb17ab811c98b006c4c8fe25fdb..c4814d7e66978a3fa66f9559e0882861df6a6eb7 100644 (file)
@@ -65,9 +65,11 @@ import com.google.gwt.user.client.Window.ClosingHandler;
 import com.google.gwt.user.client.ui.HasWidgets;
 import com.google.gwt.user.client.ui.Widget;
 import com.vaadin.client.ApplicationConfiguration.ErrorMessage;
+import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent;
 import com.vaadin.client.ResourceLoader.ResourceLoadEvent;
 import com.vaadin.client.ResourceLoader.ResourceLoadListener;
 import com.vaadin.client.communication.HasJavaScriptConnectorHelper;
+import com.vaadin.client.communication.Heartbeat;
 import com.vaadin.client.communication.JavaScriptMethodInvocation;
 import com.vaadin.client.communication.JsonDecoder;
 import com.vaadin.client.communication.JsonEncoder;
@@ -427,6 +429,8 @@ public class ApplicationConnection {
 
     private VLoadingIndicator loadingIndicator;
 
+    private Heartbeat heartbeat = GWT.create(Heartbeat.class);
+
     public static class MultiStepDuration extends Duration {
         private int previousStep = elapsedMillis();
 
@@ -489,7 +493,7 @@ public class ApplicationConnection {
 
         getLoadingIndicator().show();
 
-        scheduleHeartbeat();
+        heartbeat.init(this);
 
         Window.addWindowClosingHandler(new ClosingHandler() {
             @Override
@@ -3310,20 +3314,11 @@ public class ApplicationConnection {
      * interval elapses if the interval is a positive number. Otherwise, does
      * nothing.
      * 
-     * @see #sendHeartbeat()
-     * @see ApplicationConfiguration#getHeartbeatInterval()
+     * @deprecated as of 7.2, use {@link Heartbeat#schedule()} instead
      */
+    @Deprecated
     protected void scheduleHeartbeat() {
-        final int interval = getConfiguration().getHeartbeatInterval();
-        if (interval > 0) {
-            VConsole.log("Scheduling heartbeat in " + interval + " seconds");
-            new Timer() {
-                @Override
-                public void run() {
-                    sendHeartbeat();
-                }
-            }.schedule(interval * 1000);
-        }
+        heartbeat.schedule();
     }
 
     /**
@@ -3332,51 +3327,12 @@ public class ApplicationConnection {
      * Heartbeat requests are used to inform the server that the client-side is
      * still alive. If the client page is closed or the connection lost, the
      * server will eventually close the inactive UI.
-     * <p>
-     * <b>TODO</b>: Improved error handling, like in doUidlRequest().
      * 
-     * @see #scheduleHeartbeat()
+     * @deprecated as of 7.2, use {@link Heartbeat#send()} instead
      */
+    @Deprecated
     protected void sendHeartbeat() {
-        final String uri = addGetParameters(
-                translateVaadinUri(ApplicationConstants.APP_PROTOCOL_PREFIX
-                        + ApplicationConstants.HEARTBEAT_PATH + '/'),
-                UIConstants.UI_ID_PARAMETER + "="
-                        + getConfiguration().getUIId());
-
-        final RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, uri);
-
-        final RequestCallback callback = new RequestCallback() {
-
-            @Override
-            public void onResponseReceived(Request request, Response response) {
-                int status = response.getStatusCode();
-                if (status == Response.SC_OK) {
-                    // TODO Permit retry in some error situations
-                    VConsole.log("Heartbeat response OK");
-                    scheduleHeartbeat();
-                } else if (status == Response.SC_GONE) {
-                    showSessionExpiredError(null);
-                } else {
-                    VConsole.error("Failed sending heartbeat to server. Error code: "
-                            + status);
-                }
-            }
-
-            @Override
-            public void onError(Request request, Throwable exception) {
-                VConsole.error("Exception sending heartbeat: " + exception);
-            }
-        };
-
-        rb.setCallback(callback);
-
-        try {
-            VConsole.log("Sending heartbeat request...");
-            rb.send();
-        } catch (RequestException re) {
-            callback.onError(null, re);
-        }
+        heartbeat.send();
     }
 
     /**
diff --git a/client/src/com/vaadin/client/communication/Heartbeat.java b/client/src/com/vaadin/client/communication/Heartbeat.java
new file mode 100644 (file)
index 0000000..46c8d62
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+ * 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.logging.Logger;
+
+import com.google.gwt.http.client.Request;
+import com.google.gwt.http.client.RequestBuilder;
+import com.google.gwt.http.client.RequestCallback;
+import com.google.gwt.http.client.RequestException;
+import com.google.gwt.http.client.Response;
+import com.google.gwt.user.client.Timer;
+import com.vaadin.client.ApplicationConnection;
+import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent;
+import com.vaadin.shared.ApplicationConstants;
+import com.vaadin.shared.ui.ui.UIConstants;
+
+/**
+ * Handles sending of heartbeats to the server and reacting to the response
+ * 
+ * @since 7.2
+ * @author Vaadin Ltd
+ */
+public class Heartbeat {
+
+    private int interval = -1;
+    private Timer timer = new Timer() {
+        @Override
+        public void run() {
+            send();
+        }
+    };
+
+    private ApplicationConnection connection;
+
+    private static Logger getLogger() {
+        return Logger.getLogger(Heartbeat.class.getName());
+    }
+
+    /**
+     * Initializes the heartbeat for the given application connection
+     * 
+     * @param applicationConnection
+     *            the connection
+     */
+    public void init(ApplicationConnection applicationConnection) {
+        interval = applicationConnection.getConfiguration()
+                .getHeartbeatInterval();
+        setInterval(interval);
+        schedule();
+
+        applicationConnection.addHandler(
+                ApplicationConnection.ApplicationStoppedEvent.TYPE,
+                new ApplicationConnection.ApplicationStoppedHandler() {
+
+                    @Override
+                    public void onApplicationStopped(
+                            ApplicationStoppedEvent event) {
+                        setInterval(-1);
+                        schedule();
+                    }
+                });
+
+    }
+
+    /**
+     * Sends a heartbeat to the server
+     */
+    public void send() {
+        final String uri = ApplicationConnection.addGetParameters(
+                getConnection().translateVaadinUri(
+                        ApplicationConstants.APP_PROTOCOL_PREFIX
+                                + ApplicationConstants.HEARTBEAT_PATH + '/'),
+                UIConstants.UI_ID_PARAMETER + "="
+                        + getConnection().getConfiguration().getUIId());
+
+        final RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, uri);
+
+        final RequestCallback callback = new RequestCallback() {
+
+            @Override
+            public void onResponseReceived(Request request, Response response) {
+                int status = response.getStatusCode();
+                if (status == Response.SC_OK) {
+                    // TODO Permit retry in some error situations
+                    getLogger().fine("Heartbeat response OK");
+                    schedule();
+                } else if (status == Response.SC_GONE) {
+                    // FIXME This should really do something else like send an
+                    // event
+                    getConnection().showSessionExpiredError(null);
+                } else {
+                    getLogger().warning(
+                            "Failed sending heartbeat to server. Error code: "
+                                    + status);
+                }
+            }
+
+            @Override
+            public void onError(Request request, Throwable exception) {
+                getLogger().severe("Exception sending heartbeat: " + exception);
+            }
+        };
+
+        rb.setCallback(callback);
+
+        try {
+            getLogger().fine("Sending heartbeat request...");
+            rb.send();
+        } catch (RequestException re) {
+            callback.onError(null, re);
+        }
+
+    }
+
+    /**
+     * @return the interval at which heartbeat requests are sent
+     */
+    public int getInterval() {
+        return interval;
+    }
+
+    /**
+     * sets the interval at which heartbeat requests are sent
+     * 
+     * @param interval
+     *            the new interval
+     */
+    public void setInterval(int interval) {
+        this.interval = interval;
+    }
+
+    /**
+     * Updates the schedule of the heartbeat to match the set interval. A
+     * negative interval disables the heartbeat.
+     */
+    public void schedule() {
+        if (getInterval() > 0) {
+            getLogger()
+                    .fine("Scheduling heartbeat in " + interval + " seconds");
+            timer.schedule(interval * 1000);
+        } else {
+            if (timer != null) {
+                getLogger().fine("Disabling heartbeat");
+                timer.cancel();
+            }
+        }
+
+    }
+
+    /**
+     * @return the application connection
+     */
+    protected ApplicationConnection getConnection() {
+        return connection;
+    }
+
+}