Repository: tipsy/javalin
Pull request #1256 (2021-05-24 20:34:36)

[core] Use Loom by default if present


Patch:
diff --git a/javalin/src/main/java/io/javalin/core/JavalinServer.kt b/javalin/src/main/java/io/javalin/core/JavalinServer.kt
index ae95d62c..8023b189 100644
--- a/javalin/src/main/java/io/javalin/core/JavalinServer.kt
+++ b/javalin/src/main/java/io/javalin/core/JavalinServer.kt
@@ -95,50 +95,3 @@ class JavalinServer(val config: JavalinConfig) {
     }
 
 }
-
-object JettyUtil {
-
-    private var defaultLogger: org.eclipse.jetty.util.log.Logger? = null
-
-    @JvmStatic
-    fun getOrDefault(server: Server?) = server ?: Server(QueuedThreadPool(250, 8, 60_000)).apply {
-        addBean(LowResourceMonitor(this))
-        insertHandler(StatisticsHandler())
-        setAttribute("is-default-server", true)
-    }
-
-    @JvmField
-    var logDuringStartup = false
-
-    @JvmStatic
-    fun disableJettyLogger() {
-        if (logDuringStartup) return
-        defaultLogger = defaultLogger ?: org.eclipse.jetty.util.log.Log.getLog()
-        org.eclipse.jetty.util.log.Log.setLog(NoopLogger())
-    }
-
-    fun reEnableJettyLogger() {
-        if (logDuringStartup) return
-        org.eclipse.jetty.util.log.Log.setLog(defaultLogger)
-    }
-
-}
-
-class NoopLogger : org.eclipse.jetty.util.log.Logger {
-    override fun getName() = "noop"
-    override fun getLogger(name: String) = this
-    override fun setDebugEnabled(enabled: Boolean) {}
-    override fun isDebugEnabled() = false
-    override fun ignore(ignored: Throwable) {}
-    override fun warn(msg: String, vararg args: Any) {}
-    override fun warn(thrown: Throwable) {}
-    override fun warn(msg: String, thrown: Throwable) {}
-    override fun info(msg: String, vararg args: Any) {}
-    override fun info(thrown: Throwable) {}
-    override fun info(msg: String, thrown: Throwable) {}
-    override fun debug(msg: String, vararg args: Any) {}
-    override fun debug(s: String, l: Long) {}
-    override fun debug(thrown: Throwable) {}
-    override fun debug(msg: String, thrown: Throwable) {}
-}
-
diff --git a/javalin/src/main/java/io/javalin/core/JettyUtil.kt b/javalin/src/main/java/io/javalin/core/JettyUtil.kt
new file mode 100644
index 00000000..09cf2a91
--- /dev/null
+++ b/javalin/src/main/java/io/javalin/core/JettyUtil.kt
@@ -0,0 +1,99 @@
+package io.javalin.core
+
+import io.javalin.core.LoomUtil.loomAvailable
+import io.javalin.core.LoomUtil.useLoomThreadPool
+import io.javalin.core.util.JavalinLogger
+import org.eclipse.jetty.server.LowResourceMonitor
+import org.eclipse.jetty.server.Server
+import org.eclipse.jetty.server.handler.StatisticsHandler
+import org.eclipse.jetty.util.thread.QueuedThreadPool
+import org.eclipse.jetty.util.thread.ThreadPool
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+
+object JettyUtil {
+
+    private var defaultLogger: org.eclipse.jetty.util.log.Logger? = null
+
+    @JvmStatic
+    fun getOrDefault(server: Server?) = server ?: Server(defaultThreadPool()).apply {
+        addBean(LowResourceMonitor(this))
+        insertHandler(StatisticsHandler())
+        setAttribute("is-default-server", true)
+    }
+
+    private fun defaultThreadPool() = if (useLoomThreadPool && loomAvailable) {
+        JavalinLogger.info("Loom is available, using Virtual ThreadPool... Neat!")
+        LoomThreadPool()
+    } else {
+        QueuedThreadPool(250, 8, 60_000)
+    }
+
+    @JvmField
+    var logDuringStartup = false
+
+    @JvmStatic
+    fun disableJettyLogger() {
+        if (logDuringStartup) return
+        defaultLogger = defaultLogger ?: org.eclipse.jetty.util.log.Log.getLog()
+        org.eclipse.jetty.util.log.Log.setLog(NoopLogger())
+    }
+
+    fun reEnableJettyLogger() {
+        if (logDuringStartup) return
+        org.eclipse.jetty.util.log.Log.setLog(defaultLogger)
+    }
+
+
+    class NoopLogger : org.eclipse.jetty.util.log.Logger {
+        override fun getName() = "noop"
+        override fun getLogger(name: String) = this
+        override fun setDebugEnabled(enabled: Boolean) {}
+        override fun isDebugEnabled() = false
+        override fun ignore(ignored: Throwable) {}
+        override fun warn(msg: String, vararg args: Any) {}
+        override fun warn(thrown: Throwable) {}
+        override fun warn(msg: String, thrown: Throwable) {}
+        override fun info(msg: String, vararg args: Any) {}
+        override fun info(thrown: Throwable) {}
+        override fun info(msg: String, thrown: Throwable) {}
+        override fun debug(msg: String, vararg args: Any) {}
+        override fun debug(s: String, l: Long) {}
+        override fun debug(thrown: Throwable) {}
+        override fun debug(msg: String, thrown: Throwable) {}
+    }
+
+}
+
+object LoomUtil {
+
+    @JvmField
+    var useLoomThreadPool = true
+    val loomAvailable = System.getProperty("java.version").contains("loom", ignoreCase = true) || try {
+        Thread::class.java.getDeclaredMethod("startVirtualThread", Runnable::class.java)
+        true
+    } catch (e: Exception) {
+        false
+    }
+
+    fun getExecutorService(): ExecutorService {
+        if (!loomAvailable) {
+            throw IllegalStateException("Your Java version (${System.getProperty("java.version")}) doesn't support Loom")
+        }
+        return Executors::class.java.getMethod("newVirtualThreadExecutor").invoke(Executors::class.java) as ExecutorService
+    }
+}
+
+class LoomThreadPool : ThreadPool {
+
+    private val executorService = LoomUtil.getExecutorService()
+
+    override fun execute(command: Runnable) {
+        executorService.submit(command)
+    }
+
+    override fun join() {}
+    override fun getThreads() = 1
+    override fun getIdleThreads() = 1
+    override fun isLowOnThreads() = false
+}


Test patch:
diff --git a/javalin/src/test/java/io/javalin/TestCustomJetty.kt b/javalin/src/test/java/io/javalin/TestCustomJetty.kt
index b15117d2..360e98c7 100644
--- a/javalin/src/test/java/io/javalin/TestCustomJetty.kt
+++ b/javalin/src/test/java/io/javalin/TestCustomJetty.kt
@@ -8,6 +8,7 @@
 package io.javalin
 
 import com.mashape.unirest.http.Unirest
+import io.javalin.core.LoomUtil
 import io.javalin.testing.TestServlet
 import io.javalin.testing.TestUtil
 import org.assertj.core.api.Assertions.assertThat
@@ -167,5 +168,14 @@ class TestCustomJetty {
         }
     }
 
+    @Test
+    fun `default server uses loom if available`() {
+        if (!LoomUtil.loomAvailable) return;
+        val log = TestUtil.captureStdOut {
+            Javalin.create().start().stop()
+        }
+        assertThat(log).contains("Loom is available, using Virtual ThreadPool... Neat!")
+    }
+
 }