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:
2
gradlew
vendored
Normal file → Executable file
2
gradlew
vendored
Normal file → Executable file
@@ -82,6 +82,7 @@ esac
|
|||||||
|
|
||||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
# Determine the Java command to use to start the JVM.
|
# Determine the Java command to use to start the JVM.
|
||||||
if [ -n "$JAVA_HOME" ] ; then
|
if [ -n "$JAVA_HOME" ] ; then
|
||||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||||
@@ -129,6 +130,7 @@ fi
|
|||||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||||
|
|
||||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||||
|
|
||||||
# We build the pattern for arguments to be converted via cygpath
|
# We build the pattern for arguments to be converted via cygpath
|
||||||
|
4
gradlew.bat
vendored
4
gradlew.bat
vendored
@@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
|
|||||||
set APP_BASE_NAME=%~n0
|
set APP_BASE_NAME=%~n0
|
||||||
set APP_HOME=%DIRNAME%
|
set APP_HOME=%DIRNAME%
|
||||||
|
|
||||||
|
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||||
|
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||||
|
|
||||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||||
|
|
||||||
@@ -81,6 +84,7 @@ set CMD_LINE_ARGS=%*
|
|||||||
|
|
||||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||||
|
|
||||||
|
|
||||||
@rem Execute Gradle
|
@rem Execute Gradle
|
||||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||||
|
|
||||||
|
@@ -4,6 +4,7 @@ package net.axay.kspigot.runnables
|
|||||||
|
|
||||||
import net.axay.kspigot.main.KSpigotMainInstance
|
import net.axay.kspigot.main.KSpigotMainInstance
|
||||||
import org.bukkit.Bukkit
|
import org.bukkit.Bukkit
|
||||||
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
abstract class ChainedRunnablePart<T, R>(
|
abstract class ChainedRunnablePart<T, R>(
|
||||||
val sync: Boolean
|
val sync: Boolean
|
||||||
@@ -12,20 +13,44 @@ abstract class ChainedRunnablePart<T, R>(
|
|||||||
var next: ChainedRunnablePart<R, *>? = null
|
var next: ChainedRunnablePart<R, *>? = null
|
||||||
|
|
||||||
abstract fun execute()
|
abstract fun execute()
|
||||||
|
|
||||||
|
abstract fun <E : Exception> executeCatching(clazz: KClass<E>, handler: (E) -> Unit = {})
|
||||||
|
|
||||||
protected abstract fun invoke(data: T): R
|
protected abstract fun invoke(data: T): R
|
||||||
|
|
||||||
protected fun start(data: T) {
|
protected fun start(data: T) {
|
||||||
val realRunnable = Runnable {
|
run {
|
||||||
val result = invoke(data)
|
val result = invoke(data)
|
||||||
next?.start(result)
|
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)
|
if (sync)
|
||||||
Bukkit.getScheduler().runTask(KSpigotMainInstance, realRunnable)
|
Bukkit.getScheduler().runTask(KSpigotMainInstance, realRunnable)
|
||||||
else
|
else
|
||||||
Bukkit.getScheduler().runTaskAsynchronously(KSpigotMainInstance, realRunnable)
|
Bukkit.getScheduler().runTaskAsynchronously(KSpigotMainInstance, realRunnable)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ChainedRunnablePartFirst<R>(
|
class ChainedRunnablePartFirst<R>(
|
||||||
@@ -35,6 +60,9 @@ class ChainedRunnablePartFirst<R>(
|
|||||||
|
|
||||||
override fun execute() = start(Unit)
|
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()
|
override fun invoke(data: Unit) = runnable.invoke()
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -47,6 +75,9 @@ class ChainedRunnablePartThen<T, R>(
|
|||||||
|
|
||||||
override fun execute() = previous.execute()
|
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)
|
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)
|
fun <R> firstAsync(runnable: () -> R) = firstDo(false, runnable)
|
||||||
|
|
||||||
// THEN
|
// THEN
|
||||||
fun <T, R, U> ChainedRunnablePart<T, R>.thenDo(sync: Boolean, runnable: (R) -> U)
|
fun <T, R, U> ChainedRunnablePart<T, R>.thenDo(sync: Boolean, runnable: (R) -> U) =
|
||||||
= ChainedRunnablePartThen(runnable, sync, this).apply { previous.next = this }
|
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>.thenSync(runnable: (R) -> U) = thenDo(true, runnable)
|
||||||
fun <T, R, U> ChainedRunnablePart<T, R>.thenAsync(runnable: (R) -> U) = thenDo(false, runnable)
|
fun <T, R, U> ChainedRunnablePart<T, R>.thenAsync(runnable: (R) -> U) = thenDo(false, runnable)
|
Reference in New Issue
Block a user