ServletServer.kt

  1. package com.hexagonkt.http.server.servlet

  2. import com.hexagonkt.core.text.AnsiColor.BLUE
  3. import com.hexagonkt.core.text.AnsiEffect.BOLD
  4. import com.hexagonkt.core.text.AnsiColor.CYAN
  5. import com.hexagonkt.core.text.AnsiColor.MAGENTA
  6. import com.hexagonkt.core.text.Ansi.RESET
  7. import com.hexagonkt.core.Jvm
  8. import com.hexagonkt.core.text.prependIndent
  9. import com.hexagonkt.core.require
  10. import com.hexagonkt.core.logging.Logger
  11. import com.hexagonkt.http.server.HttpServer
  12. import com.hexagonkt.http.server.HttpServerSettings
  13. import com.hexagonkt.http.handlers.HttpHandler
  14. import com.hexagonkt.http.handlers.OnHandler
  15. import jakarta.servlet.*
  16. import java.lang.management.ManagementFactory
  17. import java.util.*

  18. /**
  19.  * Adapter to run a router inside a Servlets container. It is not a standard engine as it is not
  20.  * started/stopped (not passed to an [HttpServer]).
  21.  */
  22. abstract class ServletServer(
  23.     private val handler: HttpHandler = OnHandler { this },
  24.     private val settings: HttpServerSettings = HttpServerSettings(),
  25. ) : ServletContextListener {

  26.     private companion object {
  27.         val logger: Logger = Logger(ServletServer::class)
  28.     }

  29.     override fun contextInitialized(sce: ServletContextEvent) {
  30.         val startTimestamp = System.nanoTime()

  31.         val servletFilter = ServletFilter(handler)
  32.         // Let's be a good JEE citizen
  33.         val servletContext = sce.servletContext
  34.         servletFilter.init(object : FilterConfig {
  35.             val params = Hashtable<String, String>(1).apply { put("filterName", filterName) }
  36.             override fun getFilterName(): String = ServletFilter::class.java.name
  37.             override fun getServletContext(): ServletContext = servletContext
  38.             override fun getInitParameter(name: String): String = params.require(name)
  39.             override fun getInitParameterNames(): Enumeration<String> = params.keys()
  40.         })
  41.         val filter = servletContext.addFilter("filters", servletFilter)
  42.         filter.addMappingForUrlPatterns(EnumSet.allOf(DispatcherType::class.java), true, "/*")

  43.         logger.info { "Server started\n${createBanner(System.nanoTime() - startTimestamp)}" }
  44.     }

  45.     override fun contextDestroyed(sce: ServletContextEvent?) {
  46.         logger.info { "Server context destroyed" }
  47.     }

  48.     private fun createBanner(startUpTimestamp: Long): String {

  49.         val heap = ManagementFactory.getMemoryMXBean().heapMemoryUsage
  50.         val jvmMemory = "%,d".format(heap.init / 1024)
  51.         val usedMemory = "%,d".format(heap.used / 1024)
  52.         val bootTime = "%01.3f".format(ManagementFactory.getRuntimeMXBean().uptime / 1e3)
  53.         val startUpTime = "%,.0f".format(startUpTimestamp / 1e6)

  54.         val serverAdapterValue = "$BOLD$CYAN${javaClass.simpleName}$RESET"

  55.         val hostnameValue = "$BLUE${Jvm.hostName}$RESET"
  56.         val cpuCountValue = "$BLUE${Jvm.cpuCount}$RESET"
  57.         val jvmMemoryValue = "$BLUE$jvmMemory$RESET"

  58.         val javaVersionValue = "$BOLD${BLUE}Java ${Jvm.version}$RESET [$BLUE${Jvm.name}$RESET]"

  59.         val localeValue = "$BLUE${Jvm.localeCode}$RESET"
  60.         val timezoneValue = "$BLUE${Jvm.timeZone.id}$RESET"
  61.         val charsetValue = "$BLUE${Jvm.charset}$RESET"

  62.         val bootTimeValue = "$BOLD$MAGENTA$bootTime s$RESET"
  63.         val startUpTimeValue = "$BOLD$MAGENTA$startUpTime ms$RESET"
  64.         val usedMemoryValue = "$BOLD$MAGENTA$usedMemory KB$RESET"

  65.         val information = """

  66.             Server Adapter: $serverAdapterValue

  67.             🖥️️ Running in '$hostnameValue' with $cpuCountValue CPUs $jvmMemoryValue KB
  68.             🛠 Using $javaVersionValue
  69.             🌍 Locale: $localeValue Timezone: $timezoneValue Charset: $charsetValue

  70.             Started in $bootTimeValue (server: $startUpTimeValue) using $usedMemoryValue
  71.             🚀 Served at a JEE Server

  72.         """.trimIndent()

  73.         val banner = (settings.banner?.let { "$it\n" } ?: HttpServer.banner) + information
  74.         return banner.prependIndent()
  75.     }
  76. }