From 0657c91f0f46f7861ef65f67908e7d9339aa172e Mon Sep 17 00:00:00 2001 From: F0Xde Date: Sat, 17 Oct 2020 13:49:43 +0200 Subject: [PATCH] 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. --- gradlew | 2 + gradlew.bat | 4 ++ .../kspigot/runnables/ChainableRunnables.kt | 41 ++++++++++++++++--- 3 files changed, 42 insertions(+), 5 deletions(-) mode change 100644 => 100755 gradlew diff --git a/gradlew b/gradlew old mode 100644 new mode 100755 index 2fe81a7d..fbd7c515 --- a/gradlew +++ b/gradlew @@ -82,6 +82,7 @@ esac CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + # Determine the Java command to use to start the JVM. if [ -n "$JAVA_HOME" ] ; then if [ -x "$JAVA_HOME/jre/sh/java" ] ; then @@ -129,6 +130,7 @@ fi if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then APP_HOME=`cygpath --path --mixed "$APP_HOME"` CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + JAVACMD=`cygpath --unix "$JAVACMD"` # We build the pattern for arguments to be converted via cygpath diff --git a/gradlew.bat b/gradlew.bat index 9618d8d9..5093609d 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=. set APP_BASE_NAME=%~n0 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. set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @@ -81,6 +84,7 @@ set CMD_LINE_ARGS=%* set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + @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% diff --git a/src/main/kotlin/net/axay/kspigot/runnables/ChainableRunnables.kt b/src/main/kotlin/net/axay/kspigot/runnables/ChainableRunnables.kt index c1945cab..363d392f 100644 --- a/src/main/kotlin/net/axay/kspigot/runnables/ChainableRunnables.kt +++ b/src/main/kotlin/net/axay/kspigot/runnables/ChainableRunnables.kt @@ -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( val sync: Boolean @@ -12,20 +13,44 @@ abstract class ChainedRunnablePart( var next: ChainedRunnablePart? = null abstract fun execute() - + + abstract fun executeCatching(clazz: KClass, 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 startCatching( + data: T, + clazz: KClass, + 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( @@ -35,6 +60,9 @@ class ChainedRunnablePartFirst( override fun execute() = start(Unit) + override fun executeCatching(clazz: KClass, handler: (E) -> Unit) = + startCatching(Unit, clazz, handler) + override fun invoke(data: Unit) = runnable.invoke() } @@ -47,6 +75,9 @@ class ChainedRunnablePartThen( override fun execute() = previous.execute() + override fun executeCatching(clazz: KClass, handler: (E) -> Unit) = + previous.executeCatching(clazz, handler) + override fun invoke(data: T) = runnable.invoke(data) } @@ -57,7 +88,7 @@ fun firstSync(runnable: () -> R) = firstDo(true, runnable) fun firstAsync(runnable: () -> R) = firstDo(false, runnable) // THEN -fun ChainedRunnablePart.thenDo(sync: Boolean, runnable: (R) -> U) - = ChainedRunnablePartThen(runnable, sync, this).apply { previous.next = this } +fun ChainedRunnablePart.thenDo(sync: Boolean, runnable: (R) -> U) = + ChainedRunnablePartThen(runnable, sync, this).apply { previous.next = this } fun ChainedRunnablePart.thenSync(runnable: (R) -> U) = thenDo(true, runnable) fun ChainedRunnablePart.thenAsync(runnable: (R) -> U) = thenDo(false, runnable) \ No newline at end of file