Browse Source

Hexagon (#2940)

* Update Hexagon benchmark

* Update Hexagon benchmark

* Remove features not used in tests

* Remove MySql storage

* Enable Resin test

* Upgrade Hexagon to v0.11

* Disable Resin

* Fix dependencies

* Improve template rendering

* Test in travis

* Fix build script

* Restore Travis script

* Restore Travis script

* MySQL store support and MongoDB fix

* Fix MySQL test

* Fix MySQL test

* Fix MySQL test

* Travis testing

* Fix MySQL test

* Restore Travis

* Take MySQL URL from other test

* Add PostgreSQL support

* Update version and tests

* Update version and tests

* Only test Hexagon in fork

* Fix config

* Fix config

* Update version and tests

* Remove Resin tests

* Restore Travis

* Restore Travis

* Upgrade framework version

* Reduce local tests temporarily

* Disable Resin tests

* Fix Gradle Wrapper issues

* Restore Resin tests

* Add Bintray repository

* Disable Resin backend

* Restore master's

* Upgrade framework version

* Test only Hexagon changes (temporary)

* Test Hexagon in Travis CI

* Add Undertow support

* Test only Undertow

* Restore Travis jobs

* Enable all Hexagon's variants

* Optimize benchmark

* Fix documentation

* Fix documentation
Juanjo Aguililla 8 years ago
parent
commit
9b87ddba89

+ 49 - 3
frameworks/Kotlin/hexagon/benchmark_config.json

@@ -21,10 +21,33 @@
                 "os": "Linux",
                 "os": "Linux",
                 "database_os": "Linux",
                 "database_os": "Linux",
                 "display_name": "Hexagon Jetty MongoDB",
                 "display_name": "Hexagon Jetty MongoDB",
-                "notes": "http://there4.co/hexagon",
+                "notes": "http://hexagonkt.com",
                 "setup_file": "setup_jetty_mongodb",
                 "setup_file": "setup_jetty_mongodb",
                 "versus": "servlet"
                 "versus": "servlet"
             },
             },
+            "undertow_mongodb": {
+                "json_url": "/json",
+                "db_url": "/db",
+                "query_url": "/query?queries=",
+                "fortune_url": "/fortunes",
+                "update_url": "/update?queries=",
+                "plaintext_url": "/plaintext",
+                "port": 9090,
+                "approach": "Realistic",
+                "classification": "Micro",
+                "database": "mongodb",
+                "framework": "Hexagon",
+                "language": "Kotlin",
+                "orm": "Raw",
+                "platform": "Servlet",
+                "webserver": "None",
+                "os": "Linux",
+                "database_os": "Linux",
+                "display_name": "Hexagon Undertow MongoDB",
+                "notes": "http://hexagonkt.com",
+                "setup_file": "setup_undertow_mongodb",
+                "versus": "servlet"
+            },
             "jetty_postgresql": {
             "jetty_postgresql": {
                 "json_url": "/json",
                 "json_url": "/json",
                 "db_url": "/db",
                 "db_url": "/db",
@@ -44,10 +67,33 @@
                 "os": "Linux",
                 "os": "Linux",
                 "database_os": "Linux",
                 "database_os": "Linux",
                 "display_name": "Hexagon Jetty PostgreSQL",
                 "display_name": "Hexagon Jetty PostgreSQL",
-                "notes": "http://there4.co/hexagon",
+                "notes": "http://hexagonkt.com",
                 "setup_file": "setup_jetty_postgresql",
                 "setup_file": "setup_jetty_postgresql",
                 "versus": "servlet"
                 "versus": "servlet"
+            },
+            "undertow_postgresql": {
+                "json_url": "/json",
+                "db_url": "/db",
+                "query_url": "/query?queries=",
+                "fortune_url": "/fortunes",
+                "update_url": "/update?queries=",
+                "plaintext_url": "/plaintext",
+                "port": 9090,
+                "approach": "Realistic",
+                "classification": "Micro",
+                "database": "postgres",
+                "framework": "Hexagon",
+                "language": "Kotlin",
+                "orm": "Raw",
+                "platform": "Servlet",
+                "webserver": "None",
+                "os": "Linux",
+                "database_os": "Linux",
+                "display_name": "Hexagon Undertow PostgreSQL",
+                "notes": "http://hexagonkt.com",
+                "setup_file": "setup_undertow_postgresql",
+                "versus": "servlet"
             }
             }
         }
         }
     ]
     ]
-}
+}

+ 6 - 10
frameworks/Kotlin/hexagon/build.gradle

@@ -1,6 +1,6 @@
 
 
 plugins {
 plugins {
-    id 'org.jetbrains.kotlin.jvm' version '1.1.3-2'
+    id 'org.jetbrains.kotlin.jvm' version '1.1.4-2'
 }
 }
 
 
 apply from: "$gradleScripts/kotlin.gradle"
 apply from: "$gradleScripts/kotlin.gradle"
@@ -10,7 +10,7 @@ apply plugin: 'war'
 
 
 defaultTasks 'installDist'
 defaultTasks 'installDist'
 
 
-mainClassName = "co.there4.hexagon.BenchmarkKt"
+mainClassName = "com.hexagonkt.BenchmarkKt"
 applicationDefaultJvmArgs = [
 applicationDefaultJvmArgs = [
     '-Xms2G', '-Xmx2G', '-XX:+UseNUMA', '-XX:+UseParallelGC', '-XX:+AggressiveOpts'
     '-Xms2G', '-Xmx2G', '-XX:+UseNUMA', '-XX:+UseParallelGC', '-XX:+AggressiveOpts'
 ]
 ]
@@ -21,15 +21,11 @@ war {
 
 
 installDist.dependsOn 'war'
 installDist.dependsOn 'war'
 
 
-repositories {
-    jcenter ()
-    mavenCentral ()
-    maven { url  "http://dl.bintray.com/jamming/maven" }
-}
-
 dependencies {
 dependencies {
-    compile ("co.there4.hexagon:server_jetty:$hexagonVersion")
-    compile ("co.there4.hexagon:templates_pebble:$hexagonVersion")
+    compile ("com.hexagonkt:hexagon_store:$hexagonVersion")
+    compile ("com.hexagonkt:server_jetty:$hexagonVersion")
+    compile ("com.hexagonkt:server_undertow:$hexagonVersion")
+    compile ("com.hexagonkt:templates_pebble:$hexagonVersion")
 
 
     compile ("ch.qos.logback:logback-classic:$logbackVersion")
     compile ("ch.qos.logback:logback-classic:$logbackVersion")
     compile ("org.mongodb:mongodb-driver:$mongodbVersion")
     compile ("org.mongodb:mongodb-driver:$mongodbVersion")

+ 4 - 4
frameworks/Kotlin/hexagon/gradle.properties

@@ -1,10 +1,10 @@
 ahcVersion=2.0.31
 ahcVersion=2.0.31
 description=Hexagon web framework's benchmark
 description=Hexagon web framework's benchmark
-gradleScripts=https\://raw.githubusercontent.com/jaguililla/hexagon/0.16.0/gradle
-hexagonVersion=0.16.0
-hikariVersion=2.6.1
+gradleScripts=https\://raw.githubusercontent.com/hexagonkt/hexagon/0.20.0/gradle
+hexagonVersion=0.20.0
+hikariVersion=2.6.2
 jettyVersion=9.4.6.v20170531
 jettyVersion=9.4.6.v20170531
-kotlinVersion=1.1.3-2
+kotlinVersion=1.1.4-2
 logbackVersion=1.2.3
 logbackVersion=1.2.3
 mongodbVersion=3.4.2
 mongodbVersion=3.4.2
 postgresqlVersion=42.0.0
 postgresqlVersion=42.0.0

+ 1 - 1
frameworks/Kotlin/hexagon/gradle/wrapper/gradle-wrapper.properties

@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.5.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip

+ 4 - 4
frameworks/Kotlin/hexagon/readme.md

@@ -6,16 +6,16 @@ development platforms. The test utilizes Hexagon routes, serialization and datab
 
 
 ## Tests
 ## Tests
 
 
-* [Hexagon Web](/src/main/java/co/there4/hexagon/Benchmark.kt)
-* [Hexagon Storage](/src/main/java/co/there4/hexagon/BenchmarkStorage.kt)
+* [Hexagon Web](src/main/kotlin/com/hexagonkt/Benchmark.kt)
+* [Hexagon Storage](src/main/kotlin/com/hexagonkt/BenchmarkStorage.kt)
 
 
 ## Infrastructure Software Versions
 ## Infrastructure Software Versions
 
 
-* [Hexagon 0.16.x](http://there4.co/hexagon)
+* [Hexagon stable version](http://hexagonkt.com)
 
 
 ## Test URLs
 ## Test URLs
 
 
-### Jetty
+### Jetty & Undertow
 
 
 * JSON Encoding Test: http://localhost:9090/json
 * JSON Encoding Test: http://localhost:9090/json
 * Data-Store/Database Mapping Test: http://localhost:9090/db?queries=5 
 * Data-Store/Database Mapping Test: http://localhost:9090/db?queries=5 

+ 1 - 0
frameworks/Kotlin/hexagon/setup_jetty_mongodb.sh

@@ -5,4 +5,5 @@ fw_depends java mongodb
 
 
 ./gradlew -x test
 ./gradlew -x test
 export DBSTORE='mongodb'
 export DBSTORE='mongodb'
+export WEBENGINE='jetty'
 nohup build/install/hexagon/bin/hexagon &
 nohup build/install/hexagon/bin/hexagon &

+ 1 - 0
frameworks/Kotlin/hexagon/setup_jetty_postgresql.sh

@@ -5,4 +5,5 @@ fw_depends java postgresql
 
 
 ./gradlew -x test
 ./gradlew -x test
 export DBSTORE='postgresql'
 export DBSTORE='postgresql'
+export WEBENGINE='jetty'
 nohup build/install/hexagon/bin/hexagon &
 nohup build/install/hexagon/bin/hexagon &

+ 1 - 0
frameworks/Kotlin/hexagon/setup_resin_mongodb.sh

@@ -5,6 +5,7 @@ fw_depends java mongodb
 
 
 ./gradlew -x test
 ./gradlew -x test
 export DBSTORE='mongodb'
 export DBSTORE='mongodb'
+export WEBENGINE='resin'
 
 
 rm -rf $RESIN_HOME/webapps/*
 rm -rf $RESIN_HOME/webapps/*
 cp build/libs/ROOT.war $RESIN_HOME/webapps
 cp build/libs/ROOT.war $RESIN_HOME/webapps

+ 1 - 0
frameworks/Kotlin/hexagon/setup_resin_postgresql.sh

@@ -5,6 +5,7 @@ fw_depends java postgresql
 
 
 ./gradlew -x test
 ./gradlew -x test
 export DBSTORE='postgresql'
 export DBSTORE='postgresql'
+export WEBENGINE='resin'
 
 
 rm -rf $RESIN_HOME/webapps/*
 rm -rf $RESIN_HOME/webapps/*
 cp build/libs/ROOT.war $RESIN_HOME/webapps
 cp build/libs/ROOT.war $RESIN_HOME/webapps

+ 9 - 0
frameworks/Kotlin/hexagon/setup_undertow_mongodb.sh

@@ -0,0 +1,9 @@
+
+#!/bin/bash
+
+fw_depends java mongodb
+
+./gradlew -x test
+export DBSTORE='mongodb'
+export WEBENGINE='undertow'
+nohup build/install/hexagon/bin/hexagon &

+ 9 - 0
frameworks/Kotlin/hexagon/setup_undertow_postgresql.sh

@@ -0,0 +1,9 @@
+
+#!/bin/bash
+
+fw_depends java postgresql
+
+./gradlew -x test
+export DBSTORE='postgresql'
+export WEBENGINE='undertow'
+nohup build/install/hexagon/bin/hexagon &

+ 3 - 3
frameworks/Kotlin/hexagon/source_code

@@ -1,10 +1,10 @@
 ./hexagon/build.gradle
 ./hexagon/build.gradle
 ./hexagon/gradle.properties
 ./hexagon/gradle.properties
-./hexagon/src/main/kotlin/co/there4/hexagon/Benchmark.kt
-./hexagon/src/main/kotlin/co/there4/hexagon/BenchmarkStorage.kt
+./hexagon/src/main/kotlin/com/hexagonkt/Benchmark.kt
+./hexagon/src/main/kotlin/com/hexagonkt/BenchmarkStorage.kt
 ./hexagon/src/main/resources/logback.xml
 ./hexagon/src/main/resources/logback.xml
 ./hexagon/src/main/resources/service.yaml
 ./hexagon/src/main/resources/service.yaml
 ./hexagon/src/main/resources/templates/fortunes.html
 ./hexagon/src/main/resources/templates/fortunes.html
-./hexagon/src/test/kotlin/co/there4/hexagon/BenchmarkTest.kt
+./hexagon/src/test/kotlin/com/hexagonkt/BenchmarkTest.kt
 ./hexagon/src/test/resources/logback-test.xml
 ./hexagon/src/test/resources/logback-test.xml
 ./hexagon/src/test/resources/service_test.yaml
 ./hexagon/src/test/resources/service_test.yaml

+ 0 - 91
frameworks/Kotlin/hexagon/src/main/kotlin/co/there4/hexagon/Benchmark.kt

@@ -1,91 +0,0 @@
-package co.there4.hexagon
-
-import co.there4.hexagon.serialization.convertToMap
-import co.there4.hexagon.serialization.serialize
-import co.there4.hexagon.server.Call
-import co.there4.hexagon.server.Router
-import co.there4.hexagon.server.Server
-import co.there4.hexagon.server.jetty.JettyServletEngine
-import co.there4.hexagon.server.router
-import co.there4.hexagon.server.servlet.ServletServer
-import co.there4.hexagon.settings.SettingsManager.settings
-import co.there4.hexagon.templates.pebble.PebbleEngine
-
-import java.lang.System.getProperty
-import java.lang.System.getenv
-import java.util.*
-import java.util.concurrent.ThreadLocalRandom
-import javax.servlet.annotation.WebListener
-import java.net.InetAddress.getByName as address
-
-// DATA CLASSES
-internal data class Message(val message: String)
-internal data class Fortune(val _id: Int, val message: String)
-internal data class World(val _id: Int, val id: Int, val randomNumber: Int)
-
-// CONSTANTS
-private const val TEXT_MESSAGE: String = "Hello, World!"
-private const val CONTENT_TYPE_JSON = "application/json"
-private const val QUERIES_PARAM = "queries"
-
-// UTILITIES
-internal fun randomWorld() = ThreadLocalRandom.current().nextInt(WORLD_ROWS) + 1
-
-private fun Call.returnWorlds(worldsList: List<World>) {
-    val worlds = worldsList.map { it.convertToMap() - "_id" }
-    val result = if (worlds.size == 1) worlds.first().serialize() else worlds.serialize()
-
-    ok(result, CONTENT_TYPE_JSON)
-}
-
-private fun Call.getWorldsCount() = (request[QUERIES_PARAM]?.toIntOrNull() ?: 1).let {
-    when {
-        it < 1 -> 1
-        it > 500 -> 500
-        else -> it
-    }
-}
-
-// HANDLERS
-private fun Call.listFortunes(store: Store) {
-    val fortunes = store.findAllFortunes() + Fortune(0, "Additional fortune added at request time.")
-    val locale = Locale.getDefault()
-    response.contentType = "text/html; charset=utf-8"
-    template(PebbleEngine, "fortunes.html", locale, "fortunes" to fortunes.sortedBy { it.message })
-}
-
-private fun Call.getWorlds(store: Store) {
-    returnWorlds(store.findWorlds(getWorldsCount()))
-}
-
-private fun Call.updateWorlds(store: Store) {
-    returnWorlds(store.replaceWorlds(getWorldsCount()))
-}
-
-// CONTROLLER
-private fun router(): Router = router {
-    val store = createStore(getProperty("DBSTORE") ?: getenv("DBSTORE") ?: "mongodb")
-
-    before {
-        response.addHeader("Server", "Servlet/3.1")
-        response.addHeader("Transfer-Encoding", "chunked")
-        response.addHeader("Date", httpDate())
-    }
-
-    get("/plaintext") { ok(TEXT_MESSAGE, "text/plain") }
-    get("/json") { ok(Message(TEXT_MESSAGE).serialize(), CONTENT_TYPE_JSON) }
-    get("/fortunes") { listFortunes(store) }
-    get("/db") { getWorlds(store) }
-    get("/query") { getWorlds(store) }
-    get("/update") { updateWorlds(store) }
-}
-
-@WebListener class Web : ServletServer () {
-    override fun createRouter() = router()
-}
-
-internal var server: Server? = null
-
-fun main(vararg args: String) {
-    server = Server(JettyServletEngine(), settings, router()).apply { run() }
-}

+ 119 - 0
frameworks/Kotlin/hexagon/src/main/kotlin/com/hexagonkt/Benchmark.kt

@@ -0,0 +1,119 @@
+package com.hexagonkt
+
+import com.hexagonkt.helpers.systemSetting
+import com.hexagonkt.serialization.convertToMap
+import com.hexagonkt.serialization.serialize
+import com.hexagonkt.server.*
+import com.hexagonkt.server.jetty.JettyServletEngine
+import com.hexagonkt.server.servlet.ServletServer
+import com.hexagonkt.server.undertow.UndertowEngine
+import com.hexagonkt.settings.SettingsManager.settings
+import com.hexagonkt.templates.pebble.PebbleEngine
+import org.slf4j.Logger
+import org.slf4j.LoggerFactory.getLogger
+
+import java.util.*
+import java.util.concurrent.ThreadLocalRandom
+import javax.servlet.annotation.WebListener
+import java.net.InetAddress.getByName as address
+
+// DATA CLASSES
+internal data class Message(val message: String)
+internal data class Fortune(val _id: Int, val message: String)
+internal data class World(val _id: Int, val id: Int, val randomNumber: Int)
+
+// CONSTANTS
+private const val TEXT_MESSAGE: String = "Hello, World!"
+private const val CONTENT_TYPE_JSON = "application/json"
+private const val QUERIES_PARAM = "queries"
+
+private val LOGGER: Logger = getLogger("BENCHMARK_LOGGER")
+private val defaultLocale: Locale = Locale.getDefault()
+
+// UTILITIES
+internal fun randomWorld() = ThreadLocalRandom.current().nextInt(WORLD_ROWS) + 1
+
+private fun Call.returnWorlds(worldsList: List<World>) {
+    val worlds = worldsList.map { it.convertToMap() - "_id" }
+    ok(worlds.serialize(), CONTENT_TYPE_JSON)
+}
+
+private fun Call.getWorldsCount() = (request[QUERIES_PARAM]?.toIntOrNull() ?: 1).let {
+    when {
+        it < 1 -> 1
+        it > 500 -> 500
+        else -> it
+    }
+}
+
+// HANDLERS
+private fun Call.listFortunes(store: Store) {
+    val fortunes = store.findAllFortunes() + Fortune(0, "Additional fortune added at request time.")
+    val sortedFortunes = fortunes.sortedBy { it.message }
+    response.contentType = "text/html;charset=utf-8"
+    template(PebbleEngine, "fortunes.html", defaultLocale, "fortunes" to sortedFortunes)
+}
+
+private fun Call.dbQuery(store: Store) {
+    val world = store.findWorlds(1).first().convertToMap() - "_id"
+    ok(world.serialize(), CONTENT_TYPE_JSON)
+}
+
+private fun Call.getWorlds(store: Store) {
+    returnWorlds(store.findWorlds(getWorldsCount()))
+}
+
+private fun Call.updateWorlds(store: Store) {
+    returnWorlds(store.replaceWorlds(getWorldsCount()))
+}
+
+// CONTROLLER
+private fun router(): Router = router {
+    val store = benchmarkStore ?: error("Invalid Store")
+
+    before {
+        response.addHeader("Server", "Servlet/3.1")
+        response.addHeader("Transfer-Encoding", "chunked")
+        response.addHeader("Date", httpDate())
+    }
+
+    get("/plaintext") { ok(TEXT_MESSAGE, "text/plain") }
+    get("/json") { ok(Message(TEXT_MESSAGE).serialize(), CONTENT_TYPE_JSON) }
+    get("/fortunes") { listFortunes(store) }
+    get("/db") { dbQuery(store) }
+    get("/query") { getWorlds(store) }
+    get("/update") { updateWorlds(store) }
+}
+
+@WebListener class Web : ServletServer () {
+    init {
+        if (benchmarkStore == null)
+            benchmarkStore = createStore(systemSetting("DBSTORE", "mongodb"))
+    }
+
+    override fun createRouter() = router()
+}
+
+internal var benchmarkStore: Store? = null
+internal var benchmarkServer: Server? = null
+
+internal fun createEngine(engine: String): ServerEngine = when (engine) {
+    "jetty" -> JettyServletEngine()
+    "undertow" -> UndertowEngine()
+    else -> error("Unsupported server engine")
+}
+
+fun main(vararg args: String) {
+    val engine = createEngine(systemSetting("WEBENGINE", "jetty"))
+    benchmarkStore = createStore(systemSetting("DBSTORE", "mongodb"))
+
+    LOGGER.info("""
+            Benchmark set up:
+                - Engine: {}
+                - Store: {}
+        """.trimIndent(),
+        engine.javaClass.name,
+        benchmarkStore?.javaClass?.name)
+
+    benchmarkServer = Server(engine, settings, router()).apply { run() }
+}

+ 28 - 20
frameworks/Kotlin/hexagon/src/main/kotlin/co/there4/hexagon/BenchmarkStorage.kt → frameworks/Kotlin/hexagon/src/main/kotlin/com/hexagonkt/BenchmarkStorage.kt

@@ -1,27 +1,28 @@
-package co.there4.hexagon
+package com.hexagonkt
 
 
-import co.there4.hexagon.settings.SettingsManager.settings
-import co.there4.hexagon.store.MongoIdRepository
-import co.there4.hexagon.store.mongoCollection
-import co.there4.hexagon.store.mongoDatabase
+import com.hexagonkt.helpers.systemSetting
+import com.hexagonkt.settings.SettingsManager.settings
+import com.hexagonkt.settings.SettingsManager.setting
+import com.hexagonkt.store.MongoIdRepository
+import com.hexagonkt.store.mongoCollection
+import com.hexagonkt.store.mongoDatabase
 
 
 import com.zaxxer.hikari.HikariConfig
 import com.zaxxer.hikari.HikariConfig
 import com.zaxxer.hikari.HikariDataSource
 import com.zaxxer.hikari.HikariDataSource
 
 
-import java.lang.System.getenv
 import java.sql.Connection
 import java.sql.Connection
 import java.sql.ResultSet.CONCUR_READ_ONLY
 import java.sql.ResultSet.CONCUR_READ_ONLY
 import java.sql.ResultSet.TYPE_FORWARD_ONLY
 import java.sql.ResultSet.TYPE_FORWARD_ONLY
-import javax.sql.DataSource
+import kotlin.reflect.KClass
 
 
 import kotlin.reflect.KProperty1
 import kotlin.reflect.KProperty1
 
 
 internal const val WORLD_ROWS = 10000
 internal const val WORLD_ROWS = 10000
 
 
-private val DB_HOST = getenv("DBHOST") ?: "localhost"
-private val DB_NAME = settings["database"] as? String ?: "hello_world"
-private val WORLD_NAME: String = settings["worldCollection"] as? String ?: "world"
-private val FORTUNE_NAME: String = settings["fortuneCollection"] as? String ?: "fortune"
+private val DB_HOST = systemSetting("DBHOST", "localhost")
+private val DB_NAME = setting("database", "hello_world")
+private val WORLD_NAME: String = setting("worldCollection", "world")
+private val FORTUNE_NAME: String = setting("fortuneCollection", "fortune")
 
 
 private val postgresqlUrl = "jdbc:postgresql://$DB_HOST/$DB_NAME?" +
 private val postgresqlUrl = "jdbc:postgresql://$DB_HOST/$DB_NAME?" +
     "jdbcCompliantTruncation=false&" +
     "jdbcCompliantTruncation=false&" +
@@ -50,22 +51,25 @@ internal interface Store {
     fun findAllFortunes(): List<Fortune>
     fun findAllFortunes(): List<Fortune>
     fun findWorlds(count: Int): List<World>
     fun findWorlds(count: Int): List<World>
     fun replaceWorlds(count: Int): List<World>
     fun replaceWorlds(count: Int): List<World>
+    fun close()
 }
 }
 
 
 private class MongoDbStore : Store {
 private class MongoDbStore : Store {
     private val database = mongoDatabase("mongodb://$DB_HOST/$DB_NAME")
     private val database = mongoDatabase("mongodb://$DB_HOST/$DB_NAME")
 
 
-    private val worldRepository = repository(WORLD_NAME, World::_id)
-    private val fortuneRepository = repository(FORTUNE_NAME, Fortune::_id)
+    private val worldRepository = repository(WORLD_NAME, World::class, World::_id)
+    private val fortuneRepository = repository(FORTUNE_NAME, Fortune::class, Fortune::_id)
 
 
     // TODO Find out why it fails when creating index '_id' with background: true
     // TODO Find out why it fails when creating index '_id' with background: true
-    private inline fun <reified T : Any> repository(name: String, key: KProperty1<T, Int>) =
-        MongoIdRepository(T::class, mongoCollection(name, database), key, indexOrder = null)
+    private fun <T : Any> repository(name: String, type: KClass<T>, key: KProperty1<T, Int>) =
+        MongoIdRepository(type, mongoCollection(name, database), key, indexOrder = null)
+
+    override fun close() { /* Not needed */ }
 
 
     override fun findAllFortunes() = fortuneRepository.findObjects().toList()
     override fun findAllFortunes() = fortuneRepository.findObjects().toList()
 
 
     override fun findWorlds(count: Int) =
     override fun findWorlds(count: Int) =
-        (1..count).map { worldRepository.find(randomWorld()) }.filterNotNull()
+        (1..count).mapNotNull { worldRepository.find(randomWorld()) }
 
 
     override fun replaceWorlds(count: Int) = (1..count)
     override fun replaceWorlds(count: Int) = (1..count)
         .map { worldRepository.find(randomWorld())?.copy(randomNumber = randomWorld()) }
         .map { worldRepository.find(randomWorld())?.copy(randomNumber = randomWorld()) }
@@ -82,17 +86,21 @@ private class SqlStore(jdbcUrl: String) : Store {
     private val UPDATE_WORLD = "update world set randomNumber = ? where id = ?"
     private val UPDATE_WORLD = "update world set randomNumber = ? where id = ?"
     private val SELECT_ALL_FORTUNES = "select * from fortune"
     private val SELECT_ALL_FORTUNES = "select * from fortune"
 
 
-    private val DATA_SOURCE: DataSource
+    private val DATA_SOURCE: HikariDataSource
 
 
     init {
     init {
         val config = HikariConfig()
         val config = HikariConfig()
         config.jdbcUrl = jdbcUrl
         config.jdbcUrl = jdbcUrl
-        config.maximumPoolSize =  settings["maximumPoolSize"] as? Int ?: 32
-        config.username = settings["databaseUsername"] as? String ?: "benchmarkdbuser"
-        config.password = settings["databasePassword"] as? String ?:  "benchmarkdbpass"
+        config.maximumPoolSize =  setting("maximumPoolSize", 16)
+        config.username = setting("databaseUsername", "benchmarkdbuser")
+        config.password = setting("databasePassword", "benchmarkdbpass")
         DATA_SOURCE = HikariDataSource(config)
         DATA_SOURCE = HikariDataSource(config)
     }
     }
 
 
+    override fun close() {
+        DATA_SOURCE.close()
+    }
+
     override fun findAllFortunes(): List<Fortune> {
     override fun findAllFortunes(): List<Fortune> {
         var fortunes = listOf<Fortune>()
         var fortunes = listOf<Fortune>()
 
 

+ 2 - 0
frameworks/Kotlin/hexagon/src/main/resources/logback.xml

@@ -22,4 +22,6 @@
     <appender-ref ref="console" />
     <appender-ref ref="console" />
     <appender-ref ref="file" />
     <appender-ref ref="file" />
   </root>
   </root>
+
+  <logger name="BENCHMARK_LOGGER" level="info" />
 </configuration>
 </configuration>

+ 23 - 10
frameworks/Kotlin/hexagon/src/test/kotlin/co/there4/hexagon/BenchmarkTest.kt → frameworks/Kotlin/hexagon/src/test/kotlin/com/hexagonkt/BenchmarkTest.kt

@@ -1,9 +1,11 @@
-package co.there4.hexagon
+package com.hexagonkt
 
 
-import co.there4.hexagon.serialization.parse
-import co.there4.hexagon.client.Client
-import co.there4.hexagon.server.HttpMethod.GET
+import com.hexagonkt.serialization.parse
+import com.hexagonkt.client.Client
+import com.hexagonkt.serialization.parseList
+import com.hexagonkt.server.HttpMethod.GET
 import org.asynchttpclient.Response
 import org.asynchttpclient.Response
+import org.testng.annotations.AfterClass
 import org.testng.annotations.BeforeClass
 import org.testng.annotations.BeforeClass
 import org.testng.annotations.Test
 import org.testng.annotations.Test
 import java.lang.System.setProperty
 import java.lang.System.setProperty
@@ -12,17 +14,23 @@ import kotlin.test.assertFailsWith
 internal const val THREADS = 4
 internal const val THREADS = 4
 internal const val TIMES = 2
 internal const val TIMES = 2
 
 
-class BenchmarkMongoDbTest : BenchmarkTest("mongodb")
-class BenchmarkPostgreSqlTest : BenchmarkTest("postgresql")
+class BenchmarkJettyMongoDbTest : BenchmarkTest("jetty", "mongodb")
+class BenchmarkJettyPostgreSqlTest : BenchmarkTest("jetty", "postgresql")
+
+class BenchmarkUndertowMongoDbTest : BenchmarkTest("undertow", "mongodb")
+class BenchmarkUndertowPostgreSqlTest : BenchmarkTest("undertow", "postgresql")
 
 
 @Test(threadPoolSize = THREADS, invocationCount = TIMES)
 @Test(threadPoolSize = THREADS, invocationCount = TIMES)
-abstract class BenchmarkTest(val databaseEngine: String) {
-    private val client by lazy { Client("http://localhost:${server?.runtimePort}") }
+@Suppress("MemberVisibilityCanPrivate")
+abstract class BenchmarkTest(private val webEngine: String, private val databaseEngine: String) {
+    private val client by lazy { Client("http://localhost:${benchmarkServer?.runtimePort}") }
 
 
     @BeforeClass fun warmup() {
     @BeforeClass fun warmup() {
         setProperty("DBSTORE", databaseEngine)
         setProperty("DBSTORE", databaseEngine)
+        setProperty("WEBENGINE", webEngine)
         main()
         main()
 
 
+        @Suppress("ConstantConditionIf")
         val warmupRounds = if (THREADS > 1) 2 else 0
         val warmupRounds = if (THREADS > 1) 2 else 0
         (1..warmupRounds).forEach {
         (1..warmupRounds).forEach {
             json()
             json()
@@ -49,6 +57,11 @@ abstract class BenchmarkTest(val databaseEngine: String) {
         }
         }
     }
     }
 
 
+    @AfterClass fun cooldown() {
+        benchmarkStore?.close()
+        benchmarkServer?.stop()
+    }
+
     fun store() {
     fun store() {
         assertFailsWith<IllegalStateException> {
         assertFailsWith<IllegalStateException> {
             createStore("invalid")
             createStore("invalid")
@@ -59,7 +72,7 @@ abstract class BenchmarkTest(val databaseEngine: String) {
         val web = Web()
         val web = Web()
 
 
         val webRoutes = web.serverRouter.requestHandlers
         val webRoutes = web.serverRouter.requestHandlers
-            .map { it.route.method.first() to it.route.path.path }
+            .map { it.route.methods.first() to it.route.path.path }
 
 
         val benchmarkRoutes = listOf(
         val benchmarkRoutes = listOf(
             GET to "/plaintext",
             GET to "/plaintext",
@@ -114,7 +127,7 @@ abstract class BenchmarkTest(val databaseEngine: String) {
         val body = response.responseBody
         val body = response.responseBody
 
 
         checkResponse(response, "application/json")
         checkResponse(response, "application/json")
-        val bodyMap = body.parse(Map::class)
+        val bodyMap = body.parseList(Map::class).first()
         assert(bodyMap.containsKey(World::id.name))
         assert(bodyMap.containsKey(World::id.name))
         assert(bodyMap.containsKey(World::randomNumber.name))
         assert(bodyMap.containsKey(World::randomNumber.name))
     }
     }

+ 2 - 0
frameworks/Kotlin/hexagon/src/test/resources/logback-test.xml

@@ -13,4 +13,6 @@
   <root level="off">
   <root level="off">
     <appender-ref ref="console" />
     <appender-ref ref="console" />
   </root>
   </root>
+
+  <logger name="BENCHMARK_LOGGER" level="info" />
 </configuration>
 </configuration>