Skip to content

Instantly share code, notes, and snippets.

@nwalker
Created May 16, 2019 01:56
Show Gist options
  • Save nwalker/54a8fc00d41356a0fce90399f32ea5f4 to your computer and use it in GitHub Desktop.
Save nwalker/54a8fc00d41356a0fce90399f32ea5f4 to your computer and use it in GitHub Desktop.
minimal async servlet example with undertow
import io.undertow.Handlers
import io.undertow.Undertow
import io.undertow.servlet.Servlets
import io.undertow.servlet.Servlets.servlet
import java.util.*
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.TimeUnit
import java.util.stream.Collectors
import javax.servlet.http.HttpServlet
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse
class TimeoutServlet: HttpServlet() {
override fun doGet(req: HttpServletRequest, resp: HttpServletResponse) {
val reqId = req.getHeader("x-request-id") ?: UUID.randomUUID().toString()
log("GET: reqId = '$reqId' on ${currentThread()}")
val ctx = req.startAsync()
val cf = CompletableFuture<String>()
.orTimeout(10, TimeUnit.SECONDS);
cf.handleAsync { t, u ->
log("handle on ${currentThread()}")
(ctx.response as? HttpServletResponse)?.apply {
addHeader("x-request-id", reqId)
writer.print("{completion: ${t ?: u}}")
writer.close()
}
ctx.complete()
completions.remove(reqId)
}
completions[reqId] = cf
}
private fun currentThread() = Thread.currentThread().name
override fun doPost(req: HttpServletRequest, resp: HttpServletResponse) {
val reqId = req.getHeader("x-request-id")
when {
reqId.isNullOrEmpty() -> resp.status = 400
completions[reqId] == null -> resp.status = 404
else -> {
val completion = req.reader.lines().collect(Collectors.joining(System.lineSeparator()))
completions[reqId]!!.complete(completion)
log("POST '$completion' -> $reqId: ${completions[reqId].toString()}")
resp.status = 200
}
}
}
override fun doOptions(req: HttpServletRequest, resp: HttpServletResponse) {
resp.status = 200
resp.writer.apply {
write(completions.keys.joinToString(", ", prefix = "[", postfix = "]"))
close()
}
}
companion object {
val completions = ConcurrentHashMap<String, CompletableFuture<String>>()
}
}
fun main(args: Array<String>) {
val servletContainer = Servlets.defaultContainer()
val di = Servlets.deployment()
.setClassLoader(TimeoutServlet::class.java.classLoader)
.setContextPath("/")
.setDeploymentName("main.war")
.addServlets(
servlet("servletName", TimeoutServlet::class.java)
.addMapping("/*")
.setLoadOnStartup(1)
.setAsyncSupported(true)
)
val manager = servletContainer.addDeployment(di)
manager.deploy()
Undertow.builder()
.addHttpListener(8080, "localhost")
.setHandler(Handlers.path().addPrefixPath(
"/", manager.start()
)).build().start()
}
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>club.apibank</groupId>
<artifactId>async-test</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<name>club.apibank async-test</name>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<kotlin.version>1.3.31</kotlin.version>
<kotlin.code.style>official</kotlin.code.style>
<junit.version>4.12</junit.version>
<undertow.version>2.0.20.Final</undertow.version>
</properties>
<dependencies>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-stdlib</artifactId>
<version>${kotlin.version}</version>
</dependency>
<dependency>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-test-junit</artifactId>
<version>${kotlin.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-core</artifactId>
<version>${undertow.version}</version>
</dependency>
<dependency>
<groupId>io.undertow</groupId>
<artifactId>undertow-servlet</artifactId>
<version>${undertow.version}</version>
</dependency>
<!--<dependency>-->
<!--<groupId>io.undertow</groupId>-->
<!--<artifactId>undertow-websockets-jsr</artifactId>-->
<!--<version>${undertow.version}</version>-->
<!--</dependency>-->
</dependencies>
<build>
<sourceDirectory>src/main/kotlin</sourceDirectory>
<testSourceDirectory>src/test/kotlin</testSourceDirectory>
<plugins>
<plugin>
<groupId>org.jetbrains.kotlin</groupId>
<artifactId>kotlin-maven-plugin</artifactId>
<version>${kotlin.version}</version>
<executions>
<execution>
<id>compile</id>
<phase>compile</phase>
<goals>
<goal>compile</goal>
</goals>
</execution>
<execution>
<id>test-compile</id>
<phase>test-compile</phase>
<goals>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment