/*
* 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
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;
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;
*
* 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.
}
+ /**
+ * 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 {
- requestStartTime.getTime()) + "ms");
int statusCode = response.getStatusCode();
+ // Notify network observers about response status
+ fireEvent(new ConnectionStatusEvent(statusCode));
switch (statusCode) {
case 0:
} catch (RequestException e) {
VConsole.error(e);
endRequest();
+ fireEvent(new ConnectionStatusEvent(0));
}
}
}
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);
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.
return Logger.getLogger(ApplicationConnection.class.getName());
}
+ /**
+ * Returns the hearbeat instance.
+ */
+ public Heartbeat getHeartbeat() {
+ return heartbeat;
+ }
}
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;
*/
public class Heartbeat {
- private int interval = -1;
private Timer timer = new Timer() {
@Override
public void run() {
};
private ApplicationConnection connection;
+ private String uri;
+ private int interval = -1;
private static Logger getLogger() {
return Logger.getLogger(Heartbeat.class.getName());
* @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,
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);
@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();
}
};
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();
+ }
}