@@ -5,6 +5,7 @@ import io.ably.lib.realtime.CompletionListener
55import io.ably.lib.types.ChannelMode
66import io.ably.lib.types.ErrorInfo
77import io.ably.lib.types.ProtocolMessage
8+ import kotlinx.coroutines.CompletableDeferred
89import kotlinx.coroutines.suspendCancellableCoroutine
910import kotlin.coroutines.resume
1011import kotlin.coroutines.resumeWithException
@@ -14,7 +15,7 @@ import kotlin.coroutines.resumeWithException
1415 */
1516internal suspend fun LiveObjectsAdapter.sendAsync (message : ProtocolMessage ) = suspendCancellableCoroutine { continuation ->
1617 try {
17- this .send(message, object : CompletionListener {
18+ connectionManager .send(message, clientOptions.queueMessages , object : CompletionListener {
1819 override fun onSuccess () {
1920 continuation.resume(Unit )
2021 }
@@ -28,11 +29,54 @@ internal suspend fun LiveObjectsAdapter.sendAsync(message: ProtocolMessage) = su
2829 }
2930}
3031
32+ internal suspend fun LiveObjectsAdapter.attachAsync (channelName : String ) = suspendCancellableCoroutine { continuation ->
33+ try {
34+ getChannel(channelName).attach(object : CompletionListener {
35+ override fun onSuccess () {
36+ continuation.resume(Unit )
37+ }
38+
39+ override fun onError (reason : ErrorInfo ) {
40+ continuation.resumeWithException(ablyException(reason))
41+ }
42+ })
43+ } catch (e: Exception ) {
44+ continuation.resumeWithException(e)
45+ }
46+ }
47+
48+ /* *
49+ * Retrieves the channel modes for a specific channel.
50+ * This method returns the modes that are set for the specified channel.
51+ *
52+ * @param channelName the name of the channel for which to retrieve the modes
53+ * @return the array of channel modes for the specified channel, or null if the channel is not found
54+ * Spec: RTO2a, RTO2b
55+ */
56+ internal fun LiveObjectsAdapter.getChannelModes (channelName : String ): Array <ChannelMode >? {
57+ val channel = getChannel(channelName)
58+
59+ // RTO2a - channel.modes is only populated on channel attachment, so use it only if it is set
60+ channel.modes?.let { modes ->
61+ if (modes.isNotEmpty()) {
62+ return modes
63+ }
64+ }
65+
66+ // RTO2b - otherwise as a best effort use user provided channel options
67+ channel.options?.let { options ->
68+ if (options.hasModes()) {
69+ return options.modes
70+ }
71+ }
72+ return null
73+ }
74+
3175/* *
3276 * Spec: RTO15d
3377 */
3478internal fun LiveObjectsAdapter.ensureMessageSizeWithinLimit (objectMessages : Array <ObjectMessage >) {
35- val maximumAllowedSize = maxMessageSizeLimit()
79+ val maximumAllowedSize = connectionManager.maxMessageSize
3680 val objectsTotalMessageSize = objectMessages.sumOf { it.size() }
3781 if (objectsTotalMessageSize > maximumAllowedSize) {
3882 throw ablyException(" ObjectMessages size $objectsTotalMessageSize exceeds maximum allowed size of $maximumAllowedSize bytes" ,
@@ -44,19 +88,46 @@ internal fun LiveObjectsAdapter.setChannelSerial(channelName: String, protocolMe
4488 if (protocolMessage.action != ProtocolMessage .Action .`object `) return
4589 val channelSerial = protocolMessage.channelSerial
4690 if (channelSerial.isNullOrEmpty()) return
47- setChannelSerial(channelName, channelSerial)
91+ getChannel(channelName).properties.channelSerial = channelSerial
92+ }
93+
94+ internal suspend fun LiveObjectsAdapter.ensureAttached (channelName : String ) {
95+ val channel = getChannel(channelName)
96+ when (val currentChannelStatus = channel.state) {
97+ ChannelState .initialized -> attachAsync(channelName)
98+ ChannelState .attached -> return
99+ ChannelState .attaching -> {
100+ val attachDeferred = CompletableDeferred <Unit >()
101+ getChannel(channelName).once {
102+ when (it.current) {
103+ ChannelState .attached -> attachDeferred.complete(Unit )
104+ else -> {
105+ val exception = ablyException(" Channel $channelName is in invalid state: ${it.current} , " +
106+ " error: ${it.reason} " , ErrorCode .ChannelStateError )
107+ attachDeferred.completeExceptionally(exception)
108+ }
109+ }
110+ }
111+ if (channel.state == ChannelState .attached) {
112+ attachDeferred.complete(Unit )
113+ }
114+ attachDeferred.await()
115+ }
116+ else ->
117+ throw ablyException(" Channel $channelName is in invalid state: $currentChannelStatus " , ErrorCode .ChannelStateError )
118+ }
48119}
49120
50121// Spec: RTLO4b1, RTLO4b2
51122internal fun LiveObjectsAdapter.throwIfInvalidAccessApiConfiguration (channelName : String ) {
52- throwIfMissingChannelMode(channelName, ChannelMode .object_subscribe)
53123 throwIfInChannelState(channelName, arrayOf(ChannelState .detached, ChannelState .failed))
124+ throwIfMissingChannelMode(channelName, ChannelMode .object_subscribe)
54125}
55126
56127internal fun LiveObjectsAdapter.throwIfInvalidWriteApiConfiguration (channelName : String ) {
57128 throwIfEchoMessagesDisabled()
58- throwIfMissingChannelMode(channelName, ChannelMode .object_publish)
59129 throwIfInChannelState(channelName, arrayOf(ChannelState .detached, ChannelState .failed, ChannelState .suspended))
130+ throwIfMissingChannelMode(channelName, ChannelMode .object_publish)
60131}
61132
62133internal fun LiveObjectsAdapter.throwIfUnpublishableState (channelName : String ) {
@@ -67,16 +138,16 @@ internal fun LiveObjectsAdapter.throwIfUnpublishableState(channelName: String) {
67138}
68139
69140// Spec: RTO2
70- internal fun LiveObjectsAdapter.throwIfMissingChannelMode (channelName : String , channelMode : ChannelMode ) {
141+ private fun LiveObjectsAdapter.throwIfMissingChannelMode (channelName : String , channelMode : ChannelMode ) {
71142 val channelModes = getChannelModes(channelName)
72143 if (channelModes == null || ! channelModes.contains(channelMode)) {
73144 // Spec: RTO2a2, RTO2b2
74145 throw ablyException(" \" ${channelMode.name} \" channel mode must be set for this operation" , ErrorCode .ChannelModeRequired )
75146 }
76147}
77148
78- internal fun LiveObjectsAdapter.throwIfInChannelState (channelName : String , channelStates : Array <ChannelState >) {
79- val currentState = getChannelState (channelName)
149+ private fun LiveObjectsAdapter.throwIfInChannelState (channelName : String , channelStates : Array <ChannelState >) {
150+ val currentState = getChannel (channelName).state
80151 if (currentState == null || channelStates.contains(currentState)) {
81152 throw ablyException(" Channel is in invalid state: $currentState " , ErrorCode .ChannelStateError )
82153 }
0 commit comments