
com.pubnub.internal.retry.RetryableCallback.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of pubnub-kotlin-impl Show documentation
Show all versions of pubnub-kotlin-impl Show documentation
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!
package com.pubnub.internal.retry
import com.pubnub.api.retry.RetryConfiguration
import com.pubnub.api.retry.RetryableEndpointGroup
import com.pubnub.internal.extension.scheduleWithDelay
import org.slf4j.LoggerFactory
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.util.concurrent.RejectedExecutionException
import java.util.concurrent.ScheduledExecutorService
import kotlin.time.Duration
import kotlin.time.Duration.Companion.milliseconds
internal abstract class RetryableCallback(
retryConfiguration: RetryConfiguration,
endpointGroupName: RetryableEndpointGroup,
private val call: Call,
private val isEndpointRetryable: Boolean,
private val executorService: ScheduledExecutorService,
) : Callback, RetryableBase(retryConfiguration, endpointGroupName) {
private val log = LoggerFactory.getLogger(this.javaClass.simpleName)
private var retryCount = 0
private var exponentialMultiplier = 0.0
override fun onResponse(
call: Call,
response: Response,
) {
if (shouldRetryOnResponse(response)) {
retryOnResponseWithError(response)
} else {
onFinalResponse(call, response)
}
}
override fun onFailure(
call: Call,
t: Throwable,
) {
if (shouldRetryOnFailure(t)) {
retryOnFailure()
} else {
onFinalFailure(call, t)
}
}
private fun shouldRetryOnResponse(response: Response): Boolean {
return !response.isSuccessful &&
retryCount < maxRetryNumberFromConfiguration &&
isErrorCodeRetryable(response.raw().code) &&
isRetryConfSetForThisRestCall &&
isEndpointRetryable
}
private fun shouldRetryOnFailure(t: Throwable): Boolean {
val exception = Exception(t)
return retryCount < maxRetryNumberFromConfiguration &&
isExceptionRetryable(exception) &&
isRetryConfSetForThisRestCall &&
isEndpointRetryable
}
private fun isExceptionRetryable(e: Exception): Boolean {
return e.cause?.let { cause ->
retryableExceptions.any { it.isInstance(cause) }
} ?: false
}
private fun retryOnFailure() {
val effectiveDelay = getDelayFromRetryConfiguration()
retry(effectiveDelay)
}
private fun retryOnResponseWithError(response: Response) {
val effectiveDelay: Duration = getDelayForRetryOnResponse(response)
retry(effectiveDelay)
}
private fun retry(delay: Duration) {
retryCount++
val randomDelayInMillis: Int = random.nextInt(MAX_RANDOM_DELAY_IN_MILLIS)
val effectiveDelay: Duration = delay + randomDelayInMillis.milliseconds
log.trace("Added random delay so effective retry delay is ${effectiveDelay.inWholeMilliseconds} millis")
// don't want to block the main thread in case of Android so using executorService
try {
executorService.scheduleWithDelay(effectiveDelay) {
call.clone().enqueue(this)
}
} catch (_: RejectedExecutionException) {
log.trace("Unable to schedule retry, PubNub was likely already destroyed.")
}
}
private fun getDelayForRetryOnResponse(response: Response): Duration {
return getDelayBasedOnResponse(response)
}
abstract fun onFinalResponse(
call: Call,
response: Response,
)
abstract fun onFinalFailure(
call: Call,
t: Throwable,
)
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy