From 0657c91f0f46f7861ef65f67908e7d9339aa172e Mon Sep 17 00:00:00 2001 From: F0Xde Date: Sat, 17 Oct 2020 13:49:43 +0200 Subject: [PATCH 1/4] 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 From b97bbee74a20666250ecb1e20d9f4336df1efbd7 Mon Sep 17 00:00:00 2001 From: bluefireoly Date: Sat, 17 Oct 2020 14:21:16 +0200 Subject: [PATCH 2/4] Update .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index 5e430fe4..feb17f21 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ gradle/ .gradle/ logs/ gradle.properties +gradlew +gradlew.bat \ No newline at end of file From 11d996d78cc0ec98a3d60377b46758eae7dadc4b Mon Sep 17 00:00:00 2001 From: F0Xde Date: Sat, 17 Oct 2020 14:40:43 +0200 Subject: [PATCH 3/4] Revert changes to gradlew and gradlew.bat --- gradlew | 2 -- gradlew.bat | 4 ---- 2 files changed, 6 deletions(-) mode change 100755 => 100644 gradlew diff --git a/gradlew b/gradlew old mode 100755 new mode 100644 index fbd7c515..2fe81a7d --- a/gradlew +++ b/gradlew @@ -82,7 +82,6 @@ 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 @@ -130,7 +129,6 @@ 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 5093609d..9618d8d9 100644 --- a/gradlew.bat +++ b/gradlew.bat @@ -29,9 +29,6 @@ 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" @@ -84,7 +81,6 @@ 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% From 0daae1d06421578a2183a89432e352586d5f8a58 Mon Sep 17 00:00:00 2001 From: bluefireoly Date: Sat, 17 Oct 2020 14:45:55 +0200 Subject: [PATCH 4/4] Update ChainableRunnables.kt --- .../kspigot/runnables/ChainableRunnables.kt | 57 +++++++++++-------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/src/main/kotlin/net/axay/kspigot/runnables/ChainableRunnables.kt b/src/main/kotlin/net/axay/kspigot/runnables/ChainableRunnables.kt index 363d392f..8bd8176b 100644 --- a/src/main/kotlin/net/axay/kspigot/runnables/ChainableRunnables.kt +++ b/src/main/kotlin/net/axay/kspigot/runnables/ChainableRunnables.kt @@ -12,14 +12,17 @@ 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 + abstract fun execute() + + abstract fun executeCatching( + @Suppress("UNCHECKED_CAST") exceptionClass: KClass = Exception::class as KClass, + exceptionHandler: ((E) -> Unit)? = null, + ) + protected fun start(data: T) { - run { + this.run { val result = invoke(data) next?.start(result) } @@ -27,21 +30,21 @@ abstract class ChainedRunnablePart( protected fun startCatching( data: T, - clazz: KClass, - exceptionHandler: (E) -> Unit = {} + exceptionClass: KClass, + exceptionHandler: ((E) -> Unit)? ) { - run { - try { - val result = invoke(data) - next?.startCatching(result, clazz, exceptionHandler) + this.run { + val result = try { + invoke(data) } catch (e: Exception) { - if (clazz.isInstance(e)) { + if (exceptionClass.isInstance(e)) { @Suppress("UNCHECKED_CAST") - exceptionHandler(e as E) - } else { - throw e - } + exceptionHandler?.invoke(e as E) + null + } else throw e } + if (result != null) + next?.startCatching(result, exceptionClass, exceptionHandler) } } @@ -51,6 +54,7 @@ abstract class ChainedRunnablePart( else Bukkit.getScheduler().runTaskAsynchronously(KSpigotMainInstance, realRunnable) } + } class ChainedRunnablePartFirst( @@ -58,10 +62,11 @@ class ChainedRunnablePartFirst( sync: Boolean ) : ChainedRunnablePart(sync) { - override fun execute() = start(Unit) + override fun execute() + = start(Unit) - override fun executeCatching(clazz: KClass, handler: (E) -> Unit) = - startCatching(Unit, clazz, handler) + override fun executeCatching(exceptionClass: KClass, exceptionHandler: ((E) -> Unit)?) + = startCatching(Unit, exceptionClass, exceptionHandler) override fun invoke(data: Unit) = runnable.invoke() @@ -73,22 +78,24 @@ class ChainedRunnablePartThen( val previous: ChainedRunnablePart<*, T> ) : ChainedRunnablePart(sync) { - override fun execute() = previous.execute() + override fun execute() + = previous.execute() - override fun executeCatching(clazz: KClass, handler: (E) -> Unit) = - previous.executeCatching(clazz, handler) + override fun executeCatching(exceptionClass: KClass, exceptionHandler: ((E) -> Unit)?) + = previous.executeCatching(exceptionClass, exceptionHandler) override fun invoke(data: T) = runnable.invoke(data) } // FIRST -fun firstDo(sync: Boolean, runnable: () -> R) = ChainedRunnablePartFirst(runnable, sync) +fun firstDo(sync: Boolean, runnable: () -> R) + = ChainedRunnablePartFirst(runnable, sync) 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