Quellcode durchsuchen

Sending and receiving heartbeat requests (#9265)

tags/7.0.0.beta1
Johannes Dahlström vor 11 Jahren
Ursprung
Commit
7a8fd7b5c0

+ 70
- 0
client/src/com/vaadin/terminal/gwt/client/ApplicationConnection.java Datei anzeigen

@@ -230,6 +230,8 @@ public class ApplicationConnection {

rootConnector.init(cnf.getRootPanelId(), this);
showLoadingIndicator();

scheduleHeartbeat();
}

/**
@@ -2520,4 +2522,72 @@ public class ApplicationConnection {
public SerializerMap getSerializerMap() {
return serializerMap;
}

/**
* Schedules a heartbeat request.
*
* @see #sendHeartbeat()
*/
private void scheduleHeartbeat() {
final int interval = 1000 * getConfiguration().getHeartbeatInterval();
if (interval > 0) {
new Timer() {
@Override
public void run() {
sendHeartbeat();
}
}.schedule(interval);
}
}

/**
* Sends a heartbeat request to the server.
* <p>
* 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 Root.
* <p>
* <b>TODO</b>: Improved error handling, like in doUidlRequest().
*
* @see #scheduleHeartbeat()
* @see com.vaadin.ui.Root#heartbeat()
*/
private void sendHeartbeat() {
final String uri = addGetParameters(
translateVaadinUri(ApplicationConstants.APP_PROTOCOL_PREFIX
+ ApplicationConstants.HEARTBEAT_REQUEST_PATH),
ApplicationConstants.ROOT_ID_PARAMETER + "="
+ getConfiguration().getRootId());

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
scheduleHeartbeat();
} else {
VConsole.error("Heartbeat request failed with status code "
+ status);
}
}

@Override
public void onError(Request request, Throwable exception) {
VConsole.error("Heartbeat request resulted in exception");
VConsole.error(exception);
}
};

rb.setCallback(callback);

try {
rb.send();
} catch (RequestException re) {
callback.onError(null, re);
}
}
}

+ 0
- 39
client/src/com/vaadin/terminal/gwt/client/ui/root/RootConnector.java Datei anzeigen

@@ -23,16 +23,10 @@ import com.google.gwt.core.client.Scheduler;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style;
import com.google.gwt.dom.client.Style.Position;
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.Command;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.History;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.Widget;
@@ -90,14 +84,6 @@ public class RootConnector extends AbstractComponentContainerConnector
com.google.gwt.user.client.Window.setTitle(title);
}
});
final int heartbeatInterval = getState().getHeartbeatInterval();
new Timer() {
@Override
public void run() {
sendHeartbeat();
schedule(heartbeatInterval);
}
}.schedule(heartbeatInterval);
}

@Override
@@ -455,29 +441,4 @@ public class RootConnector extends AbstractComponentContainerConnector
}
});
}

private void sendHeartbeat() {
RequestBuilder rb = new RequestBuilder(RequestBuilder.POST, "url");

rb.setCallback(new RequestCallback() {

@Override
public void onResponseReceived(Request request, Response response) {
// TODO Auto-generated method stub

}

@Override
public void onError(Request request, Throwable exception) {
// TODO Auto-generated method stub

}
});

try {
rb.send();
} catch (RequestException re) {

}
}
}

+ 14
- 1
server/src/com/vaadin/terminal/gwt/server/AbstractApplicationPortlet.java Datei anzeigen

@@ -340,7 +340,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
}

protected enum RequestType {
FILE_UPLOAD, UIDL, RENDER, STATIC_FILE, APPLICATION_RESOURCE, DUMMY, EVENT, ACTION, UNKNOWN, BROWSER_DETAILS, CONNECTOR_RESOURCE;
FILE_UPLOAD, UIDL, RENDER, STATIC_FILE, APPLICATION_RESOURCE, DUMMY, EVENT, ACTION, UNKNOWN, BROWSER_DETAILS, CONNECTOR_RESOURCE, HEARTBEAT;
}

protected RequestType getRequestType(WrappedPortletRequest wrappedRequest) {
@@ -361,6 +361,8 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
} else if (ServletPortletHelper
.isApplicationResourceRequest(wrappedRequest)) {
return RequestType.APPLICATION_RESOURCE;
} else if (ServletPortletHelper.isHeartbeatRequest(wrappedRequest)) {
return RequestType.HEARTBEAT;
} else if (isDummyRequest(resourceRequest)) {
return RequestType.DUMMY;
} else {
@@ -431,6 +433,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
Application application = null;
boolean transactionStarted = false;
boolean requestStarted = false;
boolean applicationRunning = false;

try {
// TODO What about PARAM_UNLOADBURST & redirectToApplication??
@@ -459,6 +462,10 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
applicationManager.serveConnectorResource(wrappedRequest,
wrappedResponse);
return;
} else if (requestType == RequestType.HEARTBEAT) {
applicationManager.handleHeartbeatRequest(wrappedRequest,
wrappedResponse, application);
return;
}

/* Update browser information from request */
@@ -477,6 +484,7 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet

/* Start the newly created application */
startApplication(request, application, applicationContext);
applicationRunning = true;

/*
* Transaction starts. Call transaction listeners. Transaction
@@ -585,6 +593,11 @@ public abstract class AbstractApplicationPortlet extends GenericPortlet
handleServiceException(wrappedRequest, wrappedResponse,
application, e);
} finally {

if (applicationRunning) {
application.closeInactiveRoots();
}

// Notifies transaction end
try {
if (transactionStarted) {

+ 14
- 1
server/src/com/vaadin/terminal/gwt/server/AbstractApplicationServlet.java Datei anzeigen

@@ -248,6 +248,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
Application application = null;
boolean transactionStarted = false;
boolean requestStarted = false;
boolean applicationRunning = false;

try {
// If a duplicate "close application" URL is received for an
@@ -287,6 +288,10 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
if (requestType == RequestType.CONNECTOR_RESOURCE) {
applicationManager.serveConnectorResource(request, response);
return;
} else if (requestType == RequestType.HEARTBEAT) {
applicationManager.handleHeartbeatRequest(request, response,
application);
return;
}

/* Update browser information from the request */
@@ -304,6 +309,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements

// Start the application if it's newly created
startApplication(request, application, webApplicationContext);
applicationRunning = true;

/*
* Transaction starts. Call transaction listeners. Transaction end
@@ -354,6 +360,11 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
} catch (final Throwable e) {
handleServiceException(request, response, application, e);
} finally {

if (applicationRunning) {
application.closeInactiveRoots();
}

// Notifies transaction end
try {
if (transactionStarted) {
@@ -1121,7 +1132,7 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
}

protected enum RequestType {
FILE_UPLOAD, BROWSER_DETAILS, UIDL, OTHER, STATIC_FILE, APPLICATION_RESOURCE, CONNECTOR_RESOURCE;
FILE_UPLOAD, BROWSER_DETAILS, UIDL, OTHER, STATIC_FILE, APPLICATION_RESOURCE, CONNECTOR_RESOURCE, HEARTBEAT;
}

protected RequestType getRequestType(WrappedHttpServletRequest request) {
@@ -1137,6 +1148,8 @@ public abstract class AbstractApplicationServlet extends HttpServlet implements
return RequestType.STATIC_FILE;
} else if (ServletPortletHelper.isApplicationResourceRequest(request)) {
return RequestType.APPLICATION_RESOURCE;
} else if (ServletPortletHelper.isHeartbeatRequest(request)) {
return RequestType.HEARTBEAT;
}
return RequestType.OTHER;


+ 37
- 1
server/src/com/vaadin/terminal/gwt/server/AbstractCommunicationManager.java Datei anzeigen

@@ -87,6 +87,7 @@ import com.vaadin.terminal.Vaadin6Component;
import com.vaadin.terminal.VariableOwner;
import com.vaadin.terminal.WrappedRequest;
import com.vaadin.terminal.WrappedResponse;
import com.vaadin.terminal.gwt.client.ApplicationConnection;
import com.vaadin.terminal.gwt.server.BootstrapHandler.BootstrapContext;
import com.vaadin.terminal.gwt.server.ComponentSizeValidator.InvalidLayout;
import com.vaadin.terminal.gwt.server.RpcManager.RpcInvocationException;
@@ -102,7 +103,7 @@ import com.vaadin.ui.Window;
* This is a common base class for the server-side implementations of the
* communication system between the client code (compiled with GWT into
* JavaScript) and the server side components. Its client side counterpart is
* {@link ApplicationConstants}.
* {@link ApplicationConnection}.
*
* TODO Document better!
*/
@@ -577,6 +578,9 @@ public abstract class AbstractCommunicationManager implements Serializable {
return;
}

// Keep the root alive
root.heartbeat();

// Change all variables based on request parameters
if (!handleVariables(request, response, callback, application, root)) {

@@ -2634,6 +2638,38 @@ public abstract class AbstractCommunicationManager implements Serializable {

}

/**
* Handles a heartbeat request. Heartbeat requests are periodically sent by
* the client-side to inform the server that the root sending the heartbeat
* is still alive (the browser window is open, the connection is up) even
* when there are no UIDL requests for a prolonged period of time. Roots
* that do not receive either heartbeat or UIDL requests are eventually
* removed from the application and garbage collected.
*
* @param request
* @param response
* @param application
* @throws IOException
*/
public void handleHeartbeatRequest(WrappedRequest request,
WrappedResponse response, Application application)
throws IOException {
Root root = null;
try {
int rootId = Integer.parseInt(request
.getParameter(ApplicationConstants.ROOT_ID_PARAMETER));
root = application.getRootById(rootId);
} catch (NumberFormatException nfe) {
// null-check below handles this as well
}
if (root != null) {
root.heartbeat();
} else {
response.sendError(HttpServletResponse.SC_NOT_FOUND,
"Root not found");
}
}

public StreamVariable getStreamVariable(String connectorId,
String variableName) {
Map<String, StreamVariable> map = pidToNameToStreamVariable

+ 6
- 1
server/src/com/vaadin/terminal/gwt/server/ServletPortletHelper.java Datei anzeigen

@@ -9,7 +9,7 @@ import com.vaadin.terminal.WrappedRequest;
import com.vaadin.ui.Root;

/*
* Copyright 2011 Vaadin Ltd.
* Copyright 2011 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
@@ -129,4 +129,9 @@ class ServletPortletHelper implements Serializable {
return hasPathPrefix(request, ApplicationConstants.APP_REQUEST_PATH);
}

public static boolean isHeartbeatRequest(WrappedRequest request) {
return hasPathPrefix(request,
ApplicationConstants.HEARTBEAT_REQUEST_PATH);
}

}

+ 2
- 0
shared/src/com/vaadin/shared/ApplicationConstants.java Datei anzeigen

@@ -23,6 +23,8 @@ public class ApplicationConstants {

public static final String UIDL_REQUEST_PATH = "UIDL/";

public static final String HEARTBEAT_REQUEST_PATH = "HEARTBEAT/";

public static final String CONNECTOR_RESOURCE_PREFIX = APP_REQUEST_PATH
+ "CONNECTOR";


Laden…
Abbrechen
Speichern