]> source.dussan.org Git - vaadin-framework.git/commitdiff
Make Hearbeat available to other modules (#13250)
authorManolo Carrasco <manolo@vaadin.com>
Wed, 14 May 2014 10:55:47 +0000 (12:55 +0200)
committerVaadin Code Review <review@vaadin.com>
Thu, 22 May 2014 09:29:06 +0000 (09:29 +0000)
- When a mobile app goes online/offline we need to change
  Heartbeat interval and restart the schedule.
- We also need to be notified about response status in
  order to show the appropriate offline UI, etc.

Related with Issue #13250 and review https://dev.vaadin.com/review/#/c/3376/

Change-Id: I428501306e37fb8c2ee0ed6022a4c588bd8456db

client/src/com/vaadin/client/ApplicationConnection.java
client/src/com/vaadin/client/communication/Heartbeat.java

index 6dcc2abb44409163ad4d1292a125a308e48acd3b..694fc710609fab14d9ef9fb26d5b78986f2389a3 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * 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
@@ -43,6 +43,7 @@ import com.google.gwt.event.shared.EventBus;
 import com.google.gwt.event.shared.EventHandler;
 import com.google.gwt.event.shared.GwtEvent;
 import com.google.gwt.event.shared.HandlerRegistration;
+import com.google.gwt.event.shared.HasHandlers;
 import com.google.gwt.event.shared.SimpleEventBus;
 import com.google.gwt.http.client.Request;
 import com.google.gwt.http.client.RequestBuilder;
@@ -65,6 +66,7 @@ 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;
@@ -119,7 +121,7 @@ import com.vaadin.shared.ui.ui.UIState.PushConfigurationState;
  * 
  * Entry point classes (widgetsets) define <code>onModuleLoad()</code>.
  */
-public class ApplicationConnection {
+public class ApplicationConnection implements HasHandlers {
 
     /**
      * Helper used to return two values when updating the connector hierarchy.
@@ -341,6 +343,42 @@ public class ApplicationConnection {
 
     }
 
+    /**
+     * Event triggered when a XHR request has finished with the status code of
+     * the response.
+     * 
+     * Useful for handlers observing network failures like online/off-line
+     * monitors.
+     */
+    public static class ConnectionStatusEvent extends
+            GwtEvent<ConnectionStatusEvent.ConnectionStatusHandler> {
+        private int status;
+
+        public static interface ConnectionStatusHandler extends EventHandler {
+            public void onConnectionStatusChange(ConnectionStatusEvent event);
+        }
+
+        public ConnectionStatusEvent(int status) {
+            this.status = status;
+        }
+
+        public int getStatus() {
+            return status;
+        }
+
+        public final static Type<ConnectionStatusHandler> TYPE = new Type<ConnectionStatusHandler>();
+
+        @Override
+        public Type<ConnectionStatusHandler> getAssociatedType() {
+            return TYPE;
+        }
+
+        @Override
+        protected void dispatch(ConnectionStatusHandler handler) {
+            handler.onConnectionStatusChange(this);
+        }
+    }
+
     public static class ResponseHandlingStartedEvent extends
             ApplicationConnectionEvent {
 
@@ -835,6 +873,8 @@ public class ApplicationConnection {
                                 - requestStartTime.getTime()) + "ms");
 
                 int statusCode = response.getStatusCode();
+                // Notify network observers about response status
+                fireEvent(new ConnectionStatusEvent(statusCode));
 
                 switch (statusCode) {
                 case 0:
@@ -932,6 +972,7 @@ public class ApplicationConnection {
             } catch (RequestException e) {
                 VConsole.error(e);
                 endRequest();
+                fireEvent(new ConnectionStatusEvent(0));
             }
         }
     }
@@ -1083,10 +1124,13 @@ public class ApplicationConnection {
                     handleWhenCSSLoaded(jsonText, json);
                 }
             }).schedule(50);
-            VConsole.log("Assuming CSS loading is not complete, "
-                    + "postponing render phase. "
-                    + "(.v-loading-indicator height == 0)");
-            cssWaits++;
+
+            // Show this message just once
+            if (cssWaits++ == 0) {
+                VConsole.log("Assuming CSS loading is not complete, "
+                        + "postponing render phase. "
+                        + "(.v-loading-indicator height == 0)");
+            }
         } else {
             cssLoaded = true;
             handleReceivedJSONMessage(new Date(), jsonText, json);
@@ -3464,6 +3508,11 @@ public class ApplicationConnection {
         return eventBus.addHandler(type, handler);
     }
 
+    @Override
+    public void fireEvent(GwtEvent<?> event) {
+        eventBus.fireEvent(event);
+    }
+
     /**
      * Calls {@link ComponentConnector#flush()} on the active connector. Does
      * nothing if there is no active (focused) connector.
@@ -3558,4 +3607,10 @@ public class ApplicationConnection {
         return Logger.getLogger(ApplicationConnection.class.getName());
     }
 
+    /**
+     * Returns the hearbeat instance.
+     */
+    public Heartbeat getHeartbeat() {
+        return heartbeat;
+    }
 }
index f2253f3faa2d013adfab319ce03e94a6884177e8..1ff0825f0e2c6c6e0849c7f5542c0f4734d891eb 100644 (file)
@@ -25,6 +25,7 @@ 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.client.ApplicationConnection.ConnectionStatusEvent;
 import com.vaadin.shared.ApplicationConstants;
 import com.vaadin.shared.ui.ui.UIConstants;
 
@@ -36,7 +37,6 @@ import com.vaadin.shared.ui.ui.UIConstants;
  */
 public class Heartbeat {
 
-    private int interval = -1;
     private Timer timer = new Timer() {
         @Override
         public void run() {
@@ -45,6 +45,8 @@ public class Heartbeat {
     };
 
     private ApplicationConnection connection;
+    private String uri;
+    private int interval = -1;
 
     private static Logger getLogger() {
         return Logger.getLogger(Heartbeat.class.getName());
@@ -56,11 +58,16 @@ public class Heartbeat {
      * @param connection
      *            the connection
      */
-    public void init(ApplicationConnection connection) {
-        this.connection = connection;
-        interval = connection.getConfiguration().getHeartbeatInterval();
-        setInterval(interval);
-        schedule();
+    public void init(ApplicationConnection applicationConnection) {
+        connection = applicationConnection;
+
+        setInterval(connection.getConfiguration().getHeartbeatInterval());
+
+        uri = ApplicationConnection.addGetParameters(connection
+                .translateVaadinUri(ApplicationConstants.APP_PROTOCOL_PREFIX
+                        + ApplicationConstants.HEARTBEAT_PATH + '/'),
+                UIConstants.UI_ID_PARAMETER + "="
+                        + connection.getConfiguration().getUIId());
 
         connection.addHandler(
                 ApplicationConnection.ApplicationStoppedEvent.TYPE,
@@ -70,22 +77,15 @@ public class Heartbeat {
                     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());
+        timer.cancel();
 
         final RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, uri);
 
@@ -94,24 +94,41 @@ public class Heartbeat {
             @Override
             public void onResponseReceived(Request request, Response response) {
                 int status = response.getStatusCode();
+
+                // Notify network observers about response status
+                connection.fireEvent(new ConnectionStatusEvent(status));
+
                 if (status == Response.SC_OK) {
-                    // TODO Permit retry in some error situations
                     getLogger().fine("Heartbeat response OK");
-                    schedule();
+                } else if (status == 0) {
+                    getLogger().warning(
+                            "Failed sending heartbeat, server is unreachable, retrying in "
+                                    + interval + "secs.");
+                } else if (status >= 500) {
+                    getLogger().warning(
+                            "Failed sending heartbeat, see server logs, retrying in "
+                                    + interval + "secs.");
                 } else if (status == Response.SC_GONE) {
-                    // FIXME This should really do something else like send an
-                    // event
-                    getConnection().showSessionExpiredError(null);
+                    connection.showSessionExpiredError(null);
+                    // If session is expired break the loop
+                    return;
                 } else {
                     getLogger().warning(
                             "Failed sending heartbeat to server. Error code: "
                                     + status);
                 }
+
+                // Don't break the loop
+                schedule();
             }
 
             @Override
             public void onError(Request request, Throwable exception) {
-                getLogger().severe("Exception sending heartbeat: " + exception);
+                getLogger().severe("Exception sending heartbeat: " + exception.getMessage());
+                // Notify network observers about response status
+                connection.fireEvent(new ConnectionStatusEvent(0));
+                // Don't break the loop
+                schedule();
             }
         };
 
@@ -133,39 +150,39 @@ public class Heartbeat {
         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) {
+        if (interval > 0) {
             getLogger()
                     .fine("Scheduling heartbeat in " + interval + " seconds");
             timer.schedule(interval * 1000);
         } else {
-            if (timer != null) {
-                getLogger().fine("Disabling heartbeat");
-                timer.cancel();
-            }
+            getLogger().fine("Disabling heartbeat");
+            timer.cancel();
         }
-
     }
 
     /**
      * @return the application connection
      */
+    @Deprecated
     protected ApplicationConnection getConnection() {
         return connection;
     }
 
+    /**
+     * Changes the heartbeatInterval in runtime and applies it.
+     * 
+     * @param heartbeatInterval
+     *            new interval in seconds.
+     */
+    public void setInterval(int heartbeatInterval) {
+        getLogger().info(
+                "Setting hearbeat interval to " + heartbeatInterval + "sec.");
+        interval = heartbeatInterval;
+        schedule();
+    }
 }