Browse Source

API refactor based on review (#11733)

ServerMessageHandler -> MessageHandler
ServerCommunicationHandler -> MessageSender
State -> ApplicationState
CommunicationProblemHandler -> ConnectionStateHandler
CommunicationProblemEvent -> XhrConnectionError

Change-Id: I2eccfea9cf6a275eba02ac605b6a172e496bd004
tags/7.6.0.alpha5
Artur Signell 8 years ago
parent
commit
24ac02a523
22 changed files with 194 additions and 191 deletions
  1. 50
    50
      client/src/com/vaadin/client/ApplicationConnection.java
  2. 8
    1
      client/src/com/vaadin/client/LayoutManager.java
  3. 13
    13
      client/src/com/vaadin/client/communication/AtmospherePushConnection.java
  4. 10
    10
      client/src/com/vaadin/client/communication/ConnectionStateHandler.java
  5. 19
    20
      client/src/com/vaadin/client/communication/DefaultConnectionStateHandler.java
  6. 3
    3
      client/src/com/vaadin/client/communication/Heartbeat.java
  7. 15
    14
      client/src/com/vaadin/client/communication/MessageHandler.java
  8. 17
    16
      client/src/com/vaadin/client/communication/MessageSender.java
  9. 1
    1
      client/src/com/vaadin/client/communication/PushConnection.java
  10. 4
    4
      client/src/com/vaadin/client/communication/ServerRpcQueue.java
  11. 4
    1
      client/src/com/vaadin/client/communication/TranslatedURLReference.java
  12. 20
    26
      client/src/com/vaadin/client/communication/XhrConnection.java
  13. 5
    5
      client/src/com/vaadin/client/communication/XhrConnectionError.java
  14. 1
    1
      client/src/com/vaadin/client/debug/internal/InfoSection.java
  15. 1
    1
      client/src/com/vaadin/client/ui/VScrollTable.java
  16. 1
    1
      client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java
  17. 2
    2
      client/src/com/vaadin/client/ui/ui/UIConnector.java
  18. 5
    7
      client/tests/src/com/vaadin/client/communication/ServerMessageHandlerTest.java
  19. 10
    10
      uitest/src/com/vaadin/tests/widgetset/client/MockApplicationConnection.java
  20. 2
    2
      uitest/src/com/vaadin/tests/widgetset/client/MockServerCommunicationHandler.java
  21. 2
    2
      uitest/src/com/vaadin/tests/widgetset/client/MockServerMessageHandler.java
  22. 1
    1
      uitest/src/com/vaadin/tests/widgetset/client/csrf/CsrfButtonConnector.java

+ 50
- 50
client/src/com/vaadin/client/ApplicationConnection.java View File

import com.google.gwt.user.client.ui.HasWidgets; import com.google.gwt.user.client.ui.HasWidgets;
import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConfiguration.ErrorMessage; import com.vaadin.client.ApplicationConfiguration.ErrorMessage;
import com.vaadin.client.ApplicationConnection.ApplicationStoppedEvent;
import com.vaadin.client.ResourceLoader.ResourceLoadEvent; import com.vaadin.client.ResourceLoader.ResourceLoadEvent;
import com.vaadin.client.ResourceLoader.ResourceLoadListener; import com.vaadin.client.ResourceLoader.ResourceLoadListener;
import com.vaadin.client.communication.CommunicationProblemHandler;
import com.vaadin.client.communication.ConnectionStateHandler;
import com.vaadin.client.communication.DefaultConnectionStateHandler;
import com.vaadin.client.communication.Heartbeat; import com.vaadin.client.communication.Heartbeat;
import com.vaadin.client.communication.ReconnectingCommunicationProblemHandler;
import com.vaadin.client.communication.MessageHandler;
import com.vaadin.client.communication.MessageSender;
import com.vaadin.client.communication.RpcManager; import com.vaadin.client.communication.RpcManager;
import com.vaadin.client.communication.ServerCommunicationHandler;
import com.vaadin.client.communication.ServerMessageHandler;
import com.vaadin.client.communication.ServerRpcQueue; import com.vaadin.client.communication.ServerRpcQueue;
import com.vaadin.client.componentlocator.ComponentLocator; import com.vaadin.client.componentlocator.ComponentLocator;
import com.vaadin.client.metadata.ConnectorBundleLoader; import com.vaadin.client.metadata.ConnectorBundleLoader;
/** Event bus for communication events */ /** Event bus for communication events */
private EventBus eventBus = GWT.create(SimpleEventBus.class); private EventBus eventBus = GWT.create(SimpleEventBus.class);


public enum State {
public enum ApplicationState {
INITIALIZING, RUNNING, TERMINATED; INITIALIZING, RUNNING, TERMINATED;
} }


private State state = State.INITIALIZING;
private ApplicationState applicationState = ApplicationState.INITIALIZING;


/** /**
* The communication handler methods are called at certain points during * The communication handler methods are called at certain points during
loadingIndicator.setConnection(this); loadingIndicator.setConnection(this);
serverRpcQueue = GWT.create(ServerRpcQueue.class); serverRpcQueue = GWT.create(ServerRpcQueue.class);
serverRpcQueue.setConnection(this); serverRpcQueue.setConnection(this);
communicationProblemHandler = GWT
.create(ReconnectingCommunicationProblemHandler.class);
communicationProblemHandler.setConnection(this);
serverMessageHandler = GWT.create(ServerMessageHandler.class);
serverMessageHandler.setConnection(this);
serverCommunicationHandler = GWT
.create(ServerCommunicationHandler.class);
serverCommunicationHandler.setConnection(this);
connectionStateHandler = GWT
.create(DefaultConnectionStateHandler.class);
connectionStateHandler.setConnection(this);
messageHandler = GWT.create(MessageHandler.class);
messageHandler.setConnection(this);
messageSender = GWT.create(MessageSender.class);
messageSender.setConnection(this);
} }


public void init(WidgetSet widgetSet, ApplicationConfiguration cnf) { public void init(WidgetSet widgetSet, ApplicationConfiguration cnf) {
String jsonText = configuration.getUIDL(); String jsonText = configuration.getUIDL();
if (jsonText == null) { if (jsonText == null) {
// initial UIDL not in DOM, request from server // initial UIDL not in DOM, request from server
getServerCommunicationHandler().resynchronize();
getMessageSender().resynchronize();
} else { } else {
// initial UIDL provided in DOM, continue as if returned by request // initial UIDL provided in DOM, continue as if returned by request


// Hack to avoid logging an error in endRequest() // Hack to avoid logging an error in endRequest()
getServerCommunicationHandler().startRequest();
getServerMessageHandler().handleMessage(
ServerMessageHandler.parseJson(jsonText));
getMessageSender().startRequest();
getMessageHandler().handleMessage(
MessageHandler.parseJson(jsonText));
} }


// Tooltip can't be created earlier because the // Tooltip can't be created earlier because the
* @return true if the client has some work to be done, false otherwise * @return true if the client has some work to be done, false otherwise
*/ */
private boolean isActive() { private boolean isActive() {
return !getServerMessageHandler().isInitialUidlHandled()
|| isWorkPending()
|| getServerCommunicationHandler().hasActiveRequest()
return !getMessageHandler().isInitialUidlHandled() || isWorkPending()
|| getMessageSender().hasActiveRequest()
|| isExecutingDeferredCommands(); || isExecutingDeferredCommands();
} }


} }


client.getProfilingData = $entry(function() { client.getProfilingData = $entry(function() {
var smh = ap.@com.vaadin.client.ApplicationConnection::getServerMessageHandler();
var smh = ap.@com.vaadin.client.ApplicationConnection::getMessageHandler();
var pd = [ var pd = [
smh.@com.vaadin.client.communication.ServerMessageHandler::lastProcessingTime,
smh.@com.vaadin.client.communication.ServerMessageHandler::totalProcessingTime
smh.@com.vaadin.client.communication.MessageHandler::lastProcessingTime,
smh.@com.vaadin.client.communication.MessageHandler::totalProcessingTime
]; ];
pd = pd.concat(smh.@com.vaadin.client.communication.ServerMessageHandler::serverTimingInfo);
pd[pd.length] = smh.@com.vaadin.client.communication.ServerMessageHandler::bootstrapTime;
pd = pd.concat(smh.@com.vaadin.client.communication.MessageHandler::serverTimingInfo);
pd[pd.length] = smh.@com.vaadin.client.communication.MessageHandler::bootstrapTime;
return pd; return pd;
}); });


int cssWaits = 0; int cssWaits = 0;


protected ServerRpcQueue serverRpcQueue; protected ServerRpcQueue serverRpcQueue;
protected CommunicationProblemHandler communicationProblemHandler;
protected ServerMessageHandler serverMessageHandler;
protected ServerCommunicationHandler serverCommunicationHandler;
protected ConnectionStateHandler connectionStateHandler;
protected MessageHandler messageHandler;
protected MessageSender messageSender;


static final int MAX_CSS_WAITS = 100; static final int MAX_CSS_WAITS = 100;


} }


public void setApplicationRunning(boolean applicationRunning) { public void setApplicationRunning(boolean applicationRunning) {
if (getState() == State.TERMINATED) {
if (getApplicationState() == ApplicationState.TERMINATED) {
if (applicationRunning) { if (applicationRunning) {
getLogger() getLogger()
.severe("Tried to restart a terminated application. This is not supported"); .severe("Tried to restart a terminated application. This is not supported");
"Tried to stop a terminated application. This should not be done"); "Tried to stop a terminated application. This should not be done");
} }
return; return;
} else if (getState() == State.INITIALIZING) {
} else if (getApplicationState() == ApplicationState.INITIALIZING) {
if (applicationRunning) { if (applicationRunning) {
state = State.RUNNING;
applicationState = ApplicationState.RUNNING;
} else { } else {
getLogger() getLogger()
.warning( .warning(
"Tried to stop the application before it has started. This should not be done"); "Tried to stop the application before it has started. This should not be done");
} }
} else if (getState() == State.RUNNING) {
} else if (getApplicationState() == ApplicationState.RUNNING) {
if (!applicationRunning) { if (!applicationRunning) {
state = State.TERMINATED;
applicationState = ApplicationState.TERMINATED;
eventBus.fireEvent(new ApplicationStoppedEvent()); eventBus.fireEvent(new ApplicationStoppedEvent());
} else { } else {
getLogger() getLogger()
} }


/** /**
* Checks if the application is in the {@link State#RUNNING} state.
* Checks if the application is in the {@link ApplicationState#RUNNING}
* state.
* *
* @since * @since
* @return true if the application is in the running state, false otherwise * @return true if the application is in the running state, false otherwise
*/ */
public boolean isApplicationRunning() { public boolean isApplicationRunning() {
return state == State.RUNNING;
return applicationState == ApplicationState.RUNNING;
} }


public <H extends EventHandler> HandlerRegistration addHandler( public <H extends EventHandler> HandlerRegistration addHandler(
* application to go back to a previous state, i.e. a stopped application * application to go back to a previous state, i.e. a stopped application
* can never be re-started * can never be re-started
* *
* @since
* @since 7.6
* @return the current state of this application * @return the current state of this application
*/ */
public State getState() {
return state;
public ApplicationState getApplicationState() {
return applicationState;
} }


/** /**
* *
* @return the server RPC queue * @return the server RPC queue
*/ */
public CommunicationProblemHandler getCommunicationProblemHandler() {
return communicationProblemHandler;
public ConnectionStateHandler getConnectionStateHandler() {
return connectionStateHandler;
} }


/** /**
* Gets the server message handler for this application
* Gets the (server to client) message handler for this application
* *
* @return the server message handler
* @return the message handler
*/ */
public ServerMessageHandler getServerMessageHandler() {
return serverMessageHandler;
public MessageHandler getMessageHandler() {
return messageHandler;
} }


/** /**
} }


/** /**
* Gets the server communication handler for this application
* Gets the (client to server) message sender for this application
* *
* @return the server communication handler
* @return the message sender
*/ */
public ServerCommunicationHandler getServerCommunicationHandler() {
return serverCommunicationHandler;
public MessageSender getMessageSender() {
return messageSender;
} }


/** /**
} }


public int getLastSeenServerSyncId() { public int getLastSeenServerSyncId() {
return getServerMessageHandler().getLastSeenServerSyncId();
return getMessageHandler().getLastSeenServerSyncId();
} }


} }

+ 8
- 1
client/src/com/vaadin/client/LayoutManager.java View File

}; };
private boolean everythingNeedsMeasure = false; private boolean everythingNeedsMeasure = false;


/**
* Sets the application connection this instance is connected to. Called
* internally by the framework.
*
* @param connection
* the application connection this instance is connected to
*/
public void setConnection(ApplicationConnection connection) { public void setConnection(ApplicationConnection connection) {
if (this.connection != null) { if (this.connection != null) {
throw new RuntimeException( throw new RuntimeException(
"Can't start a new layout phase before the previous layout phase ends."); "Can't start a new layout phase before the previous layout phase ends.");
} }


if (connection.getServerMessageHandler().isUpdatingState()) {
if (connection.getMessageHandler().isUpdatingState()) {
// If assertions are enabled, throw an exception // If assertions are enabled, throw an exception
assert false : STATE_CHANGE_MESSAGE; assert false : STATE_CHANGE_MESSAGE;



+ 13
- 13
client/src/com/vaadin/client/communication/AtmospherePushConnection.java View File

String extraParams = UIConstants.UI_ID_PARAMETER + "=" String extraParams = UIConstants.UI_ID_PARAMETER + "="
+ connection.getConfiguration().getUIId(); + connection.getConfiguration().getUIId();


String csrfToken = connection.getServerMessageHandler().getCsrfToken();
String csrfToken = connection.getMessageHandler().getCsrfToken();
if (!csrfToken.equals(ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE)) { if (!csrfToken.equals(ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE)) {
extraParams += "&" + ApplicationConstants.CSRF_TOKEN_PARAMETER extraParams += "&" + ApplicationConstants.CSRF_TOKEN_PARAMETER
+ "=" + csrfToken; + "=" + csrfToken;
} }


if (state == State.CONNECT_PENDING) { if (state == State.CONNECT_PENDING) {
getCommunicationProblemHandler().pushNotConnected(message);
getConnectionStateHandler().pushNotConnected(message);
return; return;
} }


switch (state) { switch (state) {
case CONNECT_PENDING: case CONNECT_PENDING:
state = State.CONNECTED; state = State.CONNECTED;
getCommunicationProblemHandler().pushOk(this);
getConnectionStateHandler().pushOk(this);
break; break;
case DISCONNECT_PENDING: case DISCONNECT_PENDING:
// Set state to connected to make disconnect close the connection // Set state to connected to make disconnect close the connection


protected void onMessage(AtmosphereResponse response) { protected void onMessage(AtmosphereResponse response) {
String message = response.getResponseBody(); String message = response.getResponseBody();
ValueMap json = ServerMessageHandler.parseWrappedJson(message);
ValueMap json = MessageHandler.parseWrappedJson(message);
if (json == null) { if (json == null) {
// Invalid string (not wrapped as expected) // Invalid string (not wrapped as expected)
getCommunicationProblemHandler().pushInvalidContent(this, message);
getConnectionStateHandler().pushInvalidContent(this, message);
return; return;
} else { } else {
getLogger().info( getLogger().info(
"Received push (" + getTransportType() + ") message: " "Received push (" + getTransportType() + ") message: "
+ message); + message);
connection.getServerMessageHandler().handleMessage(json);
connection.getMessageHandler().handleMessage(json);
} }
} }


*/ */
protected void onError(AtmosphereResponse response) { protected void onError(AtmosphereResponse response) {
state = State.DISCONNECTED; state = State.DISCONNECTED;
getCommunicationProblemHandler().pushError(this);
getConnectionStateHandler().pushError(this);
} }


protected void onClose(AtmosphereResponse response) { protected void onClose(AtmosphereResponse response) {
state = State.CONNECT_PENDING; state = State.CONNECT_PENDING;
getCommunicationProblemHandler().pushClosed(this);
getConnectionStateHandler().pushClosed(this);
} }


protected void onClientTimeout(AtmosphereResponse response) { protected void onClientTimeout(AtmosphereResponse response) {
state = State.DISCONNECTED; state = State.DISCONNECTED;
getCommunicationProblemHandler().pushClientTimeout(this);
getConnectionStateHandler().pushClientTimeout(this);
} }


protected void onReconnect(JavaScriptObject request, protected void onReconnect(JavaScriptObject request,
if (state == State.CONNECTED) { if (state == State.CONNECTED) {
state = State.CONNECT_PENDING; state = State.CONNECT_PENDING;
} }
getCommunicationProblemHandler().pushReconnectPending(this);
getConnectionStateHandler().pushReconnectPending(this);
} }


public static abstract class AbstractJSO extends JavaScriptObject { public static abstract class AbstractJSO extends JavaScriptObject {


@Override @Override
public void onError(ResourceLoadEvent event) { public void onError(ResourceLoadEvent event) {
getCommunicationProblemHandler()
getConnectionStateHandler()
.pushScriptLoadError(event.getResourceUrl()); .pushScriptLoadError(event.getResourceUrl());
} }
}); });
return Logger.getLogger(AtmospherePushConnection.class.getName()); return Logger.getLogger(AtmospherePushConnection.class.getName());
} }


private CommunicationProblemHandler getCommunicationProblemHandler() {
return connection.getCommunicationProblemHandler();
private ConnectionStateHandler getConnectionStateHandler() {
return connection.getConnectionStateHandler();
} }


} }

client/src/com/vaadin/client/communication/CommunicationProblemHandler.java → client/src/com/vaadin/client/communication/ConnectionStateHandler.java View File

import elemental.json.JsonObject; import elemental.json.JsonObject;


/** /**
* Interface for handling problems which occur during communication with the
* server.
* Interface for handling problems and other events which occur during
* communication with the server.
* *
* The handler is responsible for handling any problem in XHR, heartbeat and * The handler is responsible for handling any problem in XHR, heartbeat and
* push connections in a way it sees fit. The default implementation is * push connections in a way it sees fit. The default implementation is
* {@link ReconnectingCommunicationProblemHandler}.
* {@link DefaultConnectionStateHandler}.
* *
* @since 7.6 * @since 7.6
* @author Vaadin Ltd * @author Vaadin Ltd
*/ */
public interface CommunicationProblemHandler {
public interface ConnectionStateHandler {


/** /**
* Sets the application connection this handler is connected to. Called
* Sets the application connection this instance is connected to. Called
* internally by the framework. * internally by the framework.
* *
* @param connection * @param connection
* the application connection this handler is connected to
* the application connection this instance is connected to
*/ */
void setConnection(ApplicationConnection connection); void setConnection(ApplicationConnection connection);


* Called when an exception occurs during an XmlHttpRequest request to the * Called when an exception occurs during an XmlHttpRequest request to the
* server. * server.
* *
* @param communicationProblemEvent
* @param xhrConnectionError
* An event containing what was being sent to the server and what * An event containing what was being sent to the server and what
* exception occurred * exception occurred
*/ */
void xhrException(CommunicationProblemEvent communicationProblemEvent);
void xhrException(XhrConnectionError xhrConnectionError);


/** /**
* Called when invalid content (not JSON) was returned from the server as * Called when invalid content (not JSON) was returned from the server as
* An event containing what was being sent to the server and what * An event containing what was being sent to the server and what
* was returned * was returned
*/ */
void xhrInvalidContent(CommunicationProblemEvent communicationProblemEvent);
void xhrInvalidContent(XhrConnectionError xhrConnectionError);


/** /**
* Called when invalid status code (not 200) was returned by the server as * Called when invalid status code (not 200) was returned by the server as
* An event containing what was being sent to the server and what * An event containing what was being sent to the server and what
* was returned * was returned
*/ */
void xhrInvalidStatusCode(CommunicationProblemEvent problemEvent);
void xhrInvalidStatusCode(XhrConnectionError xhrConnectionError);


/** /**
* Called whenever a XmlHttpRequest to the server completes successfully * Called whenever a XmlHttpRequest to the server completes successfully

client/src/com/vaadin/client/communication/ReconnectingCommunicationProblemHandler.java → client/src/com/vaadin/client/communication/DefaultConnectionStateHandler.java View File

import elemental.json.JsonObject; import elemental.json.JsonObject;


/** /**
* Default implementation of the communication problem handler.
* Default implementation of the connection state handler.
* <p> * <p>
* Handles temporary errors by showing a reconnect dialog to the user while * Handles temporary errors by showing a reconnect dialog to the user while
* trying to re-establish the connection to the server and re-send the pending * trying to re-establish the connection to the server and re-send the pending
* @since 7.6 * @since 7.6
* @author Vaadin Ltd * @author Vaadin Ltd
*/ */
public class ReconnectingCommunicationProblemHandler implements
CommunicationProblemHandler {
public class DefaultConnectionStateHandler implements ConnectionStateHandler {


private ApplicationConnection connection; private ApplicationConnection connection;
private ReconnectDialog reconnectDialog = GWT.create(ReconnectDialog.class); private ReconnectDialog reconnectDialog = GWT.create(ReconnectDialog.class);
} }


private static Logger getLogger() { private static Logger getLogger() {
return Logger.getLogger(ReconnectingCommunicationProblemHandler.class
.getName());
return Logger.getLogger(DefaultConnectionStateHandler.class.getName());
} }


/** /**
} }


@Override @Override
public void xhrException(CommunicationProblemEvent event) {
public void xhrException(XhrConnectionError xhrConnectionError) {
debug("xhrException"); debug("xhrException");
handleRecoverableError(Type.XHR, event.getPayload());
handleRecoverableError(Type.XHR, xhrConnectionError.getPayload());
} }


@Override @Override
} }
if (payload != null) { if (payload != null) {
getLogger().info("Re-sending last message to the server..."); getLogger().info("Re-sending last message to the server...");
getConnection().getServerCommunicationHandler().send(payload);
getConnection().getMessageSender().send(payload);
} else { } else {
// Use heartbeat // Use heartbeat
getLogger().info("Trying to re-establish server connection..."); getLogger().info("Trying to re-establish server connection...");
} }


@Override @Override
public void xhrInvalidContent(CommunicationProblemEvent event) {
public void xhrInvalidContent(XhrConnectionError xhrConnectionError) {
debug("xhrInvalidContent"); debug("xhrInvalidContent");
endRequest(); endRequest();


String responseText = event.getResponse().getText();
String responseText = xhrConnectionError.getResponse().getText();
/* /*
* A servlet filter or equivalent may have intercepted the request and * A servlet filter or equivalent may have intercepted the request and
* served non-UIDL content (for instance, a login page if the session * served non-UIDL content (for instance, a login page if the session
WidgetUtil.redirect(refreshToken.getGroup(2)); WidgetUtil.redirect(refreshToken.getGroup(2));
} else { } else {
handleUnrecoverableCommunicationError( handleUnrecoverableCommunicationError(
"Invalid JSON response from server: " + responseText, event);
"Invalid JSON response from server: " + responseText,
xhrConnectionError);
} }


} }
} }


@Override @Override
public void xhrInvalidStatusCode(CommunicationProblemEvent event) {
public void xhrInvalidStatusCode(XhrConnectionError xhrConnectionError) {
debug("xhrInvalidStatusCode"); debug("xhrInvalidStatusCode");


Response response = event.getResponse();
Response response = xhrConnectionError.getResponse();
int statusCode = response.getStatusCode(); int statusCode = response.getStatusCode();
getLogger().warning("Server returned " + statusCode + " for xhr"); getLogger().warning("Server returned " + statusCode + " for xhr");


if (statusCode == 401) { if (statusCode == 401) {
// Authentication/authorization failed, no need to re-try // Authentication/authorization failed, no need to re-try
endRequest(); endRequest();
handleUnauthorized(event);
handleUnauthorized(xhrConnectionError);
return; return;
} else { } else {
// 404, 408 and other 4xx codes CAN be temporary when you have a // 404, 408 and other 4xx codes CAN be temporary when you have a
// proxy between the client and the server and e.g. restart the // proxy between the client and the server and e.g. restart the
// server // server
// 5xx codes may or may not be temporary // 5xx codes may or may not be temporary
handleRecoverableError(Type.XHR, event.getPayload());
handleRecoverableError(Type.XHR, xhrConnectionError.getPayload());
} }
} }


* @since * @since
*/ */
private void endRequest() { private void endRequest() {
getConnection().getServerCommunicationHandler().endRequest();
getConnection().getMessageSender().endRequest();
} }


protected void handleUnauthorized(CommunicationProblemEvent event) {
protected void handleUnauthorized(XhrConnectionError xhrConnectionError) {
/* /*
* Authorization has failed (401). Could be that the session has timed * Authorization has failed (401). Could be that the session has timed
* out. * out.
} }


private void handleUnrecoverableCommunicationError(String details, private void handleUnrecoverableCommunicationError(String details,
CommunicationProblemEvent event) {
XhrConnectionError xhrConnectionError) {
int statusCode = -1; int statusCode = -1;
if (event != null) {
Response response = event.getResponse();
if (xhrConnectionError != null) {
Response response = xhrConnectionError.getResponse();
if (response != null) { if (response != null) {
statusCode = response.getStatusCode(); statusCode = response.getStatusCode();
} }

+ 3
- 3
client/src/com/vaadin/client/communication/Heartbeat.java View File

int status = response.getStatusCode(); int status = response.getStatusCode();


if (status == Response.SC_OK) { if (status == Response.SC_OK) {
connection.getCommunicationProblemHandler().heartbeatOk();
connection.getConnectionStateHandler().heartbeatOk();
} else { } else {
// Handler should stop the application if heartbeat should // Handler should stop the application if heartbeat should
// no longer be sent // no longer be sent
connection.getCommunicationProblemHandler()
connection.getConnectionStateHandler()
.heartbeatInvalidStatusCode(request, response); .heartbeatInvalidStatusCode(request, response);
} }


public void onError(Request request, Throwable exception) { public void onError(Request request, Throwable exception) {
// Handler should stop the application if heartbeat should no // Handler should stop the application if heartbeat should no
// longer be sent // longer be sent
connection.getCommunicationProblemHandler().heartbeatException(
connection.getConnectionStateHandler().heartbeatException(
request, exception); request, exception);
schedule(); schedule();
} }

client/src/com/vaadin/client/communication/ServerMessageHandler.java → client/src/com/vaadin/client/communication/MessageHandler.java View File

import com.google.gwt.user.client.ui.Widget; import com.google.gwt.user.client.ui.Widget;
import com.vaadin.client.ApplicationConfiguration; import com.vaadin.client.ApplicationConfiguration;
import com.vaadin.client.ApplicationConnection; import com.vaadin.client.ApplicationConnection;
import com.vaadin.client.ApplicationConnection.ApplicationState;
import com.vaadin.client.ApplicationConnection.MultiStepDuration; import com.vaadin.client.ApplicationConnection.MultiStepDuration;
import com.vaadin.client.ApplicationConnection.ResponseHandlingStartedEvent; import com.vaadin.client.ApplicationConnection.ResponseHandlingStartedEvent;
import com.vaadin.client.ApplicationConnection.State;
import com.vaadin.client.ComponentConnector; import com.vaadin.client.ComponentConnector;
import com.vaadin.client.ConnectorHierarchyChangeEvent; import com.vaadin.client.ConnectorHierarchyChangeEvent;
import com.vaadin.client.ConnectorMap; import com.vaadin.client.ConnectorMap;
import elemental.json.JsonObject; import elemental.json.JsonObject;


/** /**
* ServerMessageHandler is responsible for handling all incoming messages (JSON)
* A MessageHandler is responsible for handling all incoming messages (JSON)
* from the server (state changes, RPCs and other updates) and ensuring that the * from the server (state changes, RPCs and other updates) and ensuring that the
* connectors are updated accordingly. * connectors are updated accordingly.
* *
* @since 7.6 * @since 7.6
* @author Vaadin Ltd * @author Vaadin Ltd
*/ */
public class ServerMessageHandler {
public class MessageHandler {


public static final String JSON_COMMUNICATION_PREFIX = "for(;;);["; public static final String JSON_COMMUNICATION_PREFIX = "for(;;);[";
public static final String JSON_COMMUNICATION_SUFFIX = "]"; public static final String JSON_COMMUNICATION_SUFFIX = "]";
} }


/** /**
* Sets the application connection this queue is connected to
* Sets the application connection this instance is connected to. Called
* internally by the framework.
* *
* @param connection * @param connection
* the application connection this queue is connected to
* the application connection this instance is connected to
*/ */
public void setConnection(ApplicationConnection connection) { public void setConnection(ApplicationConnection connection) {
this.connection = connection; this.connection = connection;
} }


private static Logger getLogger() { private static Logger getLogger() {
return Logger.getLogger(ServerMessageHandler.class.getName());
return Logger.getLogger(MessageHandler.class.getName());
} }


/** /**
+ "Please verify that the server is up-to-date and that the response data has not been modified in transmission."); + "Please verify that the server is up-to-date and that the response data has not been modified in transmission.");
} }


if (connection.getState() == State.RUNNING) {
if (connection.getApplicationState() == ApplicationState.RUNNING) {
handleJSON(json); handleJSON(json);
} else if (connection.getState() == State.INITIALIZING) {
} else if (connection.getApplicationState() == ApplicationState.INITIALIZING) {
// Application is starting up for the first time // Application is starting up for the first time
connection.setApplicationRunning(true); connection.setApplicationRunning(true);
connection.executeWhenCSSLoaded(new Command() { connection.executeWhenCSSLoaded(new Command() {
if (json.containsKey(ApplicationConstants.CLIENT_TO_SERVER_ID)) { if (json.containsKey(ApplicationConstants.CLIENT_TO_SERVER_ID)) {
int serverNextExpected = json int serverNextExpected = json
.getInt(ApplicationConstants.CLIENT_TO_SERVER_ID); .getInt(ApplicationConstants.CLIENT_TO_SERVER_ID);
getServerCommunicationHandler().setClientToServerMessageId(
serverNextExpected, isResynchronize(json));
getMessageSender().setClientToServerMessageId(serverNextExpected,
isResynchronize(json));
} }


if (serverId != -1) { if (serverId != -1) {
if (isResponse(json)) { if (isResponse(json)) {
// End the request if the received message was a // End the request if the received message was a
// response, not sent asynchronously // response, not sent asynchronously
getServerCommunicationHandler().endRequest();
getMessageSender().endRequest();
} }
} }


// has been lost // has been lost
// Drop pending messages and resynchronize // Drop pending messages and resynchronize
pendingUIDLMessages.clear(); pendingUIDLMessages.clear();
getServerCommunicationHandler().resynchronize();
getMessageSender().resynchronize();
} }
} }
}; };
return connection.getRpcManager(); return connection.getRpcManager();
} }


private ServerCommunicationHandler getServerCommunicationHandler() {
return connection.getServerCommunicationHandler();
private MessageSender getMessageSender() {
return connection.getMessageSender();
} }


/** /**

client/src/com/vaadin/client/communication/ServerCommunicationHandler.java → client/src/com/vaadin/client/communication/MessageSender.java View File

import elemental.json.JsonObject; import elemental.json.JsonObject;


/** /**
* ServerCommunicationHandler is responsible for sending messages to the server.
*
* Internally either XHR or push is used for communicating, depending on the
* application configuration.
* MessageSender is responsible for sending messages to the server.
* <p>
* Internally uses {@link XhrConnection} and/or {@link PushConnection} for
* delivering messages, depending on the application configuration.
* *
* @since 7.6 * @since 7.6
* @author Vaadin Ltd * @author Vaadin Ltd
*/ */
public class ServerCommunicationHandler {
public class MessageSender {


private ApplicationConnection connection; private ApplicationConnection connection;
private boolean hasActiveRequest = false; private boolean hasActiveRequest = false;
private XhrConnection xhrConnection; private XhrConnection xhrConnection;
private PushConnection push; private PushConnection push;


public ServerCommunicationHandler() {
public MessageSender() {
xhrConnection = GWT.create(XhrConnection.class); xhrConnection = GWT.create(XhrConnection.class);
} }


/** /**
* Sets the application connection this handler is connected to
* Sets the application connection this instance is connected to. Called
* internally by the framework.
* *
* @param connection * @param connection
* the application connection this handler is connected to
* the application connection this instance is connected to
*/ */
public void setConnection(ApplicationConnection connection) { public void setConnection(ApplicationConnection connection) {
this.connection = connection; this.connection = connection;
} }


private static Logger getLogger() { private static Logger getLogger() {
return Logger.getLogger(ServerCommunicationHandler.class.getName());
return Logger.getLogger(MessageSender.class.getName());
} }


public void sendInvocationsToServer() { public void sendInvocationsToServer() {
startRequest(); startRequest();


JsonObject payload = Json.createObject(); JsonObject payload = Json.createObject();
String csrfToken = getServerMessageHandler().getCsrfToken();
String csrfToken = getMessageHandler().getCsrfToken();
if (!csrfToken.equals(ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE)) { if (!csrfToken.equals(ApplicationConstants.CSRF_TOKEN_DEFAULT_VALUE)) {
payload.put(ApplicationConstants.CSRF_TOKEN, csrfToken); payload.put(ApplicationConstants.CSRF_TOKEN, csrfToken);
} }
payload.put(ApplicationConstants.RPC_INVOCATIONS, reqInvocations); payload.put(ApplicationConstants.RPC_INVOCATIONS, reqInvocations);
payload.put(ApplicationConstants.SERVER_SYNC_ID,
getServerMessageHandler().getLastSeenServerSyncId());
payload.put(ApplicationConstants.SERVER_SYNC_ID, getMessageHandler()
.getLastSeenServerSyncId());
payload.put(ApplicationConstants.CLIENT_TO_SERVER_ID, payload.put(ApplicationConstants.CLIENT_TO_SERVER_ID,
clientToServerMessageId++); clientToServerMessageId++);


+ "server to client: " + serverToClient; + "server to client: " + serverToClient;
} }


private CommunicationProblemHandler getCommunicationProblemHandler() {
return connection.getCommunicationProblemHandler();
private ConnectionStateHandler getConnectionStateHandler() {
return connection.getConnectionStateHandler();
} }


private ServerMessageHandler getServerMessageHandler() {
return connection.getServerMessageHandler();
private MessageHandler getMessageHandler() {
return connection.getMessageHandler();
} }


private VLoadingIndicator getLoadingIndicator() { private VLoadingIndicator getLoadingIndicator() {

+ 1
- 1
client/src/com/vaadin/client/communication/PushConnection.java View File

* <p> * <p>
* Implementation detail: If the push connection is not connected and the * Implementation detail: If the push connection is not connected and the
* message can thus not be sent, the implementation must call * message can thus not be sent, the implementation must call
* {@link CommunicationProblemHandler#pushNotConnected(JsonObject)}, which
* {@link ConnectionStateHandler#pushNotConnected(JsonObject)}, which
* will retry the send later. * will retry the send later.
* <p> * <p>
* This method must not be called if the push connection is not * This method must not be called if the push connection is not

+ 4
- 4
client/src/com/vaadin/client/communication/ServerRpcQueue.java View File

} }


/** /**
* Sets the application connection this queue is connected to
* Sets the application connection this instance is connected to. Called
* internally by the framework.
* *
* @param connection * @param connection
* the application connection this queue is connected to
* the application connection this instance is connected to
*/ */
public void setConnection(ApplicationConnection connection) { public void setConnection(ApplicationConnection connection) {
this.connection = connection; this.connection = connection;
// Somebody else cleared the queue before we had the chance // Somebody else cleared the queue before we had the chance
return; return;
} }
connection.getServerCommunicationHandler()
.sendInvocationsToServer();
connection.getMessageSender().sendInvocationsToServer();
} }
}; };



+ 4
- 1
client/src/com/vaadin/client/communication/TranslatedURLReference.java View File

private ApplicationConnection connection; private ApplicationConnection connection;


/** /**
* Sets the application connection this instance is connected to. Called
* internally by the framework.
*
* @param connection * @param connection
* the connection to set
* the application connection this instance is connected to
*/ */
public void setConnection(ApplicationConnection connection) { public void setConnection(ApplicationConnection connection) {
this.connection = connection; this.connection = connection;

+ 20
- 26
client/src/com/vaadin/client/communication/XhrConnection.java View File

} }


/** /**
* Sets the application connection this queue is connected to
* Sets the application connection this instance is connected to. Called
* internally by the framework.
* *
* @param connection * @param connection
* the application connection this queue is connected to
* the application connection this instance is connected to
*/ */
public void setConnection(ApplicationConnection connection) { public void setConnection(ApplicationConnection connection) {
this.connection = connection; this.connection = connection;


@Override @Override
public void onError(Request request, Throwable exception) { public void onError(Request request, Throwable exception) {
getCommunicationProblemHandler().xhrException(
new CommunicationProblemEvent(request, payload, exception));
}

private ServerCommunicationHandler getServerCommunicationHandler() {
return connection.getServerCommunicationHandler();
getConnectionStateHandler().xhrException(
new XhrConnectionError(request, payload, exception));
} }


@Override @Override


if (statusCode != 200) { if (statusCode != 200) {
// There was a problem // There was a problem
CommunicationProblemEvent problemEvent = new CommunicationProblemEvent(
XhrConnectionError problemEvent = new XhrConnectionError(
request, payload, response); request, payload, response);


getCommunicationProblemHandler().xhrInvalidStatusCode(
problemEvent);
getConnectionStateHandler().xhrInvalidStatusCode(problemEvent);
return; return;
} }


String contentType = response.getHeader("Content-Type"); String contentType = response.getHeader("Content-Type");
if (contentType == null if (contentType == null
|| !contentType.startsWith("application/json")) { || !contentType.startsWith("application/json")) {
getCommunicationProblemHandler().xhrInvalidContent(
new CommunicationProblemEvent(request, payload,
response));
getConnectionStateHandler().xhrInvalidContent(
new XhrConnectionError(request, payload, response));
return; return;
} }


// for(;;);["+ realJson +"]" // for(;;);["+ realJson +"]"
String responseText = response.getText(); String responseText = response.getText();


ValueMap json = ServerMessageHandler.parseWrappedJson(responseText);
ValueMap json = MessageHandler.parseWrappedJson(responseText);
if (json == null) { if (json == null) {
// Invalid string (not wrapped as expected or can't parse) // Invalid string (not wrapped as expected or can't parse)
getCommunicationProblemHandler().xhrInvalidContent(
new CommunicationProblemEvent(request, payload,
response));
getConnectionStateHandler().xhrInvalidContent(
new XhrConnectionError(request, payload, response));
return; return;
} }


getCommunicationProblemHandler().xhrOk();
getConnectionStateHandler().xhrOk();
getLogger().info("Received xhr message: " + responseText); getLogger().info("Received xhr message: " + responseText);
getServerMessageHandler().handleMessage(json);
getMessageHandler().handleMessage(json);
} }


/** /**
}.schedule(retryTimeout); }.schedule(retryTimeout);
} }
} catch (RequestException e) { } catch (RequestException e) {
getCommunicationProblemHandler().xhrException(
new CommunicationProblemEvent(null, payload, e));
getConnectionStateHandler().xhrException(
new XhrConnectionError(null, payload, e));
} }
} }




} }


private CommunicationProblemHandler getCommunicationProblemHandler() {
return connection.getCommunicationProblemHandler();
private ConnectionStateHandler getConnectionStateHandler() {
return connection.getConnectionStateHandler();
} }


private ServerMessageHandler getServerMessageHandler() {
return connection.getServerMessageHandler();
private MessageHandler getMessageHandler() {
return connection.getMessageHandler();
} }


private static native boolean resendRequest(Request request) private static native boolean resendRequest(Request request)

client/src/com/vaadin/client/communication/CommunicationProblemEvent.java → client/src/com/vaadin/client/communication/XhrConnectionError.java View File

import elemental.json.JsonObject; import elemental.json.JsonObject;


/** /**
* Event describing a problem which took place during communication with the
* server
* XhrConnectionError provides detail about an error which occured during an XHR
* request to the server
* *
* @since 7.6 * @since 7.6
* @author Vaadin Ltd * @author Vaadin Ltd
*/ */
public class CommunicationProblemEvent {
public class XhrConnectionError {


private Throwable exception; private Throwable exception;
private Request request; private Request request;
* @param exception * @param exception
* the exception describing the problem * the exception describing the problem
*/ */
public CommunicationProblemEvent(Request request, JsonObject payload,
public XhrConnectionError(Request request, JsonObject payload,
Throwable exception) { Throwable exception) {
this.request = request; this.request = request;
this.exception = exception; this.exception = exception;
* @param response * @param response
* the response for the request * the response for the request
*/ */
public CommunicationProblemEvent(Request request, JsonObject payload,
public XhrConnectionError(Request request, JsonObject payload,
Response response) { Response response) {
this.request = request; this.request = request;
this.response = response; this.response = response;

+ 1
- 1
client/src/com/vaadin/client/debug/internal/InfoSection.java View File

addRow("Theme", connection.getUIConnector().getActiveTheme()); addRow("Theme", connection.getUIConnector().getActiveTheme());


String communicationMethodInfo = connection String communicationMethodInfo = connection
.getServerCommunicationHandler().getCommunicationMethodName();
.getMessageSender().getCommunicationMethodName();
int pollInterval = connection.getUIConnector().getState().pollInterval; int pollInterval = connection.getUIConnector().getState().pollInterval;
if (pollInterval > 0) { if (pollInterval > 0) {
communicationMethodInfo += " (poll interval " + pollInterval communicationMethodInfo += " (poll interval " + pollInterval

+ 1
- 1
client/src/com/vaadin/client/ui/VScrollTable.java View File

@Override @Override
public void run() { public void run() {


if (client.getServerCommunicationHandler().hasActiveRequest()
if (client.getMessageSender().hasActiveRequest()
|| navKeyDown) { || navKeyDown) {
// if client connection is busy, don't bother loading it more // if client connection is busy, don't bother loading it more
VConsole.log("Postponed rowfetch"); VConsole.log("Postponed rowfetch");

+ 1
- 1
client/src/com/vaadin/client/ui/dd/VDragAndDropManager.java View File

Scheduler.get().scheduleFixedDelay(new RepeatingCommand() { Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
@Override @Override
public boolean execute() { public boolean execute() {
if (!client.getServerCommunicationHandler()
if (!client.getMessageSender()
.hasActiveRequest()) { .hasActiveRequest()) {
removeActiveDragSourceStyleName(dragSource); removeActiveDragSourceStyleName(dragSource);
return false; return false;

+ 2
- 2
client/src/com/vaadin/client/ui/ui/UIConnector.java View File

} }


if (stateChangeEvent.hasPropertyChanged("pushConfiguration")) { if (stateChangeEvent.hasPropertyChanged("pushConfiguration")) {
getConnection().getServerCommunicationHandler().setPushEnabled(
getConnection().getMessageSender().setPushEnabled(
getState().pushConfiguration.mode.isEnabled()); getState().pushConfiguration.mode.isEnabled());
} }
if (stateChangeEvent.hasPropertyChanged("reconnectDialogConfiguration")) { if (stateChangeEvent.hasPropertyChanged("reconnectDialogConfiguration")) {
getConnection().getCommunicationProblemHandler()
getConnection().getConnectionStateHandler()
.configurationUpdated(); .configurationUpdated();
} }



+ 5
- 7
client/tests/src/com/vaadin/client/communication/ServerMessageHandlerTest.java View File

@Test @Test
public void unwrapValidJson() { public void unwrapValidJson() {
String payload = "{'foo': 'bar'}"; String payload = "{'foo': 'bar'}";
Assert.assertEquals(
payload,
ServerMessageHandler.stripJSONWrapping("for(;;);[" + payload
+ "]"));
Assert.assertEquals(payload,
MessageHandler.stripJSONWrapping("for(;;);[" + payload + "]"));


} }


@Test @Test
public void unwrapUnwrappedJson() { public void unwrapUnwrappedJson() {
String payload = "{'foo': 'bar'}"; String payload = "{'foo': 'bar'}";
Assert.assertNull(ServerMessageHandler.stripJSONWrapping(payload));
Assert.assertNull(MessageHandler.stripJSONWrapping(payload));


} }


@Test @Test
public void unwrapNull() { public void unwrapNull() {
Assert.assertNull(ServerMessageHandler.stripJSONWrapping(null));
Assert.assertNull(MessageHandler.stripJSONWrapping(null));


} }


@Test @Test
public void unwrapEmpty() { public void unwrapEmpty() {
Assert.assertNull(ServerMessageHandler.stripJSONWrapping(""));
Assert.assertNull(MessageHandler.stripJSONWrapping(""));


} }
} }

+ 10
- 10
uitest/src/com/vaadin/tests/widgetset/client/MockApplicationConnection.java View File



public MockApplicationConnection() { public MockApplicationConnection() {
super(); super();
serverMessageHandler = new MockServerMessageHandler();
serverMessageHandler.setConnection(this);
serverCommunicationHandler = new MockServerCommunicationHandler();
serverCommunicationHandler.setConnection(this);
messageHandler = new MockServerMessageHandler();
messageHandler.setConnection(this);
messageSender = new MockServerCommunicationHandler();
messageSender.setConnection(this);
} }


@Override @Override
public MockServerMessageHandler getServerMessageHandler() {
return (MockServerMessageHandler) super.getServerMessageHandler();
public MockServerMessageHandler getMessageHandler() {
return (MockServerMessageHandler) super.getMessageHandler();
} }


@Override @Override
public MockServerCommunicationHandler getServerCommunicationHandler() {
public MockServerCommunicationHandler getMessageSender() {
return (MockServerCommunicationHandler) super return (MockServerCommunicationHandler) super
.getServerCommunicationHandler();
.getMessageSender();
} }


/** /**
* @see CsrfTokenDisabled * @see CsrfTokenDisabled
*/ */
public String getLastCsrfTokenReceiver() { public String getLastCsrfTokenReceiver() {
return getServerMessageHandler().lastCsrfTokenReceiver;
return getMessageHandler().lastCsrfTokenReceiver;
} }


/** /**
* @see CsrfTokenDisabled * @see CsrfTokenDisabled
*/ */
public String getLastCsrfTokenSent() { public String getLastCsrfTokenSent() {
return getServerCommunicationHandler().lastCsrfTokenSent;
return getMessageSender().lastCsrfTokenSent;
} }


} }

+ 2
- 2
uitest/src/com/vaadin/tests/widgetset/client/MockServerCommunicationHandler.java View File

*/ */
package com.vaadin.tests.widgetset.client; package com.vaadin.tests.widgetset.client;


import com.vaadin.client.communication.ServerCommunicationHandler;
import com.vaadin.client.communication.MessageSender;
import com.vaadin.shared.ApplicationConstants; import com.vaadin.shared.ApplicationConstants;


import elemental.json.JsonObject; import elemental.json.JsonObject;
import elemental.json.JsonValue; import elemental.json.JsonValue;


public class MockServerCommunicationHandler extends ServerCommunicationHandler {
public class MockServerCommunicationHandler extends MessageSender {


// The last token sent to the server. // The last token sent to the server.
String lastCsrfTokenSent; String lastCsrfTokenSent;

+ 2
- 2
uitest/src/com/vaadin/tests/widgetset/client/MockServerMessageHandler.java View File

package com.vaadin.tests.widgetset.client; package com.vaadin.tests.widgetset.client;


import com.vaadin.client.ValueMap; import com.vaadin.client.ValueMap;
import com.vaadin.client.communication.ServerMessageHandler;
import com.vaadin.client.communication.MessageHandler;
import com.vaadin.shared.ApplicationConstants; import com.vaadin.shared.ApplicationConstants;


public class MockServerMessageHandler extends ServerMessageHandler {
public class MockServerMessageHandler extends MessageHandler {


// The last token received from the server. // The last token received from the server.
protected String lastCsrfTokenReceiver; protected String lastCsrfTokenReceiver;

+ 1
- 1
uitest/src/com/vaadin/tests/widgetset/client/csrf/CsrfButtonConnector.java View File

} }


private String csrfTokenInfo() { private String csrfTokenInfo() {
return getMockConnection().getServerMessageHandler().getCsrfToken()
return getMockConnection().getMessageHandler().getCsrfToken()
+ ", " + getMockConnection().getLastCsrfTokenReceiver() + ", " + ", " + getMockConnection().getLastCsrfTokenReceiver() + ", "
+ getMockConnection().getLastCsrfTokenSent(); + getMockConnection().getLastCsrfTokenSent();
} }

Loading…
Cancel
Save