From bd1993d46b2ab14e4857823af05b4ac180b352a4 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Wed, 21 Sep 2022 14:47:16 +0200 Subject: [PATCH 01/67] Remove serial implementation from Connection and ConnectionManager --- core/src/main/java/io/ably/lib/realtime/Connection.java | 5 ----- .../main/java/io/ably/lib/transport/ConnectionManager.java | 3 --- 2 files changed, 8 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/Connection.java b/core/src/main/java/io/ably/lib/realtime/Connection.java index 4499113db..20195ea66 100644 --- a/core/src/main/java/io/ably/lib/realtime/Connection.java +++ b/core/src/main/java/io/ably/lib/realtime/Connection.java @@ -41,11 +41,6 @@ public class Connection extends EventEmitter Date: Wed, 21 Sep 2022 15:14:57 +0200 Subject: [PATCH 02/67] Remove connectionSerial implementation from ProtocolMessage and ConnectionManager --- .../java/io/ably/lib/transport/ConnectionManager.java | 8 -------- core/src/main/java/io/ably/lib/types/ProtocolMessage.java | 4 ---- 2 files changed, 12 deletions(-) diff --git a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java index ba8c95173..2c91266c0 100644 --- a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -1083,10 +1083,6 @@ public void onMessage(ITransport transport, ProtocolMessage message) throws Ably } private void onChannelMessage(ProtocolMessage message) { - if(message.connectionSerial != null) { - if (connection.key != null) - connection.recoveryKey = connection.key + ":" + message.connectionSerial; - } channels.onMessage(message); } @@ -1122,10 +1118,6 @@ private synchronized void onConnected(ProtocolMessage message) { msgSerial = 0; } connection.id = message.connectionId; - if(message.connectionSerial != null) { - if (connection.key != null) - connection.recoveryKey = connection.key + ":" + message.connectionSerial; - } /* Get any parameters from connectionDetails. */ maxIdleInterval = connectionDetails.maxIdleInterval; diff --git a/core/src/main/java/io/ably/lib/types/ProtocolMessage.java b/core/src/main/java/io/ably/lib/types/ProtocolMessage.java index 9a513dff6..b3c8dc1bb 100644 --- a/core/src/main/java/io/ably/lib/types/ProtocolMessage.java +++ b/core/src/main/java/io/ably/lib/types/ProtocolMessage.java @@ -98,7 +98,6 @@ public ProtocolMessage(Action action, String channel) { public String channel; public String channelSerial; public String connectionId; - public Long connectionSerial; public Long msgSerial; public long timestamp; public Message[] messages; @@ -198,9 +197,6 @@ ProtocolMessage readMsgpack(MessageUnpacker unpacker) throws IOException { case "connectionId": connectionId = unpacker.unpackString(); break; - case "connectionSerial": - connectionSerial = Long.valueOf(unpacker.unpackLong()); - break; case "msgSerial": msgSerial = Long.valueOf(unpacker.unpackLong()); break; From 7d21386595fabb41a3393cd5ff4bc6b439184c15 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Thu, 22 Sep 2022 12:45:29 +0200 Subject: [PATCH 03/67] Set Ably API version from 1.2 to 2.0 Update tests for version check --- core/src/main/java/io/ably/lib/transport/Defaults.java | 2 +- .../java/io/ably/lib/test/realtime/RealtimeHttpHeaderTest.java | 2 +- core/src/test/java/io/ably/lib/test/rest/HttpHeaderTest.java | 2 +- core/src/test/java/io/ably/lib/transport/DefaultsTest.java | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/io/ably/lib/transport/Defaults.java b/core/src/main/java/io/ably/lib/transport/Defaults.java index 1b065b4fb..9ca4b69de 100644 --- a/core/src/main/java/io/ably/lib/transport/Defaults.java +++ b/core/src/main/java/io/ably/lib/transport/Defaults.java @@ -9,7 +9,7 @@ public class Defaults { /* versions */ - public static final float ABLY_VERSION_NUMBER = 1.2f; + public static final float ABLY_VERSION_NUMBER = 2.0f; public static final String ABLY_VERSION = new DecimalFormat("0.0", new DecimalFormatSymbols(Locale.ENGLISH)).format(ABLY_VERSION_NUMBER); public static final String ABLY_AGENT_VERSION = String.format("%s/%s", "ably-java", BuildConfig.VERSION); diff --git a/core/src/test/java/io/ably/lib/test/realtime/RealtimeHttpHeaderTest.java b/core/src/test/java/io/ably/lib/test/realtime/RealtimeHttpHeaderTest.java index 3fa14734c..5dbf287b8 100644 --- a/core/src/test/java/io/ably/lib/test/realtime/RealtimeHttpHeaderTest.java +++ b/core/src/test/java/io/ably/lib/test/realtime/RealtimeHttpHeaderTest.java @@ -81,7 +81,7 @@ public void realtime_websocket_param_test() { * Defaults.ABLY_VERSION_PARAM, as ultimately the request param has been derived from those values. */ assertEquals("Verify correct version", requestParameters.get("v"), - Collections.singletonList("1.2")); + Collections.singletonList("2.0")); /* Spec RSC7d3 * This test should not directly validate version against Defaults.ABLY_AGENT_VERSION, nor diff --git a/core/src/test/java/io/ably/lib/test/rest/HttpHeaderTest.java b/core/src/test/java/io/ably/lib/test/rest/HttpHeaderTest.java index 190b4753d..1e441e5cd 100644 --- a/core/src/test/java/io/ably/lib/test/rest/HttpHeaderTest.java +++ b/core/src/test/java/io/ably/lib/test/rest/HttpHeaderTest.java @@ -86,7 +86,7 @@ public void header_lib_channel_publish() { * from those values. */ Assert.assertNotNull("Expected headers", headers); - Assert.assertEquals(headers.get("x-ably-version"), "1.2"); + Assert.assertEquals(headers.get("x-ably-version"), "2.0"); Assert.assertEquals(headers.get("ably-agent"), expectedAblyAgentHeader); } catch (AblyException e) { e.printStackTrace(); diff --git a/core/src/test/java/io/ably/lib/transport/DefaultsTest.java b/core/src/test/java/io/ably/lib/transport/DefaultsTest.java index 3dcf45318..b5847301d 100644 --- a/core/src/test/java/io/ably/lib/transport/DefaultsTest.java +++ b/core/src/test/java/io/ably/lib/transport/DefaultsTest.java @@ -9,7 +9,7 @@ public class DefaultsTest { @Test public void versions() { - assertThat(Defaults.ABLY_VERSION, is("1.2")); + assertThat(Defaults.ABLY_VERSION, is("2.0")); } @Test From 49bfde9bedf161a014bd564592e4903d110e9443 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Thu, 22 Sep 2022 16:06:24 +0200 Subject: [PATCH 04/67] Remove recovery key direct access and create getter as spec RTN16g Make msgSerial public in ConnectionManager Add ConnectionRecoveryKey class for generating key json Add channelSerial in RealtimeChannelBase for generating recovery key Fix tests implementation for new recovery key spec --- .../java/io/ably/lib/realtime/Connection.java | 41 +++++++++++++++---- .../lib/realtime/ConnectionRecoveryKey.java | 17 ++++++++ .../lib/realtime/RealtimeChannelBase.java | 1 + .../ably/lib/transport/ConnectionManager.java | 3 +- .../test/realtime/RealtimeRecoverTest.java | 4 +- 5 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java diff --git a/core/src/main/java/io/ably/lib/realtime/Connection.java b/core/src/main/java/io/ably/lib/realtime/Connection.java index 20195ea66..f31cfe846 100644 --- a/core/src/main/java/io/ably/lib/realtime/Connection.java +++ b/core/src/main/java/io/ably/lib/realtime/Connection.java @@ -29,12 +29,6 @@ public class Connection extends EventEmitter + * Spec: RTN16g + * + * @return + */ + public String getRecoveryKey() { + //RTN16c + if (key == null || connectionManager == null || connectionManager.getConnectionState() == null || + connectionManager.getConnectionState().state == ConnectionState.closed || + connectionManager.getConnectionState().state == ConnectionState.closing || + connectionManager.getConnectionState().state == ConnectionState.failed || + connectionManager.getConnectionState().state == ConnectionState.suspended + ) { + return null; + } + + ConnectionRecoveryKey recoveryKey = new ConnectionRecoveryKey(); + recoveryKey.connectionKey = key; + recoveryKey.msgSerial = connectionManager.msgSerial; + + for (Object channel : ably.channels.values()) { + if (channel instanceof RealtimeChannelBase) { + RealtimeChannelBase rcb = (RealtimeChannelBase) channel; + recoveryKey.serials.put(rcb.name, rcb.channelSerial); + } + } + + return recoveryKey.asJson(); + } + @Deprecated public void emit(ConnectionState state, ConnectionStateChange stateChange) { super.emit(state.getConnectionEvent(), stateChange); diff --git a/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java b/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java new file mode 100644 index 000000000..2d1a2ed43 --- /dev/null +++ b/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java @@ -0,0 +1,17 @@ +package io.ably.lib.realtime; + +import java.util.HashMap; +import java.util.Map; + +import io.ably.lib.util.Serialisation; + +public class ConnectionRecoveryKey { + + public String connectionKey; + public long msgSerial; + public Map serials = new HashMap<>(); + + public String asJson() { + return Serialisation.gson.toJson(this); + } +} diff --git a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java index 4165457f7..8dd2c49dd 100644 --- a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java +++ b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java @@ -1196,6 +1196,7 @@ public void once(ChannelState state, ChannelStateListener listener) { final String basePath; ChannelOptions options; String syncChannelSerial; + String channelSerial; private Map params; private Set modes; private String lastPayloadMessageId; diff --git a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java index 2c91266c0..d64e99b28 100644 --- a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -1204,7 +1204,6 @@ private boolean checkConnectionStale() { if(connection.key != null) { Log.v(TAG, "Clearing stale connection key to suppress resume"); connection.key = null; - connection.recoveryKey = null; } return true; } @@ -1711,7 +1710,7 @@ private boolean isFatalError(ErrorInfo err) { private boolean suppressRetry; /* for tests only; modified via reflection */ private ITransport transport; private long suspendTime; - private long msgSerial; + public long msgSerial; private long lastActivity; private CMConnectivityListener connectivityListener; private long connectionStateTtl = Defaults.connectionStateTtl; diff --git a/core/src/test/java/io/ably/lib/test/realtime/RealtimeRecoverTest.java b/core/src/test/java/io/ably/lib/test/realtime/RealtimeRecoverTest.java index c933b7298..2f788f807 100644 --- a/core/src/test/java/io/ably/lib/test/realtime/RealtimeRecoverTest.java +++ b/core/src/test/java/io/ably/lib/test/realtime/RealtimeRecoverTest.java @@ -89,7 +89,7 @@ public void recover_disconnected() { * NOTE this depends on knowledge of the internal structure * of the library, to simulate a dropped transport without * causing the connection itself to be disposed */ - String recoverConnectionKey = ablyRx.connection.recoveryKey; + String recoverConnectionKey = ablyRx.connection.getRecoveryKey(); ablyRx.connection.connectionManager.requestState(ConnectionState.failed); /* wait */ @@ -189,7 +189,7 @@ public void recover_implicit_connect() { * NOTE this depends on knowledge of the internal structure * of the library, to simulate a dropped transport without * causing the connection itself to be disposed */ - String recoverConnectionKey = ablyRx.connection.recoveryKey; + String recoverConnectionKey = ablyRx.connection.getRecoveryKey(); ablyRx.connection.connectionManager.requestState(ConnectionState.failed); /* wait */ From d347a2c2462f6cf3ed273946ae66691edbc81459 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Fri, 23 Sep 2022 09:56:34 +0200 Subject: [PATCH 05/67] Implement channelSerial in onMessage and onPresence by spec RTL25 Implement channelSerial to attach message by spec RTL4c1 --- .../main/java/io/ably/lib/realtime/RealtimeChannelBase.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java index 8dd2c49dd..e39033ef4 100644 --- a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java +++ b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java @@ -168,6 +168,7 @@ private void attachImpl(final boolean forceReattach, final CompletionListener li /* send attach request and pending state */ Log.v(TAG, "attach(); channel = " + name + "; sending ATTACH request"); ProtocolMessage attachMessage = new ProtocolMessage(Action.attach, this.name); + attachMessage.channelSerial = this.channelSerial; if(this.options != null) { if(this.options.hasParams()) { attachMessage.params = CollectionUtils.copy(this.options.params); @@ -301,6 +302,7 @@ private void setAttached(ProtocolMessage message) { properties.attachSerial = message.channelSerial; params = message.params; modes = ChannelMode.toSet(message.flags); + channelSerial = message.channelSerial; if(state == ChannelState.attached) { Log.v(TAG, String.format(Locale.ROOT, "Server initiated attach for channel %s", name)); /* emit UPDATE event according to RTL12 */ @@ -728,6 +730,7 @@ private void onMessage(final ProtocolMessage protocolMessage) { lastPayloadMessageId = lastMessage.id; lastPayloadProtocolMessageChannelSerial = protocolMessage.channelSerial; + channelSerial = protocolMessage.channelSerial; for (final Message msg : messages) { this.listeners.onMessage(msg); @@ -768,6 +771,7 @@ private void onPresence(ProtocolMessage message, String syncChannelSerial) { if(msg.timestamp == 0) msg.timestamp = message.timestamp; if(msg.id == null) msg.id = message.id + ':' + i; } + channelSerial = message.channelSerial; presence.setPresence(messages, true, syncChannelSerial); } From 8ad9623bf87c4d3581daed184cce2bc4fda0b5b5 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Fri, 23 Sep 2022 10:12:38 +0200 Subject: [PATCH 06/67] Implement channelSerial clearing on detached, suspended or failed by spec RTP5a1 --- .../main/java/io/ably/lib/realtime/RealtimeChannelBase.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java index e39033ef4..35e28f5dc 100644 --- a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java +++ b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java @@ -102,6 +102,9 @@ private void setState(ChannelState newState, ErrorInfo reason, boolean resumed, /* broadcast state change */ emit(newState, stateChange); } + + if (newState == ChannelState.detached || newState == ChannelState.suspended || newState == ChannelState.failed) + channelSerial = null; } /************************************ @@ -906,6 +909,7 @@ public synchronized void publish(Message[] messages, CompletionListener listener switch(state) { case failed: case suspended: + channelSerial = null; throw AblyException.fromErrorInfo(new ErrorInfo("Unable to publish in failed or suspended state", 400, 40000)); default: connectionManager.send(msg, queueMessages, listener); From 63d7f04ebf64eff2798cdb8b47a7196ab8bf704d Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Fri, 23 Sep 2022 13:15:53 +0200 Subject: [PATCH 07/67] Remove lastPayloadProtocolMessageChannelSerial as it's implementation is replaced by channelSerial --- .../main/java/io/ably/lib/realtime/RealtimeChannelBase.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java index 35e28f5dc..66313e64f 100644 --- a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java +++ b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java @@ -181,7 +181,7 @@ private void attachImpl(final boolean forceReattach, final CompletionListener li } } if(this.decodeFailureRecoveryInProgress) { - attachMessage.channelSerial = this.lastPayloadProtocolMessageChannelSerial; + Log.v(TAG, "attach(); message decode recovery in progress."); } try { if (listener != null) { @@ -732,7 +732,6 @@ private void onMessage(final ProtocolMessage protocolMessage) { } lastPayloadMessageId = lastMessage.id; - lastPayloadProtocolMessageChannelSerial = protocolMessage.channelSerial; channelSerial = protocolMessage.channelSerial; for (final Message msg : messages) { @@ -1208,7 +1207,6 @@ public void once(ChannelState state, ChannelStateListener listener) { private Map params; private Set modes; private String lastPayloadMessageId; - private String lastPayloadProtocolMessageChannelSerial; private boolean decodeFailureRecoveryInProgress; private final DecodingContext decodingContext; } From 8d9d772c0afb2606c64f1adeea28a0cf5d682747 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Mon, 26 Sep 2022 11:00:21 +0200 Subject: [PATCH 08/67] Implement reattaching channels by spec RTN15c6 --- .../ably/lib/realtime/AblyRealtimeBase.java | 15 ++++++++++ .../ably/lib/transport/ConnectionManager.java | 28 +++++++------------ 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java index c2f5180ac..c0f4b3931 100644 --- a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java +++ b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java @@ -183,6 +183,21 @@ public void suspendAll(ErrorInfo error, boolean notifyStateChange) { } } + @Override + public void reAttach() { + for (Map.Entry entry : map.entrySet()) { + RealtimeChannelBase channel = entry.getValue(); + if (channel.state == ChannelState.attaching || channel.state == ChannelState.attached || channel.state == ChannelState.suspended) { + try { + Log.d(TAG, "reAttach(); channel = " + channel.name); + channel.attach(); + } catch (AblyException e) { + e.printStackTrace(); + } + } + } + } + private void clear() { map.clear(); } diff --git a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java index d64e99b28..16cbf3335 100644 --- a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -72,6 +72,7 @@ public class ConnectionManager implements ConnectListener { public interface Channels { void onMessage(ProtocolMessage msg); void suspendAll(ErrorInfo error, boolean notifyStateChange); + void reAttach(); Iterable values(); } @@ -1087,34 +1088,25 @@ private void onChannelMessage(ProtocolMessage message) { } private synchronized void onConnected(ProtocolMessage message) { - /* if the returned connection id differs from - * the existing connection id, then this means - * we need to suspend all existing attachments to - * the old connection. - * If realtime did not reply with an error, it - * signifies that this was a result of an earlier - * connection being invalidated due to being stale. - * - * Suspend all channels attached to the previous id; - * this will be reattached in setConnection() */ ErrorInfo error = message.error; - if(connection.id != null && !message.connectionId.equals(connection.id)) { - /* we need to suspend the original connection */ - if(error == null) { - error = REASON_SUSPENDED; - } - channels.suspendAll(error, false); + + if (message.connectionId.equals(connection.id)) { // RTN15c6 + channels.reAttach(); } /* set the new connection id */ ConnectionDetails connectionDetails = message.connectionDetails; connection.key = connectionDetails.connectionKey; if (!message.connectionId.equals(connection.id)) { + /* we need to suspend the original connection */ + if(error == null) { + error = REASON_SUSPENDED; + } + channels.suspendAll(error, false); /* The connection id has changed. Reset the message serial and the * pending message queue (which fails the messages currently in * there). */ - pendingMessages.reset(msgSerial, - new ErrorInfo("Connection resume failed", 500, 50000)); + pendingMessages.reset(msgSerial, new ErrorInfo("Connection resume failed", 500, 50000)); msgSerial = 0; } connection.id = message.connectionId; From 46b2567dab1c15d0cf202a0b857ddb0ba2076919 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Mon, 26 Sep 2022 17:12:08 +0200 Subject: [PATCH 09/67] Fix reattaching channels by spec RTN15c6 --- core/src/main/java/io/ably/lib/transport/ConnectionManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java index 16cbf3335..87be2d9af 100644 --- a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -1090,7 +1090,7 @@ private void onChannelMessage(ProtocolMessage message) { private synchronized void onConnected(ProtocolMessage message) { ErrorInfo error = message.error; - if (message.connectionId.equals(connection.id)) { // RTN15c6 + if (message.connectionId.equals(connection.id) && error == null) { // RTN15c6 channels.reAttach(); } From e994379d8602380d2402919f1888ecdf14bb2a19 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Mon, 26 Sep 2022 17:15:26 +0200 Subject: [PATCH 10/67] Set channel state to attaching on reattach --- core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java index c0f4b3931..f65feb946 100644 --- a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java +++ b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java @@ -190,6 +190,7 @@ public void reAttach() { if (channel.state == ChannelState.attaching || channel.state == ChannelState.attached || channel.state == ChannelState.suspended) { try { Log.d(TAG, "reAttach(); channel = " + channel.name); + channel.state = ChannelState.attaching; channel.attach(); } catch (AblyException e) { e.printStackTrace(); From 3f102b9e99603993dfd121398917f6bc108d4527 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Mon, 26 Sep 2022 18:27:05 +0200 Subject: [PATCH 11/67] Implement reattaching channels by spec RTN15c7 --- .../ably/lib/realtime/AblyRealtimeBase.java | 3 ++ .../ably/lib/transport/ConnectionManager.java | 31 ++++++++++++------- 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java index f65feb946..6f5d719b1 100644 --- a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java +++ b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java @@ -183,6 +183,9 @@ public void suspendAll(ErrorInfo error, boolean notifyStateChange) { } } + /** + * By spec RTN15c6, RTN15c7 + */ @Override public void reAttach() { for (Map.Entry entry : map.entrySet()) { diff --git a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java index 87be2d9af..7ad4adb66 100644 --- a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -1090,28 +1090,37 @@ private void onChannelMessage(ProtocolMessage message) { private synchronized void onConnected(ProtocolMessage message) { ErrorInfo error = message.error; - if (message.connectionId.equals(connection.id) && error == null) { // RTN15c6 + if (message.action == ProtocolMessage.Action.connected && message.connectionId.equals(connection.id) && error == null) { + //RTN15c6 + Log.d(TAG, "connection has reconnected and resumed successfully"); + connection.reason = null; channels.reAttach(); - } - - /* set the new connection id */ - ConnectionDetails connectionDetails = message.connectionDetails; - connection.key = connectionDetails.connectionKey; - if (!message.connectionId.equals(connection.id)) { + requestState(new StateIndication(ConnectionState.connected, null)); + } else if (message.action == ProtocolMessage.Action.connected && !message.connectionId.equals(connection.id) && error != null) { + //RTN15c7 + Log.d(TAG, "connection resume is invalid: " + error.message); + connection.reason = error; + msgSerial = 0; + channels.reAttach(); + requestState(new StateIndication(ConnectionState.connected, error)); + } else if (!message.connectionId.equals(connection.id)) { + Log.d(TAG, "connection resume failed: " + error.message); /* we need to suspend the original connection */ - if(error == null) { - error = REASON_SUSPENDED; - } + error = REASON_SUSPENDED; channels.suspendAll(error, false); /* The connection id has changed. Reset the message serial and the * pending message queue (which fails the messages currently in * there). */ pendingMessages.reset(msgSerial, new ErrorInfo("Connection resume failed", 500, 50000)); msgSerial = 0; + } else { + Log.d(TAG, "connection has reconnected and resumed successfully"); } - connection.id = message.connectionId; + connection.id = message.connectionId; + ConnectionDetails connectionDetails = message.connectionDetails; /* Get any parameters from connectionDetails. */ + connection.key = connectionDetails.connectionKey; maxIdleInterval = connectionDetails.maxIdleInterval; connectionStateTtl = connectionDetails.connectionStateTtl; From 014fc38081a3e83797a7fc0fe6273cb5707570d4 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Tue, 27 Sep 2022 10:17:46 +0200 Subject: [PATCH 12/67] Implement recover querystring to websocket by spec RTN16k --- .../io/ably/lib/realtime/ConnectionRecoveryKey.java | 5 +++++ .../main/java/io/ably/lib/transport/ITransport.java | 11 +++++------ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java b/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java index 2d1a2ed43..f40fe6928 100644 --- a/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java +++ b/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java @@ -14,4 +14,9 @@ public class ConnectionRecoveryKey { public String asJson() { return Serialisation.gson.toJson(this); } + + public static ConnectionRecoveryKey fromJson(String json) { + return Serialisation.gson.fromJson(json, ConnectionRecoveryKey.class); + } + } diff --git a/core/src/main/java/io/ably/lib/transport/ITransport.java b/core/src/main/java/io/ably/lib/transport/ITransport.java index 364c03abd..d56c60a33 100644 --- a/core/src/main/java/io/ably/lib/transport/ITransport.java +++ b/core/src/main/java/io/ably/lib/transport/ITransport.java @@ -1,5 +1,6 @@ package io.ably.lib.transport; +import io.ably.lib.realtime.ConnectionRecoveryKey; import io.ably.lib.types.AblyException; import io.ably.lib.types.ClientOptions; import io.ably.lib.types.ErrorInfo; @@ -73,13 +74,11 @@ public Param[] getConnectParams(Param[] baseParams) { paramList.add(new Param("resume", connectionKey)); if(connectionSerial != null) paramList.add(new Param("connectionSerial", connectionSerial)); - } else if(options.recover != null) { + } else if(options.recover != null) { //RTN16k mode = Mode.recover; - Pattern recoverSpec = Pattern.compile("^([\\w\\-\\!]+):(\\-?\\d+)$"); - Matcher match = recoverSpec.matcher(options.recover); - if(match.matches()) { - paramList.add(new Param("recover", match.group(1))); - paramList.add(new Param("connectionSerial", match.group(2))); + ConnectionRecoveryKey recoveryKey = ConnectionRecoveryKey.fromJson(options.recover); + if (recoveryKey != null && recoveryKey.connectionKey != null) { + paramList.add(new Param("recover", recoveryKey.connectionKey)); } else { Log.e(TAG, "Invalid recover string specified"); } From 7ae2fe70d5b43ca8e646193e6103ef5477824eb9 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Tue, 27 Sep 2022 14:22:25 +0200 Subject: [PATCH 13/67] Implement client recovery by spec RTN16i, RTN16f and RTN16j --- .../ably/lib/realtime/AblyRealtimeBase.java | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java index 6f5d719b1..1c8166702 100644 --- a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java +++ b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java @@ -58,7 +58,7 @@ public AblyRealtimeBase(String key, PlatformAgentProvider platformAgentProvider) * @param platformAgentProvider for providing the platform specific part of the agent header * @throws AblyException */ - public AblyRealtimeBase(ClientOptions options, PlatformAgentProvider platformAgentProvider) throws AblyException { + public AblyRealtimeBase(final ClientOptions options, PlatformAgentProvider platformAgentProvider) throws AblyException { super(options, platformAgentProvider); final InternalChannels channels = new InternalChannels(); this.channels = (Channels) channels; @@ -72,6 +72,34 @@ public void onConnectionStateChanged(ConnectionStateListener.ConnectionStateChan } }); + if (options.recover != null) { + ConnectionRecoveryKey recoveryKey = ConnectionRecoveryKey.fromJson(options.recover); + if (recoveryKey == null) { + Log.d(TAG, "Recovery key initialization failed!"); + connection.connectionManager.msgSerial = 0; //RTN16f + return; + } + connection.connectionManager.msgSerial = recoveryKey.msgSerial; //RTN16f + + for (Map.Entry entry : recoveryKey.serials.entrySet()) { + //Key is channel name and value is channel serial + RealtimeChannelBase channel = channels.get(entry.getKey()); + String channelSerial = entry.getValue(); + if (channel != null) { + channel.channelSerial = channelSerial; + //channel.attach(); + } + } + + connection.on(ConnectionEvent.connected, new ConnectionStateListener() { + @Override + public void onConnectionStateChanged(ConnectionStateChange state) { + options.recover = null; + channels.reAttach(); + } + }); + } + if(options.autoConnect) connection.connect(); } From c6b2c64f53d5b341b13bebbf1964bb5601f687f7 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Wed, 28 Sep 2022 10:43:54 +0200 Subject: [PATCH 14/67] Fix client recovery attachment by spec RTN16i --- core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java index 1c8166702..a8dfb69ff 100644 --- a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java +++ b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java @@ -87,7 +87,6 @@ public void onConnectionStateChanged(ConnectionStateListener.ConnectionStateChan String channelSerial = entry.getValue(); if (channel != null) { channel.channelSerial = channelSerial; - //channel.attach(); } } @@ -95,7 +94,6 @@ public void onConnectionStateChanged(ConnectionStateListener.ConnectionStateChan @Override public void onConnectionStateChanged(ConnectionStateChange state) { options.recover = null; - channels.reAttach(); } }); } From 8a4e8e724b7d916292bb63d459511d426cf73dfd Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Mon, 3 Oct 2022 09:21:55 +0200 Subject: [PATCH 15/67] Remove unused imports --- .../java/io/ably/lib/test/android/AndroidPushTest.java | 1 - .../src/main/java/io/ably/lib/push/ActivationStateMachine.java | 1 - core/src/main/java/io/ably/lib/transport/ITransport.java | 2 -- 3 files changed, 4 deletions(-) diff --git a/android/src/androidTest/java/io/ably/lib/test/android/AndroidPushTest.java b/android/src/androidTest/java/io/ably/lib/test/android/AndroidPushTest.java index 950274071..14124c098 100644 --- a/android/src/androidTest/java/io/ably/lib/test/android/AndroidPushTest.java +++ b/android/src/androidTest/java/io/ably/lib/test/android/AndroidPushTest.java @@ -53,7 +53,6 @@ import io.ably.lib.types.ClientOptions; import io.ably.lib.types.ErrorInfo; import io.ably.lib.types.Param; -import io.ably.lib.types.RegistrationToken; import io.ably.lib.util.Base64Coder; import io.ably.lib.util.IntentUtils; import io.ably.lib.util.JsonUtils; diff --git a/android/src/main/java/io/ably/lib/push/ActivationStateMachine.java b/android/src/main/java/io/ably/lib/push/ActivationStateMachine.java index 6ebc5118a..2835877c9 100644 --- a/android/src/main/java/io/ably/lib/push/ActivationStateMachine.java +++ b/android/src/main/java/io/ably/lib/push/ActivationStateMachine.java @@ -19,7 +19,6 @@ import io.ably.lib.types.Callback; import io.ably.lib.types.ErrorInfo; import io.ably.lib.types.Param; -import io.ably.lib.types.RegistrationToken; import io.ably.lib.util.IntentUtils; import io.ably.lib.util.Log; import io.ably.lib.util.ParamsUtils; diff --git a/core/src/main/java/io/ably/lib/transport/ITransport.java b/core/src/main/java/io/ably/lib/transport/ITransport.java index d56c60a33..2fd441b01 100644 --- a/core/src/main/java/io/ably/lib/transport/ITransport.java +++ b/core/src/main/java/io/ably/lib/transport/ITransport.java @@ -15,8 +15,6 @@ import java.util.Arrays; import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; public interface ITransport { From 6d27773e9c206bdf4d75cd5cc2979805f536d392 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Mon, 3 Oct 2022 10:24:24 +0200 Subject: [PATCH 16/67] Add spec comments for implemented features in channel recovery Add try/catch for generating class from json in ConnectionRecoveryKey --- .../io/ably/lib/realtime/AblyRealtimeBase.java | 5 ++--- .../ably/lib/realtime/ConnectionRecoveryKey.java | 14 +++++++++++++- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java index a8dfb69ff..26fcacfb5 100644 --- a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java +++ b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java @@ -81,12 +81,11 @@ public void onConnectionStateChanged(ConnectionStateListener.ConnectionStateChan } connection.connectionManager.msgSerial = recoveryKey.msgSerial; //RTN16f - for (Map.Entry entry : recoveryKey.serials.entrySet()) { + for (Map.Entry entry : recoveryKey.serials.entrySet()) { //RTN16j //Key is channel name and value is channel serial RealtimeChannelBase channel = channels.get(entry.getKey()); - String channelSerial = entry.getValue(); if (channel != null) { - channel.channelSerial = channelSerial; + channel.channelSerial = entry.getValue(); //RTN16i } } diff --git a/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java b/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java index f40fe6928..439d5c688 100644 --- a/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java +++ b/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java @@ -1,5 +1,7 @@ package io.ably.lib.realtime; +import com.google.gson.JsonSyntaxException; + import java.util.HashMap; import java.util.Map; @@ -9,6 +11,11 @@ public class ConnectionRecoveryKey { public String connectionKey; public long msgSerial; + /** + * Key - channel name, + *

+ * Value - channel serial + */ public Map serials = new HashMap<>(); public String asJson() { @@ -16,7 +23,12 @@ public String asJson() { } public static ConnectionRecoveryKey fromJson(String json) { - return Serialisation.gson.fromJson(json, ConnectionRecoveryKey.class); + try { + return Serialisation.gson.fromJson(json, ConnectionRecoveryKey.class); + } catch (JsonSyntaxException e) { + e.printStackTrace(); + return null; + } } } From 761a57233a38ff7ba5f60d69790b418f6a06c8a9 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Mon, 3 Oct 2022 16:36:15 +0200 Subject: [PATCH 17/67] Add spec comments for implemented features in ConnectionManager --- .../main/java/io/ably/lib/realtime/RealtimeChannelBase.java | 6 +++--- .../main/java/io/ably/lib/transport/ConnectionManager.java | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java index 66313e64f..03d601e67 100644 --- a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java +++ b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java @@ -305,7 +305,7 @@ private void setAttached(ProtocolMessage message) { properties.attachSerial = message.channelSerial; params = message.params; modes = ChannelMode.toSet(message.flags); - channelSerial = message.channelSerial; + channelSerial = message.channelSerial; //RTL4c1 if(state == ChannelState.attached) { Log.v(TAG, String.format(Locale.ROOT, "Server initiated attach for channel %s", name)); /* emit UPDATE event according to RTL12 */ @@ -732,7 +732,7 @@ private void onMessage(final ProtocolMessage protocolMessage) { } lastPayloadMessageId = lastMessage.id; - channelSerial = protocolMessage.channelSerial; + channelSerial = protocolMessage.channelSerial; //RTL15b for (final Message msg : messages) { this.listeners.onMessage(msg); @@ -773,7 +773,7 @@ private void onPresence(ProtocolMessage message, String syncChannelSerial) { if(msg.timestamp == 0) msg.timestamp = message.timestamp; if(msg.id == null) msg.id = message.id + ':' + i; } - channelSerial = message.channelSerial; + channelSerial = message.channelSerial; //RTL15b presence.setPresence(messages, true, syncChannelSerial); } diff --git a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java index 7ad4adb66..14efd0418 100644 --- a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -1120,7 +1120,7 @@ private synchronized void onConnected(ProtocolMessage message) { connection.id = message.connectionId; ConnectionDetails connectionDetails = message.connectionDetails; /* Get any parameters from connectionDetails. */ - connection.key = connectionDetails.connectionKey; + connection.key = connectionDetails.connectionKey; //RTN16d maxIdleInterval = connectionDetails.maxIdleInterval; connectionStateTtl = connectionDetails.connectionStateTtl; From 52eee303a801578938a522465d1a24d372a399f0 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Tue, 4 Oct 2022 10:00:35 +0200 Subject: [PATCH 18/67] Fix duplicate request state on onConnected Add resending pending messages by spec RTN19a --- .../io/ably/lib/transport/ConnectionManager.java | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java index 14efd0418..3fb1571b6 100644 --- a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -1095,14 +1095,23 @@ private synchronized void onConnected(ProtocolMessage message) { Log.d(TAG, "connection has reconnected and resumed successfully"); connection.reason = null; channels.reAttach(); - requestState(new StateIndication(ConnectionState.connected, null)); + + //RTN19a + if (pendingMessages.queue != null && !pendingMessages.queue.isEmpty()) { + for (QueuedMessage queuedMessage : pendingMessages.queue) { + try { + send(queuedMessage.msg, false, null); + } catch (AblyException e) { + e.printStackTrace(); + } + } + } } else if (message.action == ProtocolMessage.Action.connected && !message.connectionId.equals(connection.id) && error != null) { //RTN15c7 Log.d(TAG, "connection resume is invalid: " + error.message); connection.reason = error; msgSerial = 0; channels.reAttach(); - requestState(new StateIndication(ConnectionState.connected, error)); } else if (!message.connectionId.equals(connection.id)) { Log.d(TAG, "connection resume failed: " + error.message); /* we need to suspend the original connection */ @@ -1113,8 +1122,6 @@ private synchronized void onConnected(ProtocolMessage message) { * there). */ pendingMessages.reset(msgSerial, new ErrorInfo("Connection resume failed", 500, 50000)); msgSerial = 0; - } else { - Log.d(TAG, "connection has reconnected and resumed successfully"); } connection.id = message.connectionId; From c2d5fab42e04376b600811d0b1e35c458904098e Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Tue, 4 Oct 2022 14:20:39 +0200 Subject: [PATCH 19/67] Add spec comments for getRecoveryKey --- core/src/main/java/io/ably/lib/realtime/Connection.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/io/ably/lib/realtime/Connection.java b/core/src/main/java/io/ably/lib/realtime/Connection.java index f31cfe846..ad24a7fb8 100644 --- a/core/src/main/java/io/ably/lib/realtime/Connection.java +++ b/core/src/main/java/io/ably/lib/realtime/Connection.java @@ -99,13 +99,13 @@ public void emitUpdate(ErrorInfo errorInfo) { * @return */ public String getRecoveryKey() { - //RTN16c if (key == null || connectionManager == null || connectionManager.getConnectionState() == null || connectionManager.getConnectionState().state == ConnectionState.closed || connectionManager.getConnectionState().state == ConnectionState.closing || connectionManager.getConnectionState().state == ConnectionState.failed || connectionManager.getConnectionState().state == ConnectionState.suspended ) { + //RTN16h return null; } From 47734769c63580e27b724b7adb2aa04dc7e5650e Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Tue, 4 Oct 2022 14:24:18 +0200 Subject: [PATCH 20/67] Fix recovery implementation by spec RTN16l --- .../io/ably/lib/realtime/AblyRealtimeBase.java | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java index 26fcacfb5..12c5f822e 100644 --- a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java +++ b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java @@ -77,15 +77,15 @@ public void onConnectionStateChanged(ConnectionStateListener.ConnectionStateChan if (recoveryKey == null) { Log.d(TAG, "Recovery key initialization failed!"); connection.connectionManager.msgSerial = 0; //RTN16f - return; - } - connection.connectionManager.msgSerial = recoveryKey.msgSerial; //RTN16f - - for (Map.Entry entry : recoveryKey.serials.entrySet()) { //RTN16j - //Key is channel name and value is channel serial - RealtimeChannelBase channel = channels.get(entry.getKey()); - if (channel != null) { - channel.channelSerial = entry.getValue(); //RTN16i + } else { + connection.connectionManager.msgSerial = recoveryKey.msgSerial; //RTN16f + + for (Map.Entry entry : recoveryKey.serials.entrySet()) { //RTN16j + //Key is channel name and value is channel serial + RealtimeChannelBase channel = channels.get(entry.getKey()); + if (channel != null) { + channel.channelSerial = entry.getValue(); //RTN16i + } } } From bdcc5e0c88d561341ac34458580379618b7407cb Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Wed, 5 Oct 2022 14:05:33 +0200 Subject: [PATCH 21/67] Fix recovery implementation by spec RTL15b --- .../ably/lib/realtime/AblyRealtimeBase.java | 2 +- .../java/io/ably/lib/realtime/Connection.java | 2 +- .../lib/realtime/RealtimeChannelBase.java | 21 ++++++++++++------- .../io/ably/lib/types/ChannelProperties.java | 13 ++++++++++++ 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java index 12c5f822e..5c1632412 100644 --- a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java +++ b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java @@ -84,7 +84,7 @@ public void onConnectionStateChanged(ConnectionStateListener.ConnectionStateChan //Key is channel name and value is channel serial RealtimeChannelBase channel = channels.get(entry.getKey()); if (channel != null) { - channel.channelSerial = entry.getValue(); //RTN16i + channel.properties.channelSerial = entry.getValue(); //RTN16i } } } diff --git a/core/src/main/java/io/ably/lib/realtime/Connection.java b/core/src/main/java/io/ably/lib/realtime/Connection.java index ad24a7fb8..1c844103b 100644 --- a/core/src/main/java/io/ably/lib/realtime/Connection.java +++ b/core/src/main/java/io/ably/lib/realtime/Connection.java @@ -116,7 +116,7 @@ public String getRecoveryKey() { for (Object channel : ably.channels.values()) { if (channel instanceof RealtimeChannelBase) { RealtimeChannelBase rcb = (RealtimeChannelBase) channel; - recoveryKey.serials.put(rcb.name, rcb.channelSerial); + recoveryKey.serials.put(rcb.name, rcb.properties.channelSerial); } } diff --git a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java index 03d601e67..9d9f6cbb6 100644 --- a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java +++ b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java @@ -104,7 +104,7 @@ private void setState(ChannelState newState, ErrorInfo reason, boolean resumed, } if (newState == ChannelState.detached || newState == ChannelState.suspended || newState == ChannelState.failed) - channelSerial = null; + properties.channelSerial = null; //RTP5a1 } /************************************ @@ -171,7 +171,7 @@ private void attachImpl(final boolean forceReattach, final CompletionListener li /* send attach request and pending state */ Log.v(TAG, "attach(); channel = " + name + "; sending ATTACH request"); ProtocolMessage attachMessage = new ProtocolMessage(Action.attach, this.name); - attachMessage.channelSerial = this.channelSerial; + attachMessage.channelSerial = this.properties.channelSerial; if(this.options != null) { if(this.options.hasParams()) { attachMessage.params = CollectionUtils.copy(this.options.params); @@ -305,7 +305,10 @@ private void setAttached(ProtocolMessage message) { properties.attachSerial = message.channelSerial; params = message.params; modes = ChannelMode.toSet(message.flags); - channelSerial = message.channelSerial; //RTL4c1 + if (message.channelSerial != null) { + properties.channelSerial = message.channelSerial; //RTL4c1 + } + if(state == ChannelState.attached) { Log.v(TAG, String.format(Locale.ROOT, "Server initiated attach for channel %s", name)); /* emit UPDATE event according to RTL12 */ @@ -732,7 +735,10 @@ private void onMessage(final ProtocolMessage protocolMessage) { } lastPayloadMessageId = lastMessage.id; - channelSerial = protocolMessage.channelSerial; //RTL15b + + if (protocolMessage.channelSerial != null) { + properties.channelSerial = protocolMessage.channelSerial; //RTL15b + } for (final Message msg : messages) { this.listeners.onMessage(msg); @@ -773,7 +779,9 @@ private void onPresence(ProtocolMessage message, String syncChannelSerial) { if(msg.timestamp == 0) msg.timestamp = message.timestamp; if(msg.id == null) msg.id = message.id + ':' + i; } - channelSerial = message.channelSerial; //RTL15b + if (message.channelSerial != null) { + properties.channelSerial = message.channelSerial; //RTL15b + } presence.setPresence(messages, true, syncChannelSerial); } @@ -908,7 +916,7 @@ public synchronized void publish(Message[] messages, CompletionListener listener switch(state) { case failed: case suspended: - channelSerial = null; + properties.channelSerial = null; //RTP5a1 throw AblyException.fromErrorInfo(new ErrorInfo("Unable to publish in failed or suspended state", 400, 40000)); default: connectionManager.send(msg, queueMessages, listener); @@ -1203,7 +1211,6 @@ public void once(ChannelState state, ChannelStateListener listener) { final String basePath; ChannelOptions options; String syncChannelSerial; - String channelSerial; private Map params; private Set modes; private String lastPayloadMessageId; diff --git a/core/src/main/java/io/ably/lib/types/ChannelProperties.java b/core/src/main/java/io/ably/lib/types/ChannelProperties.java index cdd03603c..96e4fddf5 100644 --- a/core/src/main/java/io/ably/lib/types/ChannelProperties.java +++ b/core/src/main/java/io/ably/lib/types/ChannelProperties.java @@ -8,8 +8,21 @@ public class ChannelProperties { * A message identifier indicating the time of attachment to the channel; * used when recovering a message history to mesh exactly with messages * received on this channel subsequent to attachment. + * contains the last @channelSerial@ received in an @ATTACHED@ @ProtocolMessage@ for the channel, see spec #RTL15a + *

+ * Spec: CP2a */ public String attachSerial; + /** + * Contains the last @channelSerial@ received in any @MESSAGE@, @PRESENCE@, or @ATTACHED@ @ProtocolMesage@ on the channel, + * see spec #RTL15b + * is updated whenever a ProtocolMessage with either MESSAGE, PRESENCE, or ATTACHED actions is received on a channel, + * and is set to the TR4c channelSerial of that ProtocolMessage, if and only if that field (ProtocolMessage.channelSerial) is populated. + *

+ * Spec: CP2b + */ + public String channelSerial; + public ChannelProperties() {} } From ed019edda2d82361de3f9b223897245cf937ed42 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Wed, 5 Oct 2022 14:18:04 +0200 Subject: [PATCH 22/67] Update memberKey by spec RTP17h --- core/src/main/java/io/ably/lib/realtime/Presence.java | 4 ++-- core/src/main/java/io/ably/lib/types/PresenceMessage.java | 8 -------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/Presence.java b/core/src/main/java/io/ably/lib/realtime/Presence.java index 15808b4bb..26af3f1cb 100644 --- a/core/src/main/java/io/ably/lib/realtime/Presence.java +++ b/core/src/main/java/io/ably/lib/realtime/Presence.java @@ -872,7 +872,7 @@ synchronized Collection get(Param[] params) throws AblyExceptio * false if the message is already superseded */ synchronized boolean put(PresenceMessage item) { - String key = item.memberKey(); + String key = item.clientId; //RTP17h /* we've seen this member, so do not remove it at the end of sync */ if(residualMembers != null) residualMembers.remove(key); @@ -967,7 +967,7 @@ synchronized Collection values(boolean wait) throws AblyExcepti * @return */ synchronized boolean remove(PresenceMessage item) { - String key = item.memberKey(); + String key = item.clientId; //RTP17h if (hasNewerItem(key, item)) return false; PresenceMessage existingItem = members.remove(key); diff --git a/core/src/main/java/io/ably/lib/types/PresenceMessage.java b/core/src/main/java/io/ably/lib/types/PresenceMessage.java index bc615ea08..608f882b2 100644 --- a/core/src/main/java/io/ably/lib/types/PresenceMessage.java +++ b/core/src/main/java/io/ably/lib/types/PresenceMessage.java @@ -221,13 +221,5 @@ public JsonElement serialize(PresenceMessage message, Type typeOfMessage, JsonSe } } - /** - * Get the member key for the PresenceMessage. - * @return - */ - public String memberKey() { - return connectionId + ':' + clientId; - } - private static final String TAG = PresenceMessage.class.getName(); } From 7297b00d01756382f9ee4f53b6ef8084cb0e57b4 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Wed, 5 Oct 2022 16:20:24 +0200 Subject: [PATCH 23/67] Update specification comment documentation --- .../main/java/io/ably/lib/realtime/AblyRealtimeBase.java | 6 +++--- core/src/main/java/io/ably/lib/realtime/Connection.java | 4 ++-- .../java/io/ably/lib/realtime/ConnectionRecoveryKey.java | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java index 5c1632412..765acfa4c 100644 --- a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java +++ b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java @@ -80,8 +80,8 @@ public void onConnectionStateChanged(ConnectionStateListener.ConnectionStateChan } else { connection.connectionManager.msgSerial = recoveryKey.msgSerial; //RTN16f - for (Map.Entry entry : recoveryKey.serials.entrySet()) { //RTN16j - //Key is channel name and value is channel serial + for (Map.Entry entry : recoveryKey.serials.entrySet()) { + //RTN16j RealtimeChannelBase channel = channels.get(entry.getKey()); if (channel != null) { channel.properties.channelSerial = entry.getValue(); //RTN16i @@ -92,7 +92,7 @@ public void onConnectionStateChanged(ConnectionStateListener.ConnectionStateChan connection.on(ConnectionEvent.connected, new ConnectionStateListener() { @Override public void onConnectionStateChanged(ConnectionStateChange state) { - options.recover = null; + options.recover = null; //RTN16k } }); } diff --git a/core/src/main/java/io/ably/lib/realtime/Connection.java b/core/src/main/java/io/ably/lib/realtime/Connection.java index 1c844103b..945b6808d 100644 --- a/core/src/main/java/io/ably/lib/realtime/Connection.java +++ b/core/src/main/java/io/ably/lib/realtime/Connection.java @@ -93,10 +93,10 @@ public void emitUpdate(ErrorInfo errorInfo) { } /** - *

* Spec: RTN16g * - * @return + * @return a json string which incorporates the @connectionKey@, the current @msgSerial@, + * and a collection of pairs of channel @name@ and current @channelSerial@ for every currently attached channel. */ public String getRecoveryKey() { if (key == null || connectionManager == null || connectionManager.getConnectionState() == null || diff --git a/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java b/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java index 439d5c688..41e706b4a 100644 --- a/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java +++ b/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java @@ -12,9 +12,9 @@ public class ConnectionRecoveryKey { public String connectionKey; public long msgSerial; /** - * Key - channel name, + * Key - channel name *

- * Value - channel serial + * Value - channelSerial */ public Map serials = new HashMap<>(); From 0e705a57b79b83ba171408dae4cb911e1de19d79 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Thu, 6 Oct 2022 17:17:03 +0200 Subject: [PATCH 24/67] Add re-entry of presence members by spec RTP17g and RTP17f --- .../java/io/ably/lib/realtime/Presence.java | 24 +++++++++++++++++++ .../lib/realtime/RealtimeChannelBase.java | 15 ++++++++---- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/Presence.java b/core/src/main/java/io/ably/lib/realtime/Presence.java index 26af3f1cb..d37891c68 100644 --- a/core/src/main/java/io/ably/lib/realtime/Presence.java +++ b/core/src/main/java/io/ably/lib/realtime/Presence.java @@ -295,6 +295,15 @@ public void onError(ErrorInfo reason) { } } + /** + * Spec: RTP17f, RTP17g + */ + public void reEnter(ChannelState newState) { + if (newState == ChannelState.attached && channel.state == ChannelState.attached) { + internalPresence.reEnter(); + } + } + void setPresence(PresenceMessage[] messages, boolean broadcast, String syncChannelSerial) { Log.v(TAG, "setPresence(); channel = " + channel.name + "; broadcast = " + broadcast + "; syncChannelSerial = " + syncChannelSerial); String syncCursor = null; @@ -1031,6 +1040,21 @@ synchronized void clear() { residualMembers.clear(); } + /** + * Spec: RTP17g + */ + synchronized void reEnter() { + for (Map.Entry entry: members.entrySet()) { + PresenceMessage member = entry.getValue(); + member.action = PresenceMessage.Action.enter; + try { + updatePresence(member, null); + } catch (AblyException e) { + e.printStackTrace(); + } + } + } + private boolean syncInProgress; private Collection residualMembers; private final HashMap members = new HashMap(); diff --git a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java index 9d9f6cbb6..489a8fa09 100644 --- a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java +++ b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java @@ -92,19 +92,24 @@ private void setState(ChannelState newState, ErrorInfo reason, boolean resumed) private void setState(ChannelState newState, ErrorInfo reason, boolean resumed, boolean notifyStateChange) { Log.v(TAG, "setState(): channel = " + name + "; setting " + newState); ChannelStateListener.ChannelStateChange stateChange; - synchronized(this) { + synchronized (this) { stateChange = new ChannelStateListener.ChannelStateChange(newState, this.state, reason, resumed); this.state = stateChange.current; this.reason = stateChange.reason; } - if(notifyStateChange) { + if (newState == ChannelState.detached || newState == ChannelState.suspended || newState == ChannelState.failed) + properties.channelSerial = null; //RTP5a1 + + if (newState == ChannelState.attached && state == ChannelState.attached) { + //RTP17f + presence.reEnter(newState); + } + + if (notifyStateChange) { /* broadcast state change */ emit(newState, stateChange); } - - if (newState == ChannelState.detached || newState == ChannelState.suspended || newState == ChannelState.failed) - properties.channelSerial = null; //RTP5a1 } /************************************ From 271794b6e95c6958cf163b62f4411af8b17f8047 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Fri, 7 Oct 2022 15:21:15 +0200 Subject: [PATCH 25/67] Fix test missing deviceSecret by API protocol 2.0 --- .../java/io/ably/lib/test/android/AndroidPushTest.java | 3 ++- android/src/main/java/io/ably/lib/push/LocalDevice.java | 5 +++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/android/src/androidTest/java/io/ably/lib/test/android/AndroidPushTest.java b/android/src/androidTest/java/io/ably/lib/test/android/AndroidPushTest.java index 14124c098..b3a2ebd69 100644 --- a/android/src/androidTest/java/io/ably/lib/test/android/AndroidPushTest.java +++ b/android/src/androidTest/java/io/ably/lib/test/android/AndroidPushTest.java @@ -1366,11 +1366,12 @@ public void run() throws Exception { } testActivation.registerAndWait(); - DeviceDetails otherDevice = DeviceDetails.fromJsonObject(JsonUtils.object() + LocalDevice otherDevice = LocalDevice.fromJsonObject(JsonUtils.object() .add("id", "other") .add("platform", "android") .add("formFactor", "tablet") .add("metadata", JsonUtils.object()) + .add("deviceSecret", "testdevicesecret==") //Required by API protocol 2.0 .add("push", JsonUtils.object() .add("recipient", JsonUtils.object() .add("transportType", "fcm") diff --git a/android/src/main/java/io/ably/lib/push/LocalDevice.java b/android/src/main/java/io/ably/lib/push/LocalDevice.java index 2dd8ae043..1df953f69 100644 --- a/android/src/main/java/io/ably/lib/push/LocalDevice.java +++ b/android/src/main/java/io/ably/lib/push/LocalDevice.java @@ -15,6 +15,7 @@ import io.ably.lib.types.RegistrationToken; import io.ably.lib.util.Base64Coder; import io.ably.lib.util.Log; +import io.ably.lib.util.Serialisation; public class LocalDevice extends DeviceDetails { public String deviceSecret; @@ -43,6 +44,10 @@ public JsonObject toJsonObject() { return o; } + public static LocalDevice fromJsonObject(JsonObject o) { + return Serialisation.gson.fromJson(o, LocalDevice.class); + } + private void loadPersisted() { /* Spec: RSH8a */ String id = storage.get(SharedPrefKeys.DEVICE_ID, null); From f3d0dd7ca200c8e9674180ac61584ef361a56ee7 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Mon, 10 Oct 2022 16:37:55 +0200 Subject: [PATCH 26/67] Remove old implementation spec RTN15c3 --- .../java/io/ably/lib/transport/ConnectionManager.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java index 3fb1571b6..9f6221e55 100644 --- a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -1112,16 +1112,6 @@ private synchronized void onConnected(ProtocolMessage message) { connection.reason = error; msgSerial = 0; channels.reAttach(); - } else if (!message.connectionId.equals(connection.id)) { - Log.d(TAG, "connection resume failed: " + error.message); - /* we need to suspend the original connection */ - error = REASON_SUSPENDED; - channels.suspendAll(error, false); - /* The connection id has changed. Reset the message serial and the - * pending message queue (which fails the messages currently in - * there). */ - pendingMessages.reset(msgSerial, new ErrorInfo("Connection resume failed", 500, 50000)); - msgSerial = 0; } connection.id = message.connectionId; From 1658a2fd04fdf65706f38edd7001410b6fde5b97 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Mon, 10 Oct 2022 16:39:37 +0200 Subject: [PATCH 27/67] Fix recovery option test to implement new spec --- .../test/realtime/RealtimeConnectFailTest.java | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java b/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java index d6f1c183d..050ab19b3 100644 --- a/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java +++ b/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java @@ -3,6 +3,7 @@ import io.ably.lib.realtime.AblyRealtimeBase; import io.ably.lib.realtime.CompletionListener; import io.ably.lib.realtime.ConnectionEvent; +import io.ably.lib.realtime.ConnectionRecoveryKey; import io.ably.lib.realtime.ConnectionState; import io.ably.lib.realtime.ConnectionStateListener; import io.ably.lib.rest.AblyBase; @@ -30,6 +31,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -334,15 +336,21 @@ public void connect_unknown_recover_fail() { AblyRealtimeBase ably = null; try { ClientOptions opts = createOptions(testVars.keys[0].keyStr); - String recoverConnectionId = "0123456789abcdef-99"; - opts.recover = recoverConnectionId + ":0"; + + ConnectionRecoveryKey recovery = new ConnectionRecoveryKey(); + recovery.connectionKey = "0123456789abcdef-99"; + recovery.msgSerial = 0; + recovery.serials.put("name","0"); + opts.recover = recovery.asJson(); + ably = createAblyRealtime(opts); + ConnectionWaiter connectionWaiter = new ConnectionWaiter(ably.connection); ErrorInfo connectedError = connectionWaiter.waitFor(ConnectionState.connected); assertEquals("Verify connected state is reached", ConnectionState.connected, ably.connection.state); assertNotNull("Verify error is returned", connectedError); - assertEquals("Verify correct error code is given", 80008, connectedError.code); - assertFalse("Verify new connection id is assigned", recoverConnectionId.equals(ably.connection.key)); + assertEquals("Verify correct error code is given", 80018, connectedError.code); + assertNotEquals("Verify new connection id is assigned", recovery.connectionKey, ably.connection.key); } catch (AblyException e) { e.printStackTrace(); fail("init0: Unexpected exception instantiating library"); From 486ff01d5d7a118cb21e9553e54f8f845f14cc52 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Tue, 11 Oct 2022 16:16:26 +0200 Subject: [PATCH 28/67] Fix teardown after test class is finished on null objects --- .../io/ably/lib/test/rest/RestPushTest.java | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/core/src/test/java/io/ably/lib/test/rest/RestPushTest.java b/core/src/test/java/io/ably/lib/test/rest/RestPushTest.java index b7f3b30c6..218c689e8 100644 --- a/core/src/test/java/io/ably/lib/test/rest/RestPushTest.java +++ b/core/src/test/java/io/ably/lib/test/rest/RestPushTest.java @@ -181,12 +181,17 @@ public void setUpBefore() throws Exception { @AfterClass public static void tearDownAfter() throws Exception { - for (DeviceDetails device : allDeviceDetails) { - rest.push.admin.deviceRegistrations.remove(device); - } - for (ChannelSubscription sub : allSubscriptions) { - rest.push.admin.channelSubscriptions.remove(sub); - } + if (allDeviceDetails != null) + for (DeviceDetails device : allDeviceDetails) { + if(device != null) + rest.push.admin.deviceRegistrations.remove(device); + } + + if (allSubscriptions != null) + for (ChannelSubscription sub : allSubscriptions) { + if(sub != null) + rest.push.admin.channelSubscriptions.remove(sub); + } } // RHS1a From f62ebc7ffa3579ddf0ce85abe567d8a96f39ce1c Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Tue, 11 Oct 2022 16:17:21 +0200 Subject: [PATCH 29/67] Improve channels reattach test with closing Ably instance --- .../lib/test/realtime/ConnectionManagerTest.java | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/io/ably/lib/test/realtime/ConnectionManagerTest.java b/core/src/test/java/io/ably/lib/test/realtime/ConnectionManagerTest.java index c76ccdcfc..945b19cb4 100644 --- a/core/src/test/java/io/ably/lib/test/realtime/ConnectionManagerTest.java +++ b/core/src/test/java/io/ably/lib/test/realtime/ConnectionManagerTest.java @@ -642,7 +642,8 @@ public void connection_has_same_id_when_reconnecting_before_statettl_plus_idlein @Test public void channels_are_reattached_after_reconnecting_when_statettl_plus_idleinterval_has_passed() throws AblyException { ClientOptions opts = createOptions(testVars.keys[0].keyStr); - try(AblyRealtimeBase ably = createAblyRealtime(opts)) { + final AblyRealtimeBase ably = createAblyRealtime(opts); + try { final long newTtl = 1000L; final long newIdleInterval = 1000L; /* We want this greater than newTtl + newIdleInterval */ @@ -651,6 +652,9 @@ public void channels_are_reattached_after_reconnecting_when_statettl_plus_idlein final List expectedAttachedChannelHistory = Arrays.asList("attaching", "attached", "attaching", "attached"); final List suspendedChannelHistory = new ArrayList(); final List expectedSuspendedChannelHistory = Arrays.asList("attaching", "attached"); +// final List expectedAttachedChannelHistory = +// opts.useBinaryProtocol ? Arrays.asList("attaching", "attached") : Arrays.asList("attaching", "attached", "detached", "attaching"); + ably.connection.on(ConnectionEvent.connected, new ConnectionStateListener() { @Override public void onConnectionStateChanged(ConnectionStateChange state) { @@ -729,8 +733,14 @@ public void onChannelStateChanged(ChannelStateChange stateChange) { /* Wait for both channels to reattach and verify state histories match the expected ones */ attachedChannelWaiter.waitFor(ChannelState.attached); suspendedChannelWaiter.waitFor(ChannelState.attached); - assertEquals("Attached channel histories do not match", attachedChannelHistory, expectedAttachedChannelHistory); - assertEquals("Suspended channel histories do not match", suspendedChannelHistory, expectedSuspendedChannelHistory); + assertEquals("Attached channel histories do not match", expectedAttachedChannelHistory, attachedChannelHistory); + assertEquals("Suspended channel histories do not match", expectedSuspendedChannelHistory, suspendedChannelHistory); + } catch (AblyException e) { + e.printStackTrace(); + fail("channels_are_reattached_after_reconnecting_when_statettl_plus_idleinterval_has_passed: Unexpected exception"); + } finally { + if (ably != null) + ably.close(); } } } From bfd621d8201a31c3ecd6c4836cb5b56279b4dc4c Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Wed, 12 Oct 2022 08:48:24 +0200 Subject: [PATCH 30/67] Add device secret key required by protocol 2.0 --- core/src/test/java/io/ably/lib/test/rest/RestPushTest.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/src/test/java/io/ably/lib/test/rest/RestPushTest.java b/core/src/test/java/io/ably/lib/test/rest/RestPushTest.java index 218c689e8..88098e95f 100644 --- a/core/src/test/java/io/ably/lib/test/rest/RestPushTest.java +++ b/core/src/test/java/io/ably/lib/test/rest/RestPushTest.java @@ -90,6 +90,7 @@ public void setUpBefore() throws Exception { .add("platform", "ios") .add("formFactor", "phone") .add("metadata", JsonUtils.object()) + .add("deviceSecret", "testdevicesecret==") .add("push", JsonUtils.object() .add("recipient", JsonUtils.object() .add("transportType", "apns") @@ -102,6 +103,7 @@ public void setUpBefore() throws Exception { .add("formFactor", "tablet") .add("clientId", "clientA") .add("metadata", JsonUtils.object()) + .add("deviceSecret", "testdevicesecret==") .add("push", JsonUtils.object() .add("recipient", JsonUtils.object() .add("transportType", "fcm") @@ -114,6 +116,7 @@ public void setUpBefore() throws Exception { .add("formFactor", "tablet") .add("clientId", "clientA") .add("metadata", JsonUtils.object()) + .add("deviceSecret", "testdevicesecret==") .add("push", JsonUtils.object() .add("recipient", JsonUtils.object() .add("transportType", "fcm") @@ -126,6 +129,7 @@ public void setUpBefore() throws Exception { .add("formFactor", "tablet") .add("clientId", "clientB") .add("metadata", JsonUtils.object()) + .add("deviceSecret", "testdevicesecret==") .add("push", JsonUtils.object() .add("recipient", JsonUtils.object() .add("transportType", "fcm") @@ -138,6 +142,7 @@ public void setUpBefore() throws Exception { .add("formFactor", "tablet") .add("clientId", "clientC") .add("metadata", JsonUtils.object()) + .add("deviceSecret", "testdevicesecret==") .add("push", JsonUtils.object() .add("recipient", JsonUtils.object() .add("transportType", "fcm") @@ -411,6 +416,7 @@ public void then(final Helpers.AblyFunction get) t .add("platform", "ios") .add("formFactor", "phone") .add("metadata", JsonUtils.object()) + .add("deviceSecret", "testdevicesecret==") .add("push", JsonUtils.object() .add("recipient", JsonUtils.object() .add("transportType", "apns") @@ -453,6 +459,7 @@ public void push_admin_deviceRegistrations_remove() throws Exception { .add("platform", "ios") .add("formFactor", "phone") .add("metadata", JsonUtils.object()) + .add("deviceSecret", "testdevicesecret==") //Required by API protocol 2.0 .add("push", JsonUtils.object() .add("recipient", JsonUtils.object() .add("transportType", "apns") From 429ff783d6dc2d15752addbcf669aa3809fa7ab4 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Mon, 17 Oct 2022 08:43:22 +0200 Subject: [PATCH 31/67] Fix deviceSecret missing in DeviceDetails per new API requirement --- android/src/main/java/io/ably/lib/push/LocalDevice.java | 9 ++------- core/src/main/java/io/ably/lib/rest/DeviceDetails.java | 4 ++++ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/android/src/main/java/io/ably/lib/push/LocalDevice.java b/android/src/main/java/io/ably/lib/push/LocalDevice.java index 1df953f69..348cedf44 100644 --- a/android/src/main/java/io/ably/lib/push/LocalDevice.java +++ b/android/src/main/java/io/ably/lib/push/LocalDevice.java @@ -18,7 +18,7 @@ import io.ably.lib.util.Serialisation; public class LocalDevice extends DeviceDetails { - public String deviceSecret; + public String deviceIdentityToken; private final Storage storage; @@ -36,12 +36,7 @@ public LocalDevice(ActivationContext activationContext, Storage storage) { } public JsonObject toJsonObject() { - JsonObject o = super.toJsonObject(); - if (deviceSecret != null) { - o.addProperty("deviceSecret", deviceSecret); - } - - return o; + return super.toJsonObject(); } public static LocalDevice fromJsonObject(JsonObject o) { diff --git a/core/src/main/java/io/ably/lib/rest/DeviceDetails.java b/core/src/main/java/io/ably/lib/rest/DeviceDetails.java index 3cb0b501d..eebfa4e8e 100644 --- a/core/src/main/java/io/ably/lib/rest/DeviceDetails.java +++ b/core/src/main/java/io/ably/lib/rest/DeviceDetails.java @@ -13,6 +13,7 @@ public class DeviceDetails { public String platform; public String formFactor; public String clientId; + public String deviceSecret; public JsonObject metadata; public Push push; @@ -83,6 +84,9 @@ public JsonObject toJsonObject() { if (push != null) { o.add("push", push.toJsonObject()); } + if (deviceSecret != null) { + o.addProperty("deviceSecret", deviceSecret); + } return o; } From 2dba61989e5897062cfb8dbef12c61646ad83d33 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Mon, 17 Oct 2022 14:55:00 +0200 Subject: [PATCH 32/67] Fix connection history test on reattach --- .../io/ably/lib/test/realtime/ConnectionManagerTest.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/src/test/java/io/ably/lib/test/realtime/ConnectionManagerTest.java b/core/src/test/java/io/ably/lib/test/realtime/ConnectionManagerTest.java index 945b19cb4..923a4836c 100644 --- a/core/src/test/java/io/ably/lib/test/realtime/ConnectionManagerTest.java +++ b/core/src/test/java/io/ably/lib/test/realtime/ConnectionManagerTest.java @@ -649,11 +649,9 @@ public void channels_are_reattached_after_reconnecting_when_statettl_plus_idlein /* We want this greater than newTtl + newIdleInterval */ final long waitInDisconnectedState = 3000L; final List attachedChannelHistory = new ArrayList(); - final List expectedAttachedChannelHistory = Arrays.asList("attaching", "attached", "attaching", "attached"); + final List expectedAttachedChannelHistory = Arrays.asList("attaching", "attached", "detached", "attaching", "attached"); final List suspendedChannelHistory = new ArrayList(); final List expectedSuspendedChannelHistory = Arrays.asList("attaching", "attached"); -// final List expectedAttachedChannelHistory = -// opts.useBinaryProtocol ? Arrays.asList("attaching", "attached") : Arrays.asList("attaching", "attached", "detached", "attaching"); ably.connection.on(ConnectionEvent.connected, new ConnectionStateListener() { @Override @@ -733,6 +731,11 @@ public void onChannelStateChanged(ChannelStateChange stateChange) { /* Wait for both channels to reattach and verify state histories match the expected ones */ attachedChannelWaiter.waitFor(ChannelState.attached); suspendedChannelWaiter.waitFor(ChannelState.attached); + //wait for callbacks and lists to populate + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + } assertEquals("Attached channel histories do not match", expectedAttachedChannelHistory, attachedChannelHistory); assertEquals("Suspended channel histories do not match", expectedSuspendedChannelHistory, suspendedChannelHistory); } catch (AblyException e) { From fe8ed2cd36c829fe97fa0c2e7de5f5a274545295 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Tue, 18 Oct 2022 15:16:37 +0200 Subject: [PATCH 33/67] Fix re-attaching channel with force reattach --- .../java/io/ably/lib/realtime/AblyRealtimeBase.java | 10 +++------- .../java/io/ably/lib/realtime/RealtimeChannelBase.java | 2 +- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java index 765acfa4c..1f32e86cd 100644 --- a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java +++ b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java @@ -216,13 +216,9 @@ public void reAttach() { for (Map.Entry entry : map.entrySet()) { RealtimeChannelBase channel = entry.getValue(); if (channel.state == ChannelState.attaching || channel.state == ChannelState.attached || channel.state == ChannelState.suspended) { - try { - Log.d(TAG, "reAttach(); channel = " + channel.name); - channel.state = ChannelState.attaching; - channel.attach(); - } catch (AblyException e) { - e.printStackTrace(); - } + Log.d(TAG, "reAttach(); channel = " + channel.name); + channel.state = ChannelState.attaching; + channel.attach(true, null); } } } diff --git a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java index 489a8fa09..9340ce31c 100644 --- a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java +++ b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java @@ -145,7 +145,7 @@ public void attach(CompletionListener listener) throws AblyException { this.attach(false, listener); } - private void attach(boolean forceReattach, CompletionListener listener) { + void attach(boolean forceReattach, CompletionListener listener) { clearAttachTimers(); attachWithTimeout(forceReattach, listener); } From ce44c37bc8208fff9bee9cf2127ff3fca6dc581e Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Tue, 18 Oct 2022 15:17:13 +0200 Subject: [PATCH 34/67] Improve test for channel resuming --- .../ably/lib/test/realtime/RealtimeResumeTest.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/core/src/test/java/io/ably/lib/test/realtime/RealtimeResumeTest.java b/core/src/test/java/io/ably/lib/test/realtime/RealtimeResumeTest.java index 523bf2c6d..caad6e717 100644 --- a/core/src/test/java/io/ably/lib/test/realtime/RealtimeResumeTest.java +++ b/core/src/test/java/io/ably/lib/test/realtime/RealtimeResumeTest.java @@ -50,12 +50,15 @@ public void resume_none() { ClientOptions opts = createOptions(testVars.keys[0].keyStr); ably = createAblyRealtime(opts); + ConnectionWaiter connectionWaiter = new ConnectionWaiter(ably.connection); + /* create and attach channel */ final RealtimeChannelBase channel = ably.channels.get(channelName); System.out.println("Attaching"); channel.attach(); - (new ChannelWaiter(channel)).waitFor(ChannelState.attached); - assertEquals("Verify attached state reached", channel.state, ChannelState.attached); + ChannelWaiter channelWaiter = new ChannelWaiter(channel); + channelWaiter.waitFor(ChannelState.attached); + assertEquals("Verify attached state reached", ChannelState.attached, channel.state); /* disconnect the connection, without closing, /* suppressing automatic retries by the connection manager */ @@ -68,11 +71,15 @@ public void resume_none() { fail("Unexpected exception in suppressing retries"); } + connectionWaiter.waitFor(ConnectionState.disconnected); + System.out.println("Connection is disconnected and channels is: " + channel.state.name()); + assertEquals("Disconnected state was not reached", ConnectionState.disconnected, ably.connection.state); + /* reconnect the rx connection */ ably.connection.connect(); System.out.println("Waiting for reconnection"); - ConnectionWaiter connectionWaiter = new ConnectionWaiter(ably.connection); connectionWaiter.waitFor(ConnectionState.connected); + System.out.println("Connection reconnected and channel is " + channel.state.name()); assertEquals("Verify connected state is reached", ConnectionState.connected, ably.connection.state); /* wait */ @@ -81,7 +88,6 @@ public void resume_none() { /* Check the channel is still attached. */ assertEquals("Verify channel still attached", channel.state, ChannelState.attached); - } catch (AblyException e) { e.printStackTrace(); fail("init0: Unexpected exception instantiating library"); From 602a223dea300dfa36e6b56c6d7710dfae935e12 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Wed, 19 Oct 2022 12:51:30 +0200 Subject: [PATCH 35/67] Fix re-auth history check state --- .../lib/test/realtime/RealtimeConnectFailTest.java | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java b/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java index 050ab19b3..778169293 100644 --- a/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java +++ b/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java @@ -417,7 +417,6 @@ public void onError(ErrorInfo reason) { */ @Test public void connect_reauth_failure_state_flow_test() { - try { AblyBase ablyRest = null; ClientOptions opts = createOptions(testVars.keys[0].keyStr); @@ -464,6 +463,13 @@ public void onConnectionStateChanged(ConnectionStateChange state) { ConnectionState.connecting, ConnectionState.disconnected ); + + //wait for connection to not include connect state in list + try { + Thread.sleep(500); + } catch (InterruptedException e) { + } + final int maxDisconnections = 3; ablyRealtime.connection.on(new ConnectionStateListener() { int disconnections = 0; @@ -474,7 +480,7 @@ public void onConnectionStateChanged(ConnectionStateChange state) { if (state.current == ConnectionState.disconnected) { disconnections++; if (disconnections == maxDisconnections) { - assertTrue("Verifying state change history", stateHistory.equals(correctHistory)); + assertEquals("Verifying state change history", correctHistory, stateHistory); ablyRealtime.close(); } } @@ -484,6 +490,7 @@ public void onConnectionStateChanged(ConnectionStateChange state) { ConnectionWaiter connectionWaiter = new ConnectionWaiter(ablyRealtime.connection); connectionWaiter.waitFor(ConnectionState.closed); + assertEquals("Verifying state closed", ConnectionState.closed, ablyRealtime.connection.state); } catch (AblyException e) { e.printStackTrace(); fail("init0: Unexpected exception instantiating library"); From 73ced2d5681d14b5ce37bb0991145b71086617bb Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Thu, 20 Oct 2022 14:02:03 +0200 Subject: [PATCH 36/67] Remove connectionSerial as it is not used any more --- core/src/main/java/io/ably/lib/transport/ITransport.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/src/main/java/io/ably/lib/transport/ITransport.java b/core/src/main/java/io/ably/lib/transport/ITransport.java index 2fd441b01..5c1ef5e05 100644 --- a/core/src/main/java/io/ably/lib/transport/ITransport.java +++ b/core/src/main/java/io/ably/lib/transport/ITransport.java @@ -38,7 +38,6 @@ class TransportParams { protected String host; protected int port; protected String connectionKey; - protected String connectionSerial; protected Mode mode; protected boolean heartbeats; private final PlatformAgentProvider platformAgentProvider; @@ -70,8 +69,6 @@ public Param[] getConnectParams(Param[] baseParams) { if(connectionKey != null) { mode = Mode.resume; paramList.add(new Param("resume", connectionKey)); - if(connectionSerial != null) - paramList.add(new Param("connectionSerial", connectionSerial)); } else if(options.recover != null) { //RTN16k mode = Mode.recover; ConnectionRecoveryKey recoveryKey = ConnectionRecoveryKey.fromJson(options.recover); From 4221b801e2b00c1e8a06c6d4df1d271c2aaee529 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Fri, 21 Oct 2022 09:41:09 +0200 Subject: [PATCH 37/67] Remove unnecessary connected check and add doc comment in onConnected method --- .../java/io/ably/lib/transport/ConnectionManager.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java index 9f6221e55..04d143590 100644 --- a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -1087,10 +1087,14 @@ private void onChannelMessage(ProtocolMessage message) { channels.onMessage(message); } + /** + * Handle {@link ProtocolMessage.Action.connected} messages + * @param message a ProtocolMessage object + */ private synchronized void onConnected(ProtocolMessage message) { ErrorInfo error = message.error; - if (message.action == ProtocolMessage.Action.connected && message.connectionId.equals(connection.id) && error == null) { + if (message.connectionId.equals(connection.id) && error == null) { //RTN15c6 Log.d(TAG, "connection has reconnected and resumed successfully"); connection.reason = null; @@ -1106,7 +1110,7 @@ private synchronized void onConnected(ProtocolMessage message) { } } } - } else if (message.action == ProtocolMessage.Action.connected && !message.connectionId.equals(connection.id) && error != null) { + } else if (!message.connectionId.equals(connection.id) && error != null) { //RTN15c7 Log.d(TAG, "connection resume is invalid: " + error.message); connection.reason = error; From 0287b17b8e72fc88332dac8d5b7cb33b6e3ffb25 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Fri, 21 Oct 2022 10:40:28 +0200 Subject: [PATCH 38/67] Fix spec RTP17f to not reEnter if old state was already attached --- core/src/main/java/io/ably/lib/realtime/Presence.java | 2 +- .../java/io/ably/lib/realtime/RealtimeChannelBase.java | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/Presence.java b/core/src/main/java/io/ably/lib/realtime/Presence.java index d37891c68..7d83ee743 100644 --- a/core/src/main/java/io/ably/lib/realtime/Presence.java +++ b/core/src/main/java/io/ably/lib/realtime/Presence.java @@ -299,7 +299,7 @@ public void onError(ErrorInfo reason) { * Spec: RTP17f, RTP17g */ public void reEnter(ChannelState newState) { - if (newState == ChannelState.attached && channel.state == ChannelState.attached) { + if (newState == ChannelState.attached && channel.state != ChannelState.attached) { internalPresence.reEnter(); } } diff --git a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java index 9340ce31c..01fcf4fcc 100644 --- a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java +++ b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java @@ -98,14 +98,12 @@ private void setState(ChannelState newState, ErrorInfo reason, boolean resumed, this.reason = stateChange.reason; } - if (newState == ChannelState.detached || newState == ChannelState.suspended || newState == ChannelState.failed) + if (newState == ChannelState.detached || newState == ChannelState.suspended || newState == ChannelState.failed) { properties.channelSerial = null; //RTP5a1 - - if (newState == ChannelState.attached && state == ChannelState.attached) { - //RTP17f - presence.reEnter(newState); } + presence.reEnter(newState); //RTP17f + if (notifyStateChange) { /* broadcast state change */ emit(newState, stateChange); From 9376960d50a05bde41b75309966b53c4f30e5210 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Fri, 21 Oct 2022 10:42:48 +0200 Subject: [PATCH 39/67] Refactor sending pending messages when client is connected per spec RTN19a --- .../ably/lib/transport/ConnectionManager.java | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java index 04d143590..c1056fc6d 100644 --- a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -1099,17 +1099,6 @@ private synchronized void onConnected(ProtocolMessage message) { Log.d(TAG, "connection has reconnected and resumed successfully"); connection.reason = null; channels.reAttach(); - - //RTN19a - if (pendingMessages.queue != null && !pendingMessages.queue.isEmpty()) { - for (QueuedMessage queuedMessage : pendingMessages.queue) { - try { - send(queuedMessage.msg, false, null); - } catch (AblyException e) { - e.printStackTrace(); - } - } - } } else if (!message.connectionId.equals(connection.id) && error != null) { //RTN15c7 Log.d(TAG, "connection resume is invalid: " + error.message); @@ -1118,6 +1107,17 @@ private synchronized void onConnected(ProtocolMessage message) { channels.reAttach(); } + //RTN19a + if (pendingMessages.queue != null && !pendingMessages.queue.isEmpty()) { + for (QueuedMessage queuedMessage : pendingMessages.queue) { + try { + send(queuedMessage.msg, false, null); + } catch (AblyException e) { + e.printStackTrace(); + } + } + } + connection.id = message.connectionId; ConnectionDetails connectionDetails = message.connectionDetails; /* Get any parameters from connectionDetails. */ From 201f3884f0a33b550d8ed94e9cc39eefdbf699a6 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Fri, 21 Oct 2022 10:55:54 +0200 Subject: [PATCH 40/67] Refactor sending pending messages when client is connected and remove them on send success per spec RTN19a --- .../ably/lib/transport/ConnectionManager.java | 42 ++++++++++++++----- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java index c1056fc6d..d5d15461a 100644 --- a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -1107,17 +1107,6 @@ private synchronized void onConnected(ProtocolMessage message) { channels.reAttach(); } - //RTN19a - if (pendingMessages.queue != null && !pendingMessages.queue.isEmpty()) { - for (QueuedMessage queuedMessage : pendingMessages.queue) { - try { - send(queuedMessage.msg, false, null); - } catch (AblyException e) { - e.printStackTrace(); - } - } - } - connection.id = message.connectionId; ConnectionDetails connectionDetails = message.connectionDetails; /* Get any parameters from connectionDetails. */ @@ -1134,6 +1123,9 @@ private synchronized void onConnected(ProtocolMessage message) { return; } + //RTN19a + sendPendingQueueMessages(); + /* indicated connected currentState */ setSuspendTime(); requestState(new StateIndication(ConnectionState.connected, error)); @@ -1437,6 +1429,34 @@ public QueuedMessage(ProtocolMessage msg, CompletionListener listener) { } } + /** + * Send all pending messages which are queue. + * Remove them from the queue once they are send successfully + * Spec: RTN19a + */ + public void sendPendingQueueMessages() { + //RTN19a + if (pendingMessages.queue != null && !pendingMessages.queue.isEmpty()) { + for (final QueuedMessage queuedMessage : pendingMessages.queue) { + try { + send(queuedMessage.msg, false, new CompletionListener() { + @Override + public void onSuccess() { + pendingMessages.queue.remove(queuedMessage); + } + + @Override + public void onError(ErrorInfo reason) { + Log.d(TAG, "Unable to send pending message - " + reason.message); + } + }); + } catch (AblyException e) { + e.printStackTrace(); + } + } + } + } + public void send(ProtocolMessage msg, boolean queueEvents, CompletionListener listener) throws AblyException { State state; synchronized(this) { From 86625b85409b7a78960b9680e299733e09dec90b Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Fri, 21 Oct 2022 14:56:13 +0200 Subject: [PATCH 41/67] Refactor presence map manipulation by spec RTP17h --- .../java/io/ably/lib/realtime/Presence.java | 55 ++++++++++++++----- 1 file changed, 41 insertions(+), 14 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/Presence.java b/core/src/main/java/io/ably/lib/realtime/Presence.java index 7d83ee743..45147ce58 100644 --- a/core/src/main/java/io/ably/lib/realtime/Presence.java +++ b/core/src/main/java/io/ably/lib/realtime/Presence.java @@ -1,5 +1,17 @@ package io.ably.lib.realtime; +import java.util.ArrayList; +import java.util.Collection; +import java.util.EnumMap; +import java.util.EnumSet; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; + import io.ably.lib.http.BasePaginatedQuery; import io.ably.lib.http.HttpCore; import io.ably.lib.http.HttpUtils; @@ -14,17 +26,6 @@ import io.ably.lib.types.PresenceSerializer; import io.ably.lib.types.ProtocolMessage; import io.ably.lib.util.Log; -import java.util.ArrayList; -import java.util.Collection; -import java.util.EnumMap; -import java.util.EnumSet; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; /** * A class that provides access to presence operations and state for the @@ -802,6 +803,14 @@ void setSuspended(ErrorInfo reason) { */ private class PresenceMap { + public PresenceMap() { + this.keyByClientID = false; + } + + public PresenceMap(boolean keyByClientID) { + this.keyByClientID = keyByClientID; + } + /** * Wait for sync to be complete. If we are in attaching state wait for initial sync to * complete as well. Return false if wait was interrupted because channel transitioned to @@ -881,7 +890,7 @@ synchronized Collection get(Param[] params) throws AblyExceptio * false if the message is already superseded */ synchronized boolean put(PresenceMessage item) { - String key = item.clientId; //RTP17h + String key = memberKey(item); /* we've seen this member, so do not remove it at the end of sync */ if(residualMembers != null) residualMembers.remove(key); @@ -976,7 +985,7 @@ synchronized Collection values(boolean wait) throws AblyExcepti * @return */ synchronized boolean remove(PresenceMessage item) { - String key = item.clientId; //RTP17h + String key = memberKey(item); if (hasNewerItem(key, item)) return false; PresenceMessage existingItem = members.remove(key); @@ -1055,13 +1064,31 @@ synchronized void reEnter() { } } + /** + * Get the member key for the PresenceMessage. + * Spec: RTP17h + * @return key of the presence message + */ + public String memberKey(PresenceMessage item) { + if (this.keyByClientID) { + return item.clientId; + } else { + return item.connectionId + ':' + item.clientId; + } + } + private boolean syncInProgress; private Collection residualMembers; private final HashMap members = new HashMap(); + /** + * Used for differentiating between main and internal presence map + * Spec: RTP17h + */ + private boolean keyByClientID; } private final PresenceMap presence = new PresenceMap(); - private final PresenceMap internalPresence = new PresenceMap(); + private final PresenceMap internalPresence = new PresenceMap(true); /************************************ * general From fc543d086ab948802c1cf60304a7902d3f49052d Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Fri, 21 Oct 2022 16:14:27 +0200 Subject: [PATCH 42/67] Ignore bulk publish tests as they are not supported in protocol 2.0 --- .../java/io/ably/lib/test/rest/RestChannelBulkPublishTest.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/test/java/io/ably/lib/test/rest/RestChannelBulkPublishTest.java b/core/src/test/java/io/ably/lib/test/rest/RestChannelBulkPublishTest.java index d59095bbf..2255f9fcf 100644 --- a/core/src/test/java/io/ably/lib/test/rest/RestChannelBulkPublishTest.java +++ b/core/src/test/java/io/ably/lib/test/rest/RestChannelBulkPublishTest.java @@ -44,6 +44,7 @@ public abstract class RestChannelBulkPublishTest extends ParameterizedTest { * * It publishes the given message on all of the given channels. */ + @Ignore("Fix bulk publish in protocol 2.0") @Test public void bulk_publish_multiple_channels_simple() { try { @@ -87,6 +88,7 @@ public void bulk_publish_multiple_channels_simple() { /** * As above but with the param method */ + @Ignore("Fix bulk publish in protocol 2.0") @Test public void bulk_publish_multiple_channels_param() { AblyRealtimeBase rxAbly = null; @@ -169,6 +171,7 @@ public void bulk_publish_multiple_channels_param() { * * It publishes the given messages on the associated channels. */ + @Ignore("Fix bulk publish in protocol 2.0") @Test public void bulk_publish_multiple_channels_multiple_messages() { try { From b4eaadd3bc613535f3862a78850e5ae8f7844c30 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Fri, 21 Oct 2022 16:22:09 +0200 Subject: [PATCH 43/67] Remove public modifier from PresenceMap --- core/src/main/java/io/ably/lib/realtime/Presence.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/Presence.java b/core/src/main/java/io/ably/lib/realtime/Presence.java index 45147ce58..01336b412 100644 --- a/core/src/main/java/io/ably/lib/realtime/Presence.java +++ b/core/src/main/java/io/ably/lib/realtime/Presence.java @@ -803,11 +803,11 @@ void setSuspended(ErrorInfo reason) { */ private class PresenceMap { - public PresenceMap() { + PresenceMap() { this.keyByClientID = false; } - public PresenceMap(boolean keyByClientID) { + PresenceMap(boolean keyByClientID) { this.keyByClientID = keyByClientID; } From e91d7704f919fbebe2a371bea229ffbc93c796e0 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Tue, 25 Oct 2022 09:12:21 +0200 Subject: [PATCH 44/67] Improve re-enter by spec RTP17f --- core/src/main/java/io/ably/lib/realtime/Presence.java | 4 ++-- .../main/java/io/ably/lib/realtime/RealtimeChannelBase.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/Presence.java b/core/src/main/java/io/ably/lib/realtime/Presence.java index 01336b412..3b503f827 100644 --- a/core/src/main/java/io/ably/lib/realtime/Presence.java +++ b/core/src/main/java/io/ably/lib/realtime/Presence.java @@ -299,8 +299,8 @@ public void onError(ErrorInfo reason) { /** * Spec: RTP17f, RTP17g */ - public void reEnter(ChannelState newState) { - if (newState == ChannelState.attached && channel.state != ChannelState.attached) { + public void reEnter(ChannelState newState, ChannelState previousState) { + if (newState == ChannelState.attached && previousState != ChannelState.attached) { internalPresence.reEnter(); } } diff --git a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java index 01fcf4fcc..6ba09deee 100644 --- a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java +++ b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java @@ -102,7 +102,7 @@ private void setState(ChannelState newState, ErrorInfo reason, boolean resumed, properties.channelSerial = null; //RTP5a1 } - presence.reEnter(newState); //RTP17f + presence.reEnter(stateChange.current, stateChange.previous); //RTP17f if (notifyStateChange) { /* broadcast state change */ From a6eeb001531505e02bd5149c2c7864d951d3c203 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Tue, 25 Oct 2022 10:59:26 +0200 Subject: [PATCH 45/67] Improve reattach of the channels on every connected message --- .../main/java/io/ably/lib/transport/ConnectionManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java index d5d15461a..2924ebcf1 100644 --- a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -1098,15 +1098,15 @@ private synchronized void onConnected(ProtocolMessage message) { //RTN15c6 Log.d(TAG, "connection has reconnected and resumed successfully"); connection.reason = null; - channels.reAttach(); } else if (!message.connectionId.equals(connection.id) && error != null) { //RTN15c7 Log.d(TAG, "connection resume is invalid: " + error.message); connection.reason = error; msgSerial = 0; - channels.reAttach(); } + channels.reAttach(); + connection.id = message.connectionId; ConnectionDetails connectionDetails = message.connectionDetails; /* Get any parameters from connectionDetails. */ From 4591971018e978b414b8f4edcf231d5f89ce180f Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Tue, 25 Oct 2022 11:04:59 +0200 Subject: [PATCH 46/67] Improve re-enter on attached message by spec RTP17f --- core/src/main/java/io/ably/lib/realtime/Presence.java | 6 ++---- .../main/java/io/ably/lib/realtime/RealtimeChannelBase.java | 3 +-- 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/Presence.java b/core/src/main/java/io/ably/lib/realtime/Presence.java index 3b503f827..c42c9f1d6 100644 --- a/core/src/main/java/io/ably/lib/realtime/Presence.java +++ b/core/src/main/java/io/ably/lib/realtime/Presence.java @@ -299,10 +299,8 @@ public void onError(ErrorInfo reason) { /** * Spec: RTP17f, RTP17g */ - public void reEnter(ChannelState newState, ChannelState previousState) { - if (newState == ChannelState.attached && previousState != ChannelState.attached) { - internalPresence.reEnter(); - } + public void reEnter() { + internalPresence.reEnter(); } void setPresence(PresenceMessage[] messages, boolean broadcast, String syncChannelSerial) { diff --git a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java index 6ba09deee..71aa8eaa1 100644 --- a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java +++ b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java @@ -102,8 +102,6 @@ private void setState(ChannelState newState, ErrorInfo reason, boolean resumed, properties.channelSerial = null; //RTP5a1 } - presence.reEnter(stateChange.current, stateChange.previous); //RTP17f - if (notifyStateChange) { /* broadcast state change */ emit(newState, stateChange); @@ -319,6 +317,7 @@ private void setAttached(ProtocolMessage message) { } else { this.attachResume = true; setState(ChannelState.attached, message.error, resumed); + presence.reEnter(); //RTP17f sendQueuedMessages(); presence.setAttached(message.hasFlag(Flag.has_presence)); } From 5fae005bdb9ace00c18099a147e315be582ab00c Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Tue, 25 Oct 2022 13:10:46 +0200 Subject: [PATCH 47/67] Fix removing sending pending messages by spec RTN19a --- .../io/ably/lib/transport/ConnectionManager.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java index 2924ebcf1..c8c111342 100644 --- a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -1439,17 +1439,7 @@ public void sendPendingQueueMessages() { if (pendingMessages.queue != null && !pendingMessages.queue.isEmpty()) { for (final QueuedMessage queuedMessage : pendingMessages.queue) { try { - send(queuedMessage.msg, false, new CompletionListener() { - @Override - public void onSuccess() { - pendingMessages.queue.remove(queuedMessage); - } - - @Override - public void onError(ErrorInfo reason) { - Log.d(TAG, "Unable to send pending message - " + reason.message); - } - }); + send(queuedMessage.msg, false, null); } catch (AblyException e) { e.printStackTrace(); } From d1e262d9b05ed2245cba45a90f720d591bece8d4 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Tue, 25 Oct 2022 13:11:05 +0200 Subject: [PATCH 48/67] Fix test for attached channel --- .../java/io/ably/lib/test/realtime/ConnectionManagerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/java/io/ably/lib/test/realtime/ConnectionManagerTest.java b/core/src/test/java/io/ably/lib/test/realtime/ConnectionManagerTest.java index 923a4836c..507010fde 100644 --- a/core/src/test/java/io/ably/lib/test/realtime/ConnectionManagerTest.java +++ b/core/src/test/java/io/ably/lib/test/realtime/ConnectionManagerTest.java @@ -649,7 +649,7 @@ public void channels_are_reattached_after_reconnecting_when_statettl_plus_idlein /* We want this greater than newTtl + newIdleInterval */ final long waitInDisconnectedState = 3000L; final List attachedChannelHistory = new ArrayList(); - final List expectedAttachedChannelHistory = Arrays.asList("attaching", "attached", "detached", "attaching", "attached"); + final List expectedAttachedChannelHistory = Arrays.asList("attaching", "attached", "attaching", "attached"); final List suspendedChannelHistory = new ArrayList(); final List expectedSuspendedChannelHistory = Arrays.asList("attaching", "attached"); From 4dd11ee1609ce6e281823059105900789b89d4b5 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Wed, 26 Oct 2022 10:41:30 +0200 Subject: [PATCH 49/67] Fix test for presence enter --- .../lib/test/realtime/RealtimePresenceTest.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/core/src/test/java/io/ably/lib/test/realtime/RealtimePresenceTest.java b/core/src/test/java/io/ably/lib/test/realtime/RealtimePresenceTest.java index d980a9714..5aff8e6d7 100644 --- a/core/src/test/java/io/ably/lib/test/realtime/RealtimePresenceTest.java +++ b/core/src/test/java/io/ably/lib/test/realtime/RealtimePresenceTest.java @@ -2194,10 +2194,7 @@ public void onPresenceMessage(PresenceMessage message) { channelWaiter.waitFor(ChannelState.attached); long reconnectTimestamp = System.currentTimeMillis(); - try { - Thread.sleep(500); - } catch (InterruptedException e) { - } + try {Thread.sleep(500);} catch (InterruptedException e) {} AblyBase ablyRest = createAblyRest(opts); RestChannelBase restChannel = ablyRest.channels.get(channelName); @@ -3068,16 +3065,19 @@ public boolean matches(ProtocolMessage message) { sentPresence.get(0).clientId == null ); + //Wait for channel to reach attached state before detaching + try {Thread.sleep(500L);} catch(InterruptedException e) {} + channel.detach(); new ChannelWaiter(channel).waitFor(ChannelState.detached); try { + assertEquals("Verify if channel is detached", ChannelState.detached, channel.state); channel.presence.enter(null, null); fail("Presence.enter() shouldn't succeed in detached state"); } catch (AblyException e) { assertEquals("Verify exception error code", e.errorInfo.code, 91001 /* unable to enter presence channel (invalid channel state) */); } - } finally { if (ably != null) ably.close(); @@ -3132,16 +3132,19 @@ public boolean matches(ProtocolMessage message) { sentPresence.get(0).clientId.equals(testClientId2) ); + //Wait for channel to reach attached state before detaching + try {Thread.sleep(500L);} catch(InterruptedException e) {} + channel.detach(); new ChannelWaiter(channel).waitFor(ChannelState.detached); try { + assertEquals("Verify if channel is detached", ChannelState.detached, channel.state); channel.presence.enterClient("testClient3"); fail("Presence.enterClient() shouldn't succeed in detached state"); } catch (AblyException e) { assertEquals("Verify exception error code", e.errorInfo.code, 91001 /* unable to enter presence channel (invalid channel state) */); } - } finally { if (ably != null) ably.close(); From 52e06b36abdbfcdb71d738e398e5a7f240f39908 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Thu, 27 Oct 2022 16:40:52 +0200 Subject: [PATCH 50/67] Add spec RTL5k to fix channel attaching on detaching or detached state --- .../main/java/io/ably/lib/realtime/RealtimeChannelBase.java | 4 ++++ .../io/ably/lib/test/realtime/RealtimePresenceTest.java | 6 ------ 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java index 71aa8eaa1..4bf64664a 100644 --- a/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java +++ b/core/src/main/java/io/ably/lib/realtime/RealtimeChannelBase.java @@ -314,6 +314,10 @@ private void setAttached(ProtocolMessage message) { Log.v(TAG, String.format(Locale.ROOT, "Server initiated attach for channel %s", name)); /* emit UPDATE event according to RTL12 */ emitUpdate(null, resumed); + } else if (state == ChannelState.detaching || state == ChannelState.detached) { + //RTL5k + Log.v(TAG, "setAttached(): channel is in detaching state so no need to attach it!"); + setDetached((message.error != null) ? message.error : REASON_NOT_ATTACHED); } else { this.attachResume = true; setState(ChannelState.attached, message.error, resumed); diff --git a/core/src/test/java/io/ably/lib/test/realtime/RealtimePresenceTest.java b/core/src/test/java/io/ably/lib/test/realtime/RealtimePresenceTest.java index 5aff8e6d7..f687f4b3d 100644 --- a/core/src/test/java/io/ably/lib/test/realtime/RealtimePresenceTest.java +++ b/core/src/test/java/io/ably/lib/test/realtime/RealtimePresenceTest.java @@ -3065,9 +3065,6 @@ public boolean matches(ProtocolMessage message) { sentPresence.get(0).clientId == null ); - //Wait for channel to reach attached state before detaching - try {Thread.sleep(500L);} catch(InterruptedException e) {} - channel.detach(); new ChannelWaiter(channel).waitFor(ChannelState.detached); @@ -3132,9 +3129,6 @@ public boolean matches(ProtocolMessage message) { sentPresence.get(0).clientId.equals(testClientId2) ); - //Wait for channel to reach attached state before detaching - try {Thread.sleep(500L);} catch(InterruptedException e) {} - channel.detach(); new ChannelWaiter(channel).waitFor(ChannelState.detached); From 90dd27588b07f5f324b2e8ea8063f059b28c58f6 Mon Sep 17 00:00:00 2001 From: Simon Woolf Date: Mon, 31 Oct 2022 18:49:34 +0000 Subject: [PATCH 51/67] Rename getRecoveryKey->createRecoveryKey per updated spec --- core/src/main/java/io/ably/lib/realtime/Connection.java | 2 +- .../java/io/ably/lib/test/realtime/RealtimeRecoverTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/Connection.java b/core/src/main/java/io/ably/lib/realtime/Connection.java index 945b6808d..c0297b87e 100644 --- a/core/src/main/java/io/ably/lib/realtime/Connection.java +++ b/core/src/main/java/io/ably/lib/realtime/Connection.java @@ -98,7 +98,7 @@ public void emitUpdate(ErrorInfo errorInfo) { * @return a json string which incorporates the @connectionKey@, the current @msgSerial@, * and a collection of pairs of channel @name@ and current @channelSerial@ for every currently attached channel. */ - public String getRecoveryKey() { + public String createRecoveryKey() { if (key == null || connectionManager == null || connectionManager.getConnectionState() == null || connectionManager.getConnectionState().state == ConnectionState.closed || connectionManager.getConnectionState().state == ConnectionState.closing || diff --git a/core/src/test/java/io/ably/lib/test/realtime/RealtimeRecoverTest.java b/core/src/test/java/io/ably/lib/test/realtime/RealtimeRecoverTest.java index 2f788f807..8b6f3229f 100644 --- a/core/src/test/java/io/ably/lib/test/realtime/RealtimeRecoverTest.java +++ b/core/src/test/java/io/ably/lib/test/realtime/RealtimeRecoverTest.java @@ -89,7 +89,7 @@ public void recover_disconnected() { * NOTE this depends on knowledge of the internal structure * of the library, to simulate a dropped transport without * causing the connection itself to be disposed */ - String recoverConnectionKey = ablyRx.connection.getRecoveryKey(); + String recoverConnectionKey = ablyRx.connection.createRecoveryKey(); ablyRx.connection.connectionManager.requestState(ConnectionState.failed); /* wait */ @@ -189,7 +189,7 @@ public void recover_implicit_connect() { * NOTE this depends on knowledge of the internal structure * of the library, to simulate a dropped transport without * causing the connection itself to be disposed */ - String recoverConnectionKey = ablyRx.connection.getRecoveryKey(); + String recoverConnectionKey = ablyRx.connection.createRecoveryKey(); ablyRx.connection.connectionManager.requestState(ConnectionState.failed); /* wait */ From a6fd26db29b23e8b0900304210a8719451c0186c Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Tue, 1 Nov 2022 09:49:17 +0100 Subject: [PATCH 52/67] Add spec RTL5k to fix channel attaching on detaching or detached state --- .../java/io/ably/lib/realtime/Presence.java | 30 +++++++------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/Presence.java b/core/src/main/java/io/ably/lib/realtime/Presence.java index c42c9f1d6..23ea86564 100644 --- a/core/src/main/java/io/ably/lib/realtime/Presence.java +++ b/core/src/main/java/io/ably/lib/realtime/Presence.java @@ -800,15 +800,6 @@ void setSuspended(ErrorInfo reason) { * */ private class PresenceMap { - - PresenceMap() { - this.keyByClientID = false; - } - - PresenceMap(boolean keyByClientID) { - this.keyByClientID = keyByClientID; - } - /** * Wait for sync to be complete. If we are in attaching state wait for initial sync to * complete as well. Return false if wait was interrupted because channel transitioned to @@ -1068,25 +1059,24 @@ synchronized void reEnter() { * @return key of the presence message */ public String memberKey(PresenceMessage item) { - if (this.keyByClientID) { - return item.clientId; - } else { - return item.connectionId + ':' + item.clientId; - } + return item.connectionId + ':' + item.clientId; } private boolean syncInProgress; private Collection residualMembers; private final HashMap members = new HashMap(); - /** - * Used for differentiating between main and internal presence map - * Spec: RTP17h - */ - private boolean keyByClientID; + } + + // RTP17h + private class InternalPresenceMap extends PresenceMap { + @Override + public String memberKey(PresenceMessage item) { + return item.clientId; + } } private final PresenceMap presence = new PresenceMap(); - private final PresenceMap internalPresence = new PresenceMap(true); + private final PresenceMap internalPresence = new InternalPresenceMap(); /************************************ * general From 0fa91f7b15c2d96049298146cc6bd17c69dd49dd Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Tue, 1 Nov 2022 09:49:17 +0100 Subject: [PATCH 53/67] Refactor internal map by spec RTP17h --- .../java/io/ably/lib/realtime/Presence.java | 30 +++++++------------ 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/Presence.java b/core/src/main/java/io/ably/lib/realtime/Presence.java index c42c9f1d6..23ea86564 100644 --- a/core/src/main/java/io/ably/lib/realtime/Presence.java +++ b/core/src/main/java/io/ably/lib/realtime/Presence.java @@ -800,15 +800,6 @@ void setSuspended(ErrorInfo reason) { * */ private class PresenceMap { - - PresenceMap() { - this.keyByClientID = false; - } - - PresenceMap(boolean keyByClientID) { - this.keyByClientID = keyByClientID; - } - /** * Wait for sync to be complete. If we are in attaching state wait for initial sync to * complete as well. Return false if wait was interrupted because channel transitioned to @@ -1068,25 +1059,24 @@ synchronized void reEnter() { * @return key of the presence message */ public String memberKey(PresenceMessage item) { - if (this.keyByClientID) { - return item.clientId; - } else { - return item.connectionId + ':' + item.clientId; - } + return item.connectionId + ':' + item.clientId; } private boolean syncInProgress; private Collection residualMembers; private final HashMap members = new HashMap(); - /** - * Used for differentiating between main and internal presence map - * Spec: RTP17h - */ - private boolean keyByClientID; + } + + // RTP17h + private class InternalPresenceMap extends PresenceMap { + @Override + public String memberKey(PresenceMessage item) { + return item.clientId; + } } private final PresenceMap presence = new PresenceMap(); - private final PresenceMap internalPresence = new PresenceMap(true); + private final PresenceMap internalPresence = new InternalPresenceMap(); /************************************ * general From 52884b50330a04e5fef0f8343a84e57cdb02dcfb Mon Sep 17 00:00:00 2001 From: Simon Woolf Date: Mon, 31 Oct 2022 18:49:34 +0000 Subject: [PATCH 54/67] Rename getRecoveryKey->createRecoveryKey per updated spec --- core/src/main/java/io/ably/lib/realtime/Connection.java | 2 +- .../java/io/ably/lib/test/realtime/RealtimeRecoverTest.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/Connection.java b/core/src/main/java/io/ably/lib/realtime/Connection.java index 945b6808d..c0297b87e 100644 --- a/core/src/main/java/io/ably/lib/realtime/Connection.java +++ b/core/src/main/java/io/ably/lib/realtime/Connection.java @@ -98,7 +98,7 @@ public void emitUpdate(ErrorInfo errorInfo) { * @return a json string which incorporates the @connectionKey@, the current @msgSerial@, * and a collection of pairs of channel @name@ and current @channelSerial@ for every currently attached channel. */ - public String getRecoveryKey() { + public String createRecoveryKey() { if (key == null || connectionManager == null || connectionManager.getConnectionState() == null || connectionManager.getConnectionState().state == ConnectionState.closed || connectionManager.getConnectionState().state == ConnectionState.closing || diff --git a/core/src/test/java/io/ably/lib/test/realtime/RealtimeRecoverTest.java b/core/src/test/java/io/ably/lib/test/realtime/RealtimeRecoverTest.java index 2f788f807..8b6f3229f 100644 --- a/core/src/test/java/io/ably/lib/test/realtime/RealtimeRecoverTest.java +++ b/core/src/test/java/io/ably/lib/test/realtime/RealtimeRecoverTest.java @@ -89,7 +89,7 @@ public void recover_disconnected() { * NOTE this depends on knowledge of the internal structure * of the library, to simulate a dropped transport without * causing the connection itself to be disposed */ - String recoverConnectionKey = ablyRx.connection.getRecoveryKey(); + String recoverConnectionKey = ablyRx.connection.createRecoveryKey(); ablyRx.connection.connectionManager.requestState(ConnectionState.failed); /* wait */ @@ -189,7 +189,7 @@ public void recover_implicit_connect() { * NOTE this depends on knowledge of the internal structure * of the library, to simulate a dropped transport without * causing the connection itself to be disposed */ - String recoverConnectionKey = ablyRx.connection.getRecoveryKey(); + String recoverConnectionKey = ablyRx.connection.createRecoveryKey(); ablyRx.connection.connectionManager.requestState(ConnectionState.failed); /* wait */ From 28f438ae58be9b214956eb426c027004ecff79ac Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Wed, 2 Nov 2022 13:22:12 +0100 Subject: [PATCH 55/67] Rename entry of map and serials iterator Change visibility modifier to private for sendPendingQueueMessages --- .../java/io/ably/lib/realtime/AblyRealtimeBase.java | 10 +++++----- .../java/io/ably/lib/transport/ConnectionManager.java | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java index 1f32e86cd..2e7397158 100644 --- a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java +++ b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java @@ -80,11 +80,11 @@ public void onConnectionStateChanged(ConnectionStateListener.ConnectionStateChan } else { connection.connectionManager.msgSerial = recoveryKey.msgSerial; //RTN16f - for (Map.Entry entry : recoveryKey.serials.entrySet()) { + for (Map.Entry serial : recoveryKey.serials.entrySet()) { //RTN16j - RealtimeChannelBase channel = channels.get(entry.getKey()); + RealtimeChannelBase channel = channels.get(serial.getKey()); if (channel != null) { - channel.properties.channelSerial = entry.getValue(); //RTN16i + channel.properties.channelSerial = serial.getValue(); //RTN16i } } } @@ -213,8 +213,8 @@ public void suspendAll(ErrorInfo error, boolean notifyStateChange) { */ @Override public void reAttach() { - for (Map.Entry entry : map.entrySet()) { - RealtimeChannelBase channel = entry.getValue(); + for (Map.Entry channelEntry : map.entrySet()) { + RealtimeChannelBase channel = channelEntry.getValue(); if (channel.state == ChannelState.attaching || channel.state == ChannelState.attached || channel.state == ChannelState.suspended) { Log.d(TAG, "reAttach(); channel = " + channel.name); channel.state = ChannelState.attaching; diff --git a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java index c8c111342..ad26019c0 100644 --- a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -1434,7 +1434,7 @@ public QueuedMessage(ProtocolMessage msg, CompletionListener listener) { * Remove them from the queue once they are send successfully * Spec: RTN19a */ - public void sendPendingQueueMessages() { + private void sendPendingQueueMessages() { //RTN19a if (pendingMessages.queue != null && !pendingMessages.queue.isEmpty()) { for (final QueuedMessage queuedMessage : pendingMessages.queue) { From 18415fc1b0351392c71e6b41f3d23d055dc3ff63 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Fri, 4 Nov 2022 12:10:43 +0100 Subject: [PATCH 56/67] Add encapsulation for ConnectionRecoveryKey --- .../ably/lib/realtime/AblyRealtimeBase.java | 4 +-- .../java/io/ably/lib/realtime/Connection.java | 6 ++-- .../lib/realtime/ConnectionRecoveryKey.java | 32 +++++++++++++++++-- .../io/ably/lib/transport/ITransport.java | 4 +-- .../realtime/RealtimeConnectFailTest.java | 9 +++--- 5 files changed, 39 insertions(+), 16 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java index 2e7397158..c958a5cbc 100644 --- a/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java +++ b/core/src/main/java/io/ably/lib/realtime/AblyRealtimeBase.java @@ -78,9 +78,9 @@ public void onConnectionStateChanged(ConnectionStateListener.ConnectionStateChan Log.d(TAG, "Recovery key initialization failed!"); connection.connectionManager.msgSerial = 0; //RTN16f } else { - connection.connectionManager.msgSerial = recoveryKey.msgSerial; //RTN16f + connection.connectionManager.msgSerial = recoveryKey.getMsgSerial(); //RTN16f - for (Map.Entry serial : recoveryKey.serials.entrySet()) { + for (Map.Entry serial : recoveryKey.getSerials().entrySet()) { //RTN16j RealtimeChannelBase channel = channels.get(serial.getKey()); if (channel != null) { diff --git a/core/src/main/java/io/ably/lib/realtime/Connection.java b/core/src/main/java/io/ably/lib/realtime/Connection.java index c0297b87e..65da3144e 100644 --- a/core/src/main/java/io/ably/lib/realtime/Connection.java +++ b/core/src/main/java/io/ably/lib/realtime/Connection.java @@ -109,14 +109,12 @@ public String createRecoveryKey() { return null; } - ConnectionRecoveryKey recoveryKey = new ConnectionRecoveryKey(); - recoveryKey.connectionKey = key; - recoveryKey.msgSerial = connectionManager.msgSerial; + ConnectionRecoveryKey recoveryKey = new ConnectionRecoveryKey(key, connectionManager.msgSerial); for (Object channel : ably.channels.values()) { if (channel instanceof RealtimeChannelBase) { RealtimeChannelBase rcb = (RealtimeChannelBase) channel; - recoveryKey.serials.put(rcb.name, rcb.properties.channelSerial); + recoveryKey.addSerials(rcb.name, rcb.properties.channelSerial); } } diff --git a/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java b/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java index 41e706b4a..b22215283 100644 --- a/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java +++ b/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java @@ -9,14 +9,40 @@ public class ConnectionRecoveryKey { - public String connectionKey; - public long msgSerial; + private final String connectionKey; + private final long msgSerial; /** * Key - channel name *

* Value - channelSerial */ - public Map serials = new HashMap<>(); + private final Map serials = new HashMap<>(); + + public ConnectionRecoveryKey(String connectionKey, long msgSerial) { + this.connectionKey = connectionKey; + this.msgSerial = msgSerial; + } + + public String getConnectionKey() { + return connectionKey; + } + + public long getMsgSerial() { + return msgSerial; + } + + public Map getSerials() { + return serials; + } + + public void setSerials(Map serials) { + this.serials.clear(); + this.serials.putAll(serials); + } + + public void addSerials(String channelName, String channelSerial) { + this.serials.put(channelName, channelSerial); + } public String asJson() { return Serialisation.gson.toJson(this); diff --git a/core/src/main/java/io/ably/lib/transport/ITransport.java b/core/src/main/java/io/ably/lib/transport/ITransport.java index 5c1ef5e05..8bec53a3a 100644 --- a/core/src/main/java/io/ably/lib/transport/ITransport.java +++ b/core/src/main/java/io/ably/lib/transport/ITransport.java @@ -72,8 +72,8 @@ public Param[] getConnectParams(Param[] baseParams) { } else if(options.recover != null) { //RTN16k mode = Mode.recover; ConnectionRecoveryKey recoveryKey = ConnectionRecoveryKey.fromJson(options.recover); - if (recoveryKey != null && recoveryKey.connectionKey != null) { - paramList.add(new Param("recover", recoveryKey.connectionKey)); + if (recoveryKey != null && recoveryKey.getConnectionKey() != null) { + paramList.add(new Param("recover", recoveryKey.getConnectionKey())); } else { Log.e(TAG, "Invalid recover string specified"); } diff --git a/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java b/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java index 778169293..fa528133e 100644 --- a/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java +++ b/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java @@ -26,6 +26,7 @@ import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -337,10 +338,8 @@ public void connect_unknown_recover_fail() { try { ClientOptions opts = createOptions(testVars.keys[0].keyStr); - ConnectionRecoveryKey recovery = new ConnectionRecoveryKey(); - recovery.connectionKey = "0123456789abcdef-99"; - recovery.msgSerial = 0; - recovery.serials.put("name","0"); + ConnectionRecoveryKey recovery = new ConnectionRecoveryKey("0123456789abcdef-99", 0); + recovery.addSerials("name","0"); opts.recover = recovery.asJson(); ably = createAblyRealtime(opts); @@ -350,7 +349,7 @@ public void connect_unknown_recover_fail() { assertEquals("Verify connected state is reached", ConnectionState.connected, ably.connection.state); assertNotNull("Verify error is returned", connectedError); assertEquals("Verify correct error code is given", 80018, connectedError.code); - assertNotEquals("Verify new connection id is assigned", recovery.connectionKey, ably.connection.key); + assertNotEquals("Verify new connection id is assigned", recovery.getConnectionKey(), ably.connection.key); } catch (AblyException e) { e.printStackTrace(); fail("init0: Unexpected exception instantiating library"); From ee0dcd7e028c4750850b8713963c27d6b6419758 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Fri, 4 Nov 2022 12:15:51 +0100 Subject: [PATCH 57/67] Remove unused import --- .../java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java b/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java index fa528133e..8761773ce 100644 --- a/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java +++ b/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java @@ -26,7 +26,6 @@ import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Arrays; -import java.util.HashMap; import java.util.List; import java.util.Locale; From 5b9990767d0b5c76aa69ca56f52e425d14714f1f Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Fri, 4 Nov 2022 16:16:52 +0100 Subject: [PATCH 58/67] Add error logs instead of printing stack trace --- .../java/io/ably/lib/realtime/ConnectionRecoveryKey.java | 4 +++- core/src/main/java/io/ably/lib/realtime/Presence.java | 4 +++- .../main/java/io/ably/lib/transport/ConnectionManager.java | 6 ++++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java b/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java index b22215283..e43a1fb6a 100644 --- a/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java +++ b/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java @@ -5,9 +5,11 @@ import java.util.HashMap; import java.util.Map; +import io.ably.lib.util.Log; import io.ably.lib.util.Serialisation; public class ConnectionRecoveryKey { + private static final String TAG = "RecoveryKey"; private final String connectionKey; private final long msgSerial; @@ -52,7 +54,7 @@ public static ConnectionRecoveryKey fromJson(String json) { try { return Serialisation.gson.fromJson(json, ConnectionRecoveryKey.class); } catch (JsonSyntaxException e) { - e.printStackTrace(); + Log.e(TAG, "Cannot create recovery key from json: " + e.getMessage()); return null; } } diff --git a/core/src/main/java/io/ably/lib/realtime/Presence.java b/core/src/main/java/io/ably/lib/realtime/Presence.java index 23ea86564..572c71d57 100644 --- a/core/src/main/java/io/ably/lib/realtime/Presence.java +++ b/core/src/main/java/io/ably/lib/realtime/Presence.java @@ -1048,7 +1048,9 @@ synchronized void reEnter() { try { updatePresence(member, null); } catch (AblyException e) { - e.printStackTrace(); + String errorString = String.format(Locale.ROOT, "Cannot automatically re-enter %s on channel %s (%s)", + member.clientId, channel.name, e.errorInfo.message); + Log.e(TAG, errorString); } } } diff --git a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java index ad26019c0..c4a908bde 100644 --- a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -1088,7 +1088,7 @@ private void onChannelMessage(ProtocolMessage message) { } /** - * Handle {@link ProtocolMessage.Action.connected} messages + * Handle {@link ProtocolMessage.Action#connected} messages * @param message a ProtocolMessage object */ private synchronized void onConnected(ProtocolMessage message) { @@ -1441,7 +1441,9 @@ private void sendPendingQueueMessages() { try { send(queuedMessage.msg, false, null); } catch (AblyException e) { - e.printStackTrace(); + String errorString = String.format(Locale.ROOT, "Unable to send pending message %s (%s)", + queuedMessage.msg.id, e.errorInfo.message); + Log.e(TAG, errorString); } } } From d022c3d5013d993f5c0bd49a0ec7a2c53e38fc46 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Mon, 7 Nov 2022 09:24:47 +0100 Subject: [PATCH 59/67] Emit update on re-attach error --- core/src/main/java/io/ably/lib/realtime/Presence.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/io/ably/lib/realtime/Presence.java b/core/src/main/java/io/ably/lib/realtime/Presence.java index 572c71d57..ade10bea6 100644 --- a/core/src/main/java/io/ably/lib/realtime/Presence.java +++ b/core/src/main/java/io/ably/lib/realtime/Presence.java @@ -1051,6 +1051,7 @@ synchronized void reEnter() { String errorString = String.format(Locale.ROOT, "Cannot automatically re-enter %s on channel %s (%s)", member.clientId, channel.name, e.errorInfo.message); Log.e(TAG, errorString); + channel.emitUpdate(new ErrorInfo(errorString, 91004), true); } } } From d7ed41a600e1c15ce435c7ceb947c2aa4efaf7f0 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Mon, 7 Nov 2022 09:29:16 +0100 Subject: [PATCH 60/67] Add error info to test fail log --- .../java/io/ably/lib/test/realtime/ConnectionManagerTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/test/java/io/ably/lib/test/realtime/ConnectionManagerTest.java b/core/src/test/java/io/ably/lib/test/realtime/ConnectionManagerTest.java index 507010fde..42ce8648d 100644 --- a/core/src/test/java/io/ably/lib/test/realtime/ConnectionManagerTest.java +++ b/core/src/test/java/io/ably/lib/test/realtime/ConnectionManagerTest.java @@ -740,7 +740,7 @@ public void onChannelStateChanged(ChannelStateChange stateChange) { assertEquals("Suspended channel histories do not match", expectedSuspendedChannelHistory, suspendedChannelHistory); } catch (AblyException e) { e.printStackTrace(); - fail("channels_are_reattached_after_reconnecting_when_statettl_plus_idleinterval_has_passed: Unexpected exception"); + fail("channels_are_reattached_after_reconnecting_when_statettl_plus_idleinterval_has_passed: " + e.errorInfo.message); } finally { if (ably != null) ably.close(); From f0e09fb7d59c3581840db1f6fe891b316a33bfda Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Mon, 7 Nov 2022 13:17:42 +0100 Subject: [PATCH 61/67] Remove old implementation of re-enter --- .../java/io/ably/lib/realtime/Presence.java | 58 ------------------- 1 file changed, 58 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/Presence.java b/core/src/main/java/io/ably/lib/realtime/Presence.java index ade10bea6..630f68ef1 100644 --- a/core/src/main/java/io/ably/lib/realtime/Presence.java +++ b/core/src/main/java/io/ably/lib/realtime/Presence.java @@ -239,61 +239,6 @@ private void endSyncAndEmitLeaves() { member.timestamp = System.currentTimeMillis(); } broadcastPresence(residualMembers.toArray(new PresenceMessage[residualMembers.size()])); - - /** - * (RTP5c2) If a SYNC is initiated as part of the attach, then once the SYNC is complete, - * all members not present in the PresenceMap but present in the internal PresenceMap must - * be re-entered automatically by the client using the clientId and data attributes from - * each. The members re-entered automatically must be removed from the internal PresenceMap - * ensuring that members present on the channel are constructed from presence events sent - * from Ably since the channel became ATTACHED - */ - if (syncAsResultOfAttach) { - syncAsResultOfAttach = false; - for (PresenceMessage item: internalPresence.values()) { - if (presence.put(item)) { - /* Message is new to presence map, send it */ - final String clientId = item.clientId; - try { - /** - * (RTP17d) [...] publishing a PresenceMessage with an ENTER action using the - * clientId and data attributes from that member [...] - */ - PresenceMessage itemToSend = new PresenceMessage(); - itemToSend.clientId = item.clientId; - itemToSend.data = item.data; - itemToSend.action = PresenceMessage.Action.enter; - updatePresence(itemToSend, new CompletionListener() { - @Override - public void onSuccess() { - } - - @Override - public void onError(ErrorInfo reason) { - /* - * (RTP5c3) If any of the automatic ENTER presence messages published - * in RTP5c2 fail, then an UPDATE event should be emitted on the channel - * with resumed set to true and reason set to an ErrorInfo object with error - * code value 91004 and the error message string containing the message - * received from Ably (if applicable), the code received from Ably - * (if applicable) and the explicit or implicit client_id of the PresenceMessage - */ - String errorString = String.format(Locale.ROOT, "Cannot automatically re-enter %s on channel %s (%s)", - clientId, channel.name, reason.message); - Log.e(TAG, errorString); - channel.emitUpdate(new ErrorInfo(errorString, 91004), true); - } - }); - } catch(AblyException e) { - String errorString = String.format(Locale.ROOT, "Cannot automatically re-enter %s on channel %s (%s)", - clientId, channel.name, e.errorInfo.message); - Log.e(TAG, errorString); - channel.emitUpdate(new ErrorInfo(errorString, 91004), true); - } - } - } - internalPresence.clear(); - } } /** @@ -748,7 +693,6 @@ private void failQueuedMessages(ErrorInfo reason) { void setAttached(boolean hasPresence) { /* Start sync, if hasPresence is not set end sync immediately dropping all the current presence members */ presence.startSync(); - syncAsResultOfAttach = true; if (!hasPresence) { /* * RTP19a If the PresenceMap has existing members when an ATTACHED message is received without a @@ -1095,8 +1039,6 @@ public String memberKey(PresenceMessage item) { /* channel serial if sync is in progress */ private String currentSyncChannelSerial; - /* Sync in progress is a result of attach operation */ - private boolean syncAsResultOfAttach; /** * (RTP13) Presence#syncComplete returns true if the initial SYNC operation has completed for From 865bca81e42e4f8665abcb847e21349d3f07fa42 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Mon, 7 Nov 2022 16:41:55 +0100 Subject: [PATCH 62/67] Emit update on send pending message error --- core/src/main/java/io/ably/lib/transport/ConnectionManager.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java index c4a908bde..bf8ac332c 100644 --- a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -1444,6 +1444,7 @@ private void sendPendingQueueMessages() { String errorString = String.format(Locale.ROOT, "Unable to send pending message %s (%s)", queuedMessage.msg.id, e.errorInfo.message); Log.e(TAG, errorString); + connection.emitUpdate(e.errorInfo); } } } From e079abb127570f1df9c9846d40848167855e3cf5 Mon Sep 17 00:00:00 2001 From: Igor QSD Date: Tue, 8 Nov 2022 08:42:43 +0100 Subject: [PATCH 63/67] Update core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java Co-authored-by: KacperKluka <62378170+KacperKluka@users.noreply.github.com> --- .../main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java b/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java index e43a1fb6a..36ece3dd7 100644 --- a/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java +++ b/core/src/main/java/io/ably/lib/realtime/ConnectionRecoveryKey.java @@ -42,7 +42,7 @@ public void setSerials(Map serials) { this.serials.putAll(serials); } - public void addSerials(String channelName, String channelSerial) { + public void addSerial(String channelName, String channelSerial) { this.serials.put(channelName, channelSerial); } From bd5a540cb93dd1f798a9199d2fc1c2420c9e1579 Mon Sep 17 00:00:00 2001 From: Igor QSD Date: Tue, 8 Nov 2022 08:48:45 +0100 Subject: [PATCH 64/67] Update core/src/main/java/io/ably/lib/transport/ConnectionManager.java Co-authored-by: KacperKluka <62378170+KacperKluka@users.noreply.github.com> --- .../main/java/io/ably/lib/transport/ConnectionManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java index bf8ac332c..006b94d2a 100644 --- a/core/src/main/java/io/ably/lib/transport/ConnectionManager.java +++ b/core/src/main/java/io/ably/lib/transport/ConnectionManager.java @@ -1430,8 +1430,8 @@ public QueuedMessage(ProtocolMessage msg, CompletionListener listener) { } /** - * Send all pending messages which are queue. - * Remove them from the queue once they are send successfully + * Send all pending messages which are in the queue. + * Remove them from the queue once they are sent successfully * Spec: RTN19a */ private void sendPendingQueueMessages() { From 437bb08226f43d16a088a9362f90c02db03d7279 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Tue, 8 Nov 2022 08:51:29 +0100 Subject: [PATCH 65/67] Fix wrong method name in ConnectionRecoveryKey --- core/src/main/java/io/ably/lib/realtime/Connection.java | 2 +- .../java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/Connection.java b/core/src/main/java/io/ably/lib/realtime/Connection.java index 65da3144e..a9c8f3eb4 100644 --- a/core/src/main/java/io/ably/lib/realtime/Connection.java +++ b/core/src/main/java/io/ably/lib/realtime/Connection.java @@ -114,7 +114,7 @@ public String createRecoveryKey() { for (Object channel : ably.channels.values()) { if (channel instanceof RealtimeChannelBase) { RealtimeChannelBase rcb = (RealtimeChannelBase) channel; - recoveryKey.addSerials(rcb.name, rcb.properties.channelSerial); + recoveryKey.addSerial(rcb.name, rcb.properties.channelSerial); } } diff --git a/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java b/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java index 8761773ce..4d145762b 100644 --- a/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java +++ b/core/src/test/java/io/ably/lib/test/realtime/RealtimeConnectFailTest.java @@ -338,7 +338,7 @@ public void connect_unknown_recover_fail() { ClientOptions opts = createOptions(testVars.keys[0].keyStr); ConnectionRecoveryKey recovery = new ConnectionRecoveryKey("0123456789abcdef-99", 0); - recovery.addSerials("name","0"); + recovery.addSerial("name","0"); opts.recover = recovery.asJson(); ably = createAblyRealtime(opts); From 28531da66223c2beed1753ea7df9e1729eb87d4e Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Fri, 11 Nov 2022 11:30:00 +0100 Subject: [PATCH 66/67] Fix spec RTP17g --- .../java/io/ably/lib/realtime/Presence.java | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/Presence.java b/core/src/main/java/io/ably/lib/realtime/Presence.java index 630f68ef1..4573d3acb 100644 --- a/core/src/main/java/io/ably/lib/realtime/Presence.java +++ b/core/src/main/java/io/ably/lib/realtime/Presence.java @@ -986,16 +986,35 @@ synchronized void clear() { * Spec: RTP17g */ synchronized void reEnter() { - for (Map.Entry entry: members.entrySet()) { - PresenceMessage member = entry.getValue(); - member.action = PresenceMessage.Action.enter; - try { - updatePresence(member, null); - } catch (AblyException e) { - String errorString = String.format(Locale.ROOT, "Cannot automatically re-enter %s on channel %s (%s)", - member.clientId, channel.name, e.errorInfo.message); - Log.e(TAG, errorString); - channel.emitUpdate(new ErrorInfo(errorString, 91004), true); + for (PresenceMessage item: internalPresence.values()) { + if (presence.put(item)) { + /* Message is new to presence map, send it */ + final String clientId = item.clientId; + try { + PresenceMessage itemToSend = new PresenceMessage(); + itemToSend.id = item.id; + itemToSend.clientId = item.clientId; + itemToSend.data = item.data; + itemToSend.action = PresenceMessage.Action.enter; + updatePresence(itemToSend, new CompletionListener() { + @Override + public void onSuccess() { + } + + @Override + public void onError(ErrorInfo reason) { + String errorString = String.format(Locale.ROOT, "Cannot automatically re-enter %s on channel %s (%s)", + clientId, channel.name, reason.message); + Log.e(TAG, errorString); + channel.emitUpdate(new ErrorInfo(errorString, 91004), true); + } + }); + } catch(AblyException e) { + String errorString = String.format(Locale.ROOT, "Cannot automatically re-enter %s on channel %s (%s)", + clientId, channel.name, e.errorInfo.message); + Log.e(TAG, errorString); + channel.emitUpdate(new ErrorInfo(errorString, 91004), true); + } } } } From 49f8d6ad32ac4a5d3401147c7bde739ec8d1e9a9 Mon Sep 17 00:00:00 2001 From: QSD_igor Date: Mon, 14 Nov 2022 10:53:21 +0100 Subject: [PATCH 67/67] Add missing javadoc --- .../main/java/io/ably/lib/realtime/Presence.java | 16 ++++++++++++---- .../java/io/ably/lib/rest/DeviceDetails.java | 3 +++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/io/ably/lib/realtime/Presence.java b/core/src/main/java/io/ably/lib/realtime/Presence.java index 52ffee70e..229dcdae0 100644 --- a/core/src/main/java/io/ably/lib/realtime/Presence.java +++ b/core/src/main/java/io/ably/lib/realtime/Presence.java @@ -1188,12 +1188,14 @@ public void onError(ErrorInfo reason) { } /** - * Get the member key for the PresenceMessage. - * Spec: RTP17h - * @return key of the presence message + * Combines clientId and connectionId to ensure that multiple connected clients with an identical clientId are uniquely identifiable. + * A string function that returns the combined clientId and connectionId. + *

+ * Spec: TP3h + * @return A combination of clientId and connectionId. */ public String memberKey(PresenceMessage item) { - return item.connectionId + ':' + item.clientId; + return item.clientId + ':' + item.connectionId; } private boolean syncInProgress; @@ -1203,6 +1205,12 @@ public String memberKey(PresenceMessage item) { // RTP17h private class InternalPresenceMap extends PresenceMap { + + /** + * Get the member key for the internal PresenceMessage. + * Spec: RTP17h + * @return key of the presence message + */ @Override public String memberKey(PresenceMessage item) { return item.clientId; diff --git a/core/src/main/java/io/ably/lib/rest/DeviceDetails.java b/core/src/main/java/io/ably/lib/rest/DeviceDetails.java index 87211496f..60673f667 100644 --- a/core/src/main/java/io/ably/lib/rest/DeviceDetails.java +++ b/core/src/main/java/io/ably/lib/rest/DeviceDetails.java @@ -30,6 +30,9 @@ public class DeviceDetails { * The client ID the device is connected to Ably with. */ public String clientId; + /** + * A unique device secret generated by the Ably SDK. + */ public String deviceSecret; /** * A JSON object of key-value pairs that contains metadata for the device.