Allow catching of exceptions in chained runnables

The user can handle exceptions of a specified type occurring in a chained
runnable by calling `executeCatching` and optionally passing a handler
function.

This is needed needed in order to gracefully stop the execution of a
runnable chain without the exception being caught by Bukkit and
resulting in it being logged as uncaught, while also allowing further
handling of the exception.
This commit is contained in:
F0Xde
2020-10-17 13:49:43 +02:00
parent f887c73f90
commit 0657c91f0f
3 changed files with 42 additions and 5 deletions

View File

@@ -4,6 +4,7 @@ package net.axay.kspigot.runnables
import net.axay.kspigot.main.KSpigotMainInstance
import org.bukkit.Bukkit
import kotlin.reflect.KClass
abstract class ChainedRunnablePart<T, R>(
val sync: Boolean
@@ -12,20 +13,44 @@ abstract class ChainedRunnablePart<T, R>(
var next: ChainedRunnablePart<R, *>? = null
abstract fun execute()
abstract fun <E : Exception> executeCatching(clazz: KClass<E>, handler: (E) -> Unit = {})
protected abstract fun invoke(data: T): R
protected fun start(data: T) {
val realRunnable = Runnable {
run {
val result = invoke(data)
next?.start(result)
}
}
protected fun <E : Exception> startCatching(
data: T,
clazz: KClass<E>,
exceptionHandler: (E) -> Unit = {}
) {
run {
try {
val result = invoke(data)
next?.startCatching(result, clazz, exceptionHandler)
} catch (e: Exception) {
if (clazz.isInstance(e)) {
@Suppress("UNCHECKED_CAST")
exceptionHandler(e as E)
} else {
throw e
}
}
}
}
private fun run(realRunnable: () -> Unit) {
if (sync)
Bukkit.getScheduler().runTask(KSpigotMainInstance, realRunnable)
else
Bukkit.getScheduler().runTaskAsynchronously(KSpigotMainInstance, realRunnable)
}
}
class ChainedRunnablePartFirst<R>(
@@ -35,6 +60,9 @@ class ChainedRunnablePartFirst<R>(
override fun execute() = start(Unit)
override fun <E : Exception> executeCatching(clazz: KClass<E>, handler: (E) -> Unit) =
startCatching(Unit, clazz, handler)
override fun invoke(data: Unit) = runnable.invoke()
}
@@ -47,6 +75,9 @@ class ChainedRunnablePartThen<T, R>(
override fun execute() = previous.execute()
override fun <E : Exception> executeCatching(clazz: KClass<E>, handler: (E) -> Unit) =
previous.executeCatching(clazz, handler)
override fun invoke(data: T) = runnable.invoke(data)
}
@@ -57,7 +88,7 @@ fun <R> firstSync(runnable: () -> R) = firstDo(true, runnable)
fun <R> firstAsync(runnable: () -> R) = firstDo(false, runnable)
// THEN
fun <T, R, U> ChainedRunnablePart<T, R>.thenDo(sync: Boolean, runnable: (R) -> U)
= ChainedRunnablePartThen(runnable, sync, this).apply { previous.next = this }
fun <T, R, U> ChainedRunnablePart<T, R>.thenDo(sync: Boolean, runnable: (R) -> U) =
ChainedRunnablePartThen(runnable, sync, this).apply { previous.next = this }
fun <T, R, U> ChainedRunnablePart<T, R>.thenSync(runnable: (R) -> U) = thenDo(true, runnable)
fun <T, R, U> ChainedRunnablePart<T, R>.thenAsync(runnable: (R) -> U) = thenDo(false, runnable)