Coverage Summary for Class: JettyServerKt (dev.suresh.vthread.jetty)
Class |
Class, %
|
Method, %
|
Branch, %
|
Line, %
|
Instruction, %
|
JettyServerKt |
0%
(0/1)
|
0%
(0/6)
|
0%
(0/18)
|
0%
(0/78)
|
0%
(0/407)
|
package dev.suresh.vthread.jetty
import io.mikael.urlbuilder.UrlBuilder
import jakarta.servlet.http.HttpServlet
import jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import java.net.http.HttpClient
import java.net.http.HttpRequest
import java.net.http.HttpResponse.BodyHandlers
import java.time.Duration
import java.util.concurrent.Executors
import kotlin.time.DurationUnit
import kotlin.time.measureTime
import org.eclipse.jetty.ee10.servlet.ServletContextHandler
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.server.ServerConnector
import org.eclipse.jetty.util.JavaVersion
import org.eclipse.jetty.util.Jetty
fun main() {
run()
}
fun run(args: Array<String>? = emptyArray()) {
val httpPort = 8080
println("Starting the Jetty server on $httpPort...")
val server = Server(VirtualThreadPool())
val connector =
ServerConnector(server).apply {
port = httpPort
acceptQueueSize = 64
}
server.connectors = arrayOf(connector)
val context = ServletContextHandler()
context.addServlet(HelloServlet::class.java, "/")
server.handler = context
server.start()
println("Server started at ${server.uri}")
val took = measureTime { pumpRequests(server, 50) }
println("Took ${took.toDouble(DurationUnit.SECONDS)} seconds")
if (args.orEmpty().any { it.equals("--no-shutdown", true) }) {
server.join()
} else {
println("Shutting down the server!")
server.stop()
}
}
fun pumpRequests(server: Server, count: Int, deadlineInSec: Long = 5L) {
require(count > 0)
println(
"Sending $count concurrent requests to ${server.uri} and wait for $deadlineInSec seconds...",
)
val client =
HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.followRedirects(HttpClient.Redirect.NORMAL)
.connectTimeout(Duration.ofSeconds(5))
.build()
val factory = Thread.ofVirtual().name("VirtualThreadPool-", 1).factory()
val execSvc = Executors.newThreadPerTaskExecutor(factory)
// val ecs = ExecutorCompletionService<String>(execSvc)
val results =
execSvc.use { exec ->
val user = System.getProperty("user.name", "user")
println("--> Sending $count concurrent requests")
(1..count).map { idx ->
exec.submit<Result<String>> {
try {
val uri =
UrlBuilder.fromUri(server.uri)
.addParameter("id", idx.toString())
.addParameter("user", user)
.toUri()
val req =
HttpRequest.newBuilder()
.uri(uri)
.timeout(Duration.ofSeconds(2))
.header("Content-Type", "application/json")
.GET()
.build()
val res = client.send(req, BodyHandlers.ofString())
println("<--- $idx. Response($threadInfo): ${res.statusCode()} - ${res.body()}")
Result.success(res.body())
} catch (t: Throwable) {
Result.failure(t)
}
}
}
}
// Clear the interrupt status
println("Checking if the current thread has been interrupted: ${Thread.interrupted()}")
val (ok, err) = results.map { it.get() }.partition { it.isSuccess }
err.forEachIndexed { i, r ->
if (i == 0) println("=== ERRORS ===")
val msg =
when (val ex = r.exceptionOrNull()) {
is InterruptedException -> "Task interrupted/cancelled due to timeout!"
else -> ex?.cause?.message
}
println("ERROR ${i + 1} -> $msg")
}
println(
"""
SUCCESS: ${ok.size} / ${results.size}
FAILURE: ${err.size} / ${results.size}
"""
.trimIndent(),
)
}
class HelloServlet : HttpServlet() {
private val ID = ScopedValue.newInstance<String>()
private val USER = ScopedValue.newInstance<String>()
private val OS: String = System.getProperty("os.name")
init {
println("Initializing Jakarta Servlet >>>>> ")
}
override fun doGet(req: HttpServletRequest, resp: HttpServletResponse) {
val id = req.getParameter("id")
val user = req.getParameter("user")
ScopedValue.where(ID, id).where(USER, user).run {
resp.apply {
contentType = "application/json"
status = HttpServletResponse.SC_OK
writer?.println(exec(req))
}
}
}
private fun exec(req: HttpServletRequest): String {
// Simulate blocking
Thread.sleep(Duration.ofMillis(500))
return """
{
"Id" : ${ID.orElse("n/a")},
"User" : ${USER.orElse("n/a")},
"server" : Jetty-${Jetty.VERSION},
"Java" : ${JavaVersion.VERSION},
"OS" : $OS,
"target" : ${req.fullURL},
"Thread" : ${Thread.currentThread()}
}
"""
.trimIndent()
}
}
val HttpServletRequest.fullURL: String
get() =
when (queryString.isNullOrBlank()) {
true -> requestURL.toString()
else -> requestURL.append('?').append(queryString).toString()
}
val threadInfo
get() = Thread.currentThread().run { "$name-${threadId()}-$isVirtual" }