All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.pubnub.api.endpoints.remoteaction.RetryingRemoteAction.kt Maven / Gradle / Ivy

Go to download

PubNub is a cross-platform client-to-client (1:1 and 1:many) push service in the cloud, capable of broadcasting real-time messages to millions of web and mobile clients simultaneously, in less than a quarter second!

There is a newer version: 10.2.0
Show newest version
package com.pubnub.api.endpoints.remoteaction

import com.pubnub.api.PubNubError
import com.pubnub.api.PubNubException
import com.pubnub.api.enums.PNOperationType
import com.pubnub.api.enums.PNStatusCategory
import com.pubnub.api.models.consumer.PNStatus
import java.util.concurrent.CountDownLatch
import java.util.concurrent.ExecutorService
import java.util.concurrent.atomic.AtomicReference

internal class RetryingRemoteAction(
    private val remoteAction: ExtendedRemoteAction,
    private val maxNumberOfAutomaticRetries: Int,
    private val operationType: PNOperationType,
    private val executorService: ExecutorService
) : ExtendedRemoteAction {
    private lateinit var cachedCallback: (result: T?, status: PNStatus) -> Unit

    @Throws(PubNubException::class)
    override fun sync(): T? {
        validate()
        var thrownException: PubNubException? = null
        for (i in 0 until maxNumberOfAutomaticRetries) {
            thrownException = try {
                return remoteAction.sync()
            } catch (ex: PubNubException) {
                ex
            }
        }
        throw thrownException!!
    }

    override fun async(callback: (result: T?, status: PNStatus) -> Unit) {
        cachedCallback = callback
        executorService.execute(
            Runnable {
                try {
                    validate()
                } catch (ex: PubNubException) {
                    callback(
                        null,
                        PNStatus(
                            operation = operationType,
                            error = true,
                            exception = ex,
                            category = PNStatusCategory.PNBadRequestCategory
                        ).apply {
                            executedEndpoint = this@RetryingRemoteAction
                        }
                    )
                    return@Runnable
                }
                var lastResultAndStatus: ResultAndStatus? = null
                for (i in 0 until maxNumberOfAutomaticRetries) {
                    lastResultAndStatus = syncAsync()
                    if (!lastResultAndStatus.status.error) {
                        callback(lastResultAndStatus.result, lastResultAndStatus.status)
                        return@Runnable
                    }
                }
                callback(lastResultAndStatus!!.result, lastResultAndStatus.status)
            }
        )
    }

    override fun retry() {
        async(cachedCallback)
    }

    override fun silentCancel() {
        remoteAction.silentCancel()
    }

    override fun operationType(): PNOperationType {
        return remoteAction.operationType()
    }

    private data class ResultAndStatus(
        val result: T? = null,
        val status: PNStatus
    )

    private fun syncAsync(): ResultAndStatus {
        val latch = CountDownLatch(1)
        val atomicResultAndStatus = AtomicReference>()
        remoteAction.async { result: T?, status: PNStatus ->
            atomicResultAndStatus.set(
                ResultAndStatus(
                    result,
                    status.copy().apply { executedEndpoint = this@RetryingRemoteAction }
                )
            )
            latch.countDown()
        }

        return try {
            latch.await()
            atomicResultAndStatus.get()
        } catch (e: InterruptedException) {
            remoteAction.silentCancel()
            ResultAndStatus(
                null,
                PNStatus(
                    category = PNStatusCategory.PNUnknownCategory,
                    operation = operationType,
                    error = true,
                    exception = PubNubException(errorMessage = e.message)
                ).apply { executedEndpoint = this@RetryingRemoteAction }
            )
        }
    }

    @Throws(PubNubException::class)
    private fun validate() {
        if (maxNumberOfAutomaticRetries < 1) {
            throw PubNubException(PubNubError.INVALID_ARGUMENTS)
        }
    }

    companion object {
        fun  autoRetry(
            remoteAction: ExtendedRemoteAction,
            maxNumberOfAutomaticRetries: Int,
            operationType: PNOperationType,
            executorService: ExecutorService
        ): RetryingRemoteAction {
            return RetryingRemoteAction(remoteAction, maxNumberOfAutomaticRetries, operationType, executorService)
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy