diff options
author | Artur Signell <artur@vaadin.com> | 2015-04-17 22:02:49 +0300 |
---|---|---|
committer | Artur Signell <artur@vaadin.com> | 2015-07-13 17:19:07 +0300 |
commit | 72b36bbe2d18ee1adc037ba0d4f2093d23412c4f (patch) | |
tree | cef1f19b770c5cdbf0835c0a223efc038ad70255 /server | |
parent | 90b4c678d139fc4927811cdcad87d0b91eca98b6 (diff) | |
download | vaadin-framework-72b36bbe2d18ee1adc037ba0d4f2093d23412c4f.tar.gz vaadin-framework-72b36bbe2d18ee1adc037ba0d4f2093d23412c4f.zip |
Use counter in client to server messages to avoid duplicate handling (#11733)
An server message id counter is included in every client to server message and an
expected id is included in every server to client message. This makes it safe to
re-send any message when the client is not 100% sure the server has received the
message, without having to worry about double handling on the server side.
Change-Id: I840cc04829fc2491f35a0e6f98f07eaf46b1ea42
Diffstat (limited to 'server')
-rw-r--r-- | server/src/com/vaadin/server/communication/ServerRpcHandler.java | 56 | ||||
-rw-r--r-- | server/src/com/vaadin/server/communication/UidlWriter.java | 5 | ||||
-rw-r--r-- | server/src/com/vaadin/ui/UI.java | 31 |
3 files changed, 88 insertions, 4 deletions
diff --git a/server/src/com/vaadin/server/communication/ServerRpcHandler.java b/server/src/com/vaadin/server/communication/ServerRpcHandler.java index 65fb144810..85a8b341d0 100644 --- a/server/src/com/vaadin/server/communication/ServerRpcHandler.java +++ b/server/src/com/vaadin/server/communication/ServerRpcHandler.java @@ -77,6 +77,7 @@ public class ServerRpcHandler implements Serializable { private final int syncId; private final JsonObject json; private final boolean resynchronize; + private final int clientToServerMessageId; public RpcRequest(String jsonString, VaadinRequest request) { json = JsonUtil.parse(jsonString); @@ -106,7 +107,14 @@ public class ServerRpcHandler implements Serializable { } else { resynchronize = false; } - + if (json.hasKey(ApplicationConstants.CLIENT_TO_SERVER_ID)) { + clientToServerMessageId = (int) json + .getNumber(ApplicationConstants.CLIENT_TO_SERVER_ID); + } else { + getLogger() + .warning("Server message without client id received"); + clientToServerMessageId = -1; + } invocations = json.getArray(ApplicationConstants.RPC_INVOCATIONS); } @@ -149,6 +157,15 @@ public class ServerRpcHandler implements Serializable { } /** + * Gets the id of the client to server message + * + * @return the server message id + */ + public int getClientToServerId() { + return clientToServerMessageId; + } + + /** * Gets the entire request in JSON format, as it was received from the * client. * <p> @@ -199,8 +216,41 @@ public class ServerRpcHandler implements Serializable { rpcRequest.getCsrfToken())) { throw new InvalidUIDLSecurityKeyException(""); } - handleInvocations(ui, rpcRequest.getSyncId(), - rpcRequest.getRpcInvocationsData()); + + int expectedId = ui.getLastProcessedClientToServerId() + 1; + if (rpcRequest.getClientToServerId() != -1 + && rpcRequest.getClientToServerId() != expectedId) { + // Invalid message id, skip RPC processing but force a full + // re-synchronization of the client as it might have not received + // the previous response (e.g. due to a bad connection) + + // Must resync also for duplicate messages because the server might + // have generated a response for the first message but the response + // did not reach the client. When the client re-sends the message, + // it would only get an empty response (because the dirty flags have + // been cleared on the server) and would be out of sync + ui.getSession().getCommunicationManager().repaintAll(ui); + + if (rpcRequest.getClientToServerId() < expectedId) { + // Just a duplicate message due to a bad connection or similar + // It has already been handled by the server so it is safe to + // ignore + getLogger().fine( + "Ignoring old message from the client. Expected: " + + expectedId + ", got: " + + rpcRequest.getClientToServerId()); + } else { + getLogger().warning( + "Unexpected message id from the client. Expected: " + + expectedId + ", got: " + + rpcRequest.getClientToServerId()); + } + } else { + // Message id ok, process RPCs + ui.setLastProcessedClientToServerId(expectedId); + handleInvocations(ui, rpcRequest.getSyncId(), + rpcRequest.getRpcInvocationsData()); + } ui.getConnectorTracker().cleanConcurrentlyRemovedConnectorIds( rpcRequest.getSyncId()); diff --git a/server/src/com/vaadin/server/communication/UidlWriter.java b/server/src/com/vaadin/server/communication/UidlWriter.java index a4797e49aa..3d52230927 100644 --- a/server/src/com/vaadin/server/communication/UidlWriter.java +++ b/server/src/com/vaadin/server/communication/UidlWriter.java @@ -130,7 +130,10 @@ public class UidlWriter implements Serializable { .getCurrentSyncId() : -1; writer.write("\"" + ApplicationConstants.SERVER_SYNC_ID + "\": " + syncId + ", "); - + int nextClientToServerMessageId = ui + .getLastProcessedClientToServerId() + 1; + writer.write("\"" + ApplicationConstants.CLIENT_TO_SERVER_ID + + "\": " + nextClientToServerMessageId + ", "); writer.write("\"changes\" : "); JsonPaintTarget paintTarget = new JsonPaintTarget(manager, writer, diff --git a/server/src/com/vaadin/ui/UI.java b/server/src/com/vaadin/ui/UI.java index 2129db614b..28f18a6b92 100644 --- a/server/src/com/vaadin/ui/UI.java +++ b/server/src/com/vaadin/ui/UI.java @@ -260,6 +260,12 @@ public abstract class UI extends AbstractSingleComponentContainer implements this); /** + * Tracks which message from the client should come next. First message from + * the client has id 0. + */ + private int lastProcessedClientToServerId = -1; + + /** * Creates a new empty UI without a caption. The content of the UI must be * set by calling {@link #setContent(Component)} before using the UI. */ @@ -1691,4 +1697,29 @@ public abstract class UI extends AbstractSingleComponentContainer implements public String getEmbedId() { return embedId; } + + /** + * Gets the last processed server message id. + * + * Used internally for communication tracking. + * + * @return lastProcessedServerMessageId the id of the last processed server + * message + */ + public int getLastProcessedClientToServerId() { + return lastProcessedClientToServerId; + } + + /** + * Sets the last processed server message id. + * + * Used internally for communication tracking. + * + * @param lastProcessedServerMessageId + * the id of the last processed server message + */ + public void setLastProcessedClientToServerId( + int lastProcessedClientToServerId) { + this.lastProcessedClientToServerId = lastProcessedClientToServerId; + } } |