ServletServer.kt
package com.hexagonkt.http.server.servlet
import com.hexagonkt.core.text.AnsiColor.BLUE
import com.hexagonkt.core.text.AnsiEffect.BOLD
import com.hexagonkt.core.text.AnsiColor.CYAN
import com.hexagonkt.core.text.AnsiColor.MAGENTA
import com.hexagonkt.core.text.Ansi.RESET
import com.hexagonkt.core.Jvm
import com.hexagonkt.core.text.prependIndent
import com.hexagonkt.core.require
import com.hexagonkt.core.logging.Logger
import com.hexagonkt.http.server.HttpServer
import com.hexagonkt.http.server.HttpServerSettings
import com.hexagonkt.http.handlers.HttpHandler
import com.hexagonkt.http.handlers.OnHandler
import jakarta.servlet.*
import java.lang.management.ManagementFactory
import java.util.*
/**
* Adapter to run a router inside a Servlets container. It is not a standard engine as it is not
* started/stopped (not passed to an [HttpServer]).
*/
abstract class ServletServer(
private val handler: HttpHandler = OnHandler { this },
private val settings: HttpServerSettings = HttpServerSettings(),
) : ServletContextListener {
private companion object {
val logger: Logger = Logger(ServletServer::class)
}
override fun contextInitialized(sce: ServletContextEvent) {
val startTimestamp = System.nanoTime()
val servletFilter = ServletFilter(handler)
// Let's be a good JEE citizen
val servletContext = sce.servletContext
servletFilter.init(object : FilterConfig {
val params = Hashtable<String, String>(1).apply { put("filterName", filterName) }
override fun getFilterName(): String = ServletFilter::class.java.name
override fun getServletContext(): ServletContext = servletContext
override fun getInitParameter(name: String): String = params.require(name)
override fun getInitParameterNames(): Enumeration<String> = params.keys()
})
val filter = servletContext.addFilter("filters", servletFilter)
filter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType::class.java), true, "/*")
logger.info { "Server started\n${createBanner(System.nanoTime() - startTimestamp)}" }
}
override fun contextDestroyed(sce: ServletContextEvent?) {
logger.info { "Server context destroyed" }
}
private fun createBanner(startUpTimestamp: Long): String {
val heap = ManagementFactory.getMemoryMXBean().heapMemoryUsage
val jvmMemory = "%,d".format(heap.init / 1024)
val usedMemory = "%,d".format(heap.used / 1024)
val bootTime = "%01.3f".format(ManagementFactory.getRuntimeMXBean().uptime / 1e3)
val startUpTime = "%,.0f".format(startUpTimestamp / 1e6)
val serverAdapterValue = "$BOLD$CYAN${javaClass.simpleName}$RESET"
val hostnameValue = "$BLUE${Jvm.hostName}$RESET"
val cpuCountValue = "$BLUE${Jvm.cpuCount}$RESET"
val jvmMemoryValue = "$BLUE$jvmMemory$RESET"
val javaVersionValue = "$BOLD${BLUE}Java ${Jvm.version}$RESET [$BLUE${Jvm.name}$RESET]"
val localeValue = "$BLUE${Jvm.localeCode}$RESET"
val timezoneValue = "$BLUE${Jvm.timeZone.id}$RESET"
val charsetValue = "$BLUE${Jvm.charset}$RESET"
val bootTimeValue = "$BOLD$MAGENTA$bootTime s$RESET"
val startUpTimeValue = "$BOLD$MAGENTA$startUpTime ms$RESET"
val usedMemoryValue = "$BOLD$MAGENTA$usedMemory KB$RESET"
val information = """
Server Adapter: $serverAdapterValue
🖥️️ Running in '$hostnameValue' with $cpuCountValue CPUs $jvmMemoryValue KB
🛠 Using $javaVersionValue
🌍 Locale: $localeValue Timezone: $timezoneValue Charset: $charsetValue
⏱ Started in $bootTimeValue (server: $startUpTimeValue) using $usedMemoryValue
🚀 Served at a JEE Server
""".trimIndent()
val banner = (settings.banner?.let { "$it\n" } ?: HttpServer.banner) + information
return banner.prependIndent()
}
}