org.enodeframework.common.io.IOHelper.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of enode Show documentation
Show all versions of enode Show documentation
The enodeframework core implementation.
package org.enodeframework.common.io
import org.enodeframework.common.exception.IORuntimeException
import org.enodeframework.common.function.Action1
import org.enodeframework.common.function.Action2
import org.enodeframework.common.function.DelayedTask
import org.enodeframework.common.function.Func
import org.enodeframework.common.utilities.Ensure
import org.slf4j.LoggerFactory
import java.time.Duration
import java.util.concurrent.CompletableFuture
import java.util.concurrent.CompletionException
/**
* @author [email protected]
*/
object IOHelper {
private val logger = LoggerFactory.getLogger(IOHelper::class.java)
@JvmStatic
fun tryAsyncActionRecursively(
asyncActionName: String,
asyncAction: Func>,
successAction: Action1,
getContextInfoFunc: Func,
failedAction: Action2?,
retryTimes: Int,
retryWhenFailed: Boolean) {
tryAsyncActionRecursively(asyncActionName, asyncAction, successAction, getContextInfoFunc, failedAction, retryTimes, retryWhenFailed, 3, 1000)
}
@JvmStatic
fun tryAsyncActionRecursively(
asyncActionName: String,
asyncAction: Func>,
successAction: Action1,
getContextInfoFunc: Func,
failedAction: Action2?,
retryTimes: Int,
retryWhenFailed: Boolean,
maxRetryTimes: Int,
retryInterval: Int) {
val asyncTaskExecutionContext = AsyncTaskExecutionContext(asyncActionName, asyncAction, successAction, getContextInfoFunc, failedAction, retryTimes, retryWhenFailed, maxRetryTimes, retryInterval)
asyncTaskExecutionContext.execute()
}
@JvmStatic
fun tryAsyncActionRecursivelyWithoutResult(
asyncActionName: String,
asyncAction: Func>,
successAction: Action1,
getContextInfoFunc: Func,
failedAction: Action2?,
retryTimes: Int,
retryWhenFailed: Boolean) {
val asyncTaskExecutionContext = AsyncTaskExecutionContext(asyncActionName, asyncAction, successAction, getContextInfoFunc, failedAction, retryTimes, retryWhenFailed, 3, 1000)
asyncTaskExecutionContext.execute()
}
@JvmStatic
fun tryIOFunc(func: Func, funcName: String): T {
Ensure.notNull(func, "func")
Ensure.notNull(funcName, "funcName")
return try {
func.apply()
} catch (ex: Exception) {
throw IORuntimeException(String.format("%s failed.", funcName), ex)
}
}
@JvmStatic
fun tryIOFuncAsync(func: Func>, funcName: String): CompletableFuture {
Ensure.notNull(func, "func")
Ensure.notNull(funcName, "funcName")
return try {
func.apply()
} catch (ex: Exception) {
throw IORuntimeException(String.format("%s failed.", funcName), ex)
}
}
internal class AsyncTaskExecutionContext(private val actionName: String, asyncAction: Func>, successAction: Action1, contextInfoFunc: Func, failedAction: Action2?, retryTimes: Int, retryWhenFailed: Boolean, maxRetryTimes: Int, retryInterval: Int) {
private val asyncAction: Func>
private val successAction: Action1
private val contextInfoFunc: Func
private val failedAction: Action2?
private var currentRetryTimes: Int
private val retryWhenFailed: Boolean
private val maxRetryTimes: Int
private val retryInterval: Int
fun execute() {
var asyncResult = CompletableFuture()
try {
asyncResult = asyncAction.apply()
} catch (ex: Exception) {
asyncResult.completeExceptionally(ex)
}
if (asyncResult.isCancelled) {
asyncResult.exceptionally { ex: Throwable ->
logger.error("Task '{}' was cancelled, contextInfo: {}, current retryTimes: {}.",
actionName,
getContextInfo(contextInfoFunc),
currentRetryTimes, ex)
executeFailedAction(ex, String.format("Task '%s' was cancelled.", actionName))
null
}
return
}
asyncResult
.thenAccept { result: TAsyncResult ->
executeSuccessAction(result)
}
.exceptionally { ex: Throwable ->
processTaskException(ex)
null
}
}
private fun executeRetryAction() {
try {
if (currentRetryTimes >= maxRetryTimes) {
DelayedTask.startDelayedTask(Duration.ofMillis(retryInterval.toLong()), { doRetry() })
} else {
doRetry()
}
} catch (ex: Exception) {
logger.error("Failed to execute the retryAction, actionName:{}, contextInfo:{}", actionName, getContextInfo(contextInfoFunc), ex)
}
}
private fun doRetry() {
currentRetryTimes++
execute()
}
private fun executeSuccessAction(result: TAsyncResult) {
try {
successAction.apply(result)
} catch (ex: Exception) {
logger.error("Failed to execute the successAction, actionName:{}, contextInfo:{}", actionName, getContextInfo(contextInfoFunc), ex)
}
}
private fun executeFailedAction(e: Throwable, errorMessage: String?) {
try {
failedAction?.apply(e, errorMessage)
} catch (ex: Exception) {
logger.error("Failed to execute the failedAction of action:{}, contextInfo:{}", actionName, getContextInfo(contextInfoFunc), ex)
}
}
private fun getContextInfo(func: Func): String? {
return try {
func.apply()
} catch (ex: Exception) {
logger.error("Failed to execute the getContextInfoFunc.", ex)
""
}
}
private fun processTaskException(exception: Throwable) {
if (exception is IORuntimeException) {
logger.error("Async task '{}' has io exception, contextInfo:{}, current retryTimes:{}, try to run the async task again.", actionName, getContextInfo(contextInfoFunc), currentRetryTimes, exception)
executeRetryAction()
} else if (exception is CompletionException && exception.cause is IORuntimeException) {
logger.error("Async task '{}' has io exception, contextInfo:{}, current retryTimes:{}, try to run the async task again.", actionName, getContextInfo(contextInfoFunc), currentRetryTimes, exception)
executeRetryAction()
} else {
logger.error("Task '{}' has unknown exception, contextInfo:{}, current retryTimes:{}", actionName, getContextInfo(contextInfoFunc), currentRetryTimes, exception)
if (retryWhenFailed) {
executeRetryAction()
} else {
executeFailedAction(exception, exception.message)
}
}
}
init {
this.successAction = successAction
this.contextInfoFunc = contextInfoFunc
this.failedAction = failedAction
this.currentRetryTimes = retryTimes
this.retryWhenFailed = retryWhenFailed
this.maxRetryTimes = maxRetryTimes
this.retryInterval = retryInterval
this.asyncAction = asyncAction
}
}
}