summaryrefslogtreecommitdiffstats
path: root/server
diff options
context:
space:
mode:
authorArtur Signell <artur@vaadin.com>2015-04-17 22:02:49 +0300
committerArtur Signell <artur@vaadin.com>2015-07-13 17:19:07 +0300
commit72b36bbe2d18ee1adc037ba0d4f2093d23412c4f (patch)
treecef1f19b770c5cdbf0835c0a223efc038ad70255 /server
parent90b4c678d139fc4927811cdcad87d0b91eca98b6 (diff)
downloadvaadin-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.java56
-rw-r--r--server/src/com/vaadin/server/communication/UidlWriter.java5
-rw-r--r--server/src/com/vaadin/ui/UI.java31
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;
+ }
}