ServerMessageHandler -> MessageHandler ServerCommunicationHandler -> MessageSender State -> ApplicationState CommunicationProblemHandler -> ConnectionStateHandler CommunicationProblemEvent -> XhrConnectionError Change-Id: I2eccfea9cf6a275eba02ac605b6a172e496bd004tags/7.6.0.alpha5
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(); | |||||
} | } | ||||
} | } |
}; | }; | ||||
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; | ||||
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(); | |||||
} | } | ||||
} | } |
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 |
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(); | ||||
} | } |
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(); | ||||
} | } |
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(); | |||||
} | } | ||||
/** | /** |
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() { |
* <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 |
} | } | ||||
/** | /** | ||||
* 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(); | |||||
} | } | ||||
}; | }; | ||||
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; |
} | } | ||||
/** | /** | ||||
* 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) |
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; |
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 |
@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"); |
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; |
} | } | ||||
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(); | ||||
} | } | ||||
@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("")); | |||||
} | } | ||||
} | } |
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; | |||||
} | } | ||||
} | } |
*/ | */ | ||||
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; |
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; |
} | } | ||||
private String csrfTokenInfo() { | private String csrfTokenInfo() { | ||||
return getMockConnection().getServerMessageHandler().getCsrfToken() | |||||
return getMockConnection().getMessageHandler().getCsrfToken() | |||||
+ ", " + getMockConnection().getLastCsrfTokenReceiver() + ", " | + ", " + getMockConnection().getLastCsrfTokenReceiver() + ", " | ||||
+ getMockConnection().getLastCsrfTokenSent(); | + getMockConnection().getLastCsrfTokenSent(); | ||||
} | } |