Browse Source

Merge pull request #1601 from sxend/feature/akka-http

add framework akka-http 1.0-RC2
Mike Smith 10 years ago
parent
commit
fb06a66926
27 changed files with 732 additions and 0 deletions
  1. 1 0
      .travis.yml
  2. 10 0
      frameworks/Scala/akka-http/.gitignore
  3. 27 0
      frameworks/Scala/akka-http/benchmark_config.json
  4. 23 0
      frameworks/Scala/akka-http/build.sbt
  5. 3 0
      frameworks/Scala/akka-http/install.sh
  6. 1 0
      frameworks/Scala/akka-http/project/assembly.sbt
  7. 1 0
      frameworks/Scala/akka-http/project/build.properties
  8. 8 0
      frameworks/Scala/akka-http/setup.sh
  9. 30 0
      frameworks/Scala/akka-http/source_code
  10. 24 0
      frameworks/Scala/akka-http/src/main/resources/application.conf
  11. 12 0
      frameworks/Scala/akka-http/src/main/resources/templates/fortunes.mustache
  12. 37 0
      frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/Bootstrap.scala
  13. 30 0
      frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/Components.scala
  14. 7 0
      frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/Main.scala
  15. 28 0
      frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/RequestMapping.scala
  16. 13 0
      frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/datastore/DataStore.scala
  17. 118 0
      frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/datastore/MySqlDataStore.scala
  18. 3 0
      frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/entity/Fortune.scala
  19. 3 0
      frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/entity/World.scala
  20. 58 0
      frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/DbHandler.scala
  21. 42 0
      frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/FortunesHandler.scala
  22. 37 0
      frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/JsonHandler.scala
  23. 18 0
      frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/PlaintextHandler.scala
  24. 80 0
      frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/QueriesHandler.scala
  25. 76 0
      frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/UpdatesHandler.scala
  26. 10 0
      frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/util/RandomGenerator.scala
  27. 32 0
      frameworks/Scala/akka-http/src/test/scala/com/typesafe/akka/http/benchmark/DbHandlerSpec.scala

+ 1 - 0
.travis.yml

@@ -156,6 +156,7 @@ env:
     - "TESTDIR=Scala/spray-es"
     - "TESTDIR=Scala/unfiltered"
     - "TESTDIR=Scala/http4s"
+    - "TESTDIR=Scala/akka-http"
     - "TESTDIR=Ur/urweb"
 
 before_install:

+ 10 - 0
frameworks/Scala/akka-http/.gitignore

@@ -0,0 +1,10 @@
+target/
+project/target
+bin/
+logs/
+.cache
+.classpath
+.project
+/bin/
+.idea/
+*.iml

+ 27 - 0
frameworks/Scala/akka-http/benchmark_config.json

@@ -0,0 +1,27 @@
+{
+  "framework": "akka-http",
+  "tests": [{
+      "default": {
+        "setup_file": "setup",
+        "json_url": "/json",
+        "plaintext_url": "/plaintext",
+        "db_url": "/db",
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes",
+        "update_url": "/updates?queries=",
+        "port": 9000,
+        "approach": "Realistic",
+        "classification": "Micro",
+        "database": "MySQL",
+        "framework": "akka-http",
+        "language": "Scala",
+        "platform": "Akka",
+        "webserver": "None",
+        "os": "Linux",
+        "orm": "Raw",
+        "database_os": "Linux",
+        "display_name": "akka-http",
+        "notes": ""
+    }
+  }]
+}

+ 23 - 0
frameworks/Scala/akka-http/build.sbt

@@ -0,0 +1,23 @@
+organization := "com.typesafe.akka"
+
+name := "akka-http-benchmark"
+
+version := "0.0.1-SNAPSHOT"
+
+scalaVersion := "2.11.6"
+
+resolvers += "Akka Snapshot Repository" at "http://repo.akka.io/snapshots/"
+
+libraryDependencies ++= Seq(
+  "com.typesafe.akka" %% "akka-http-core-experimental" % "1.0-RC2",
+  "com.typesafe.akka" %% "akka-http-scala-experimental" % "1.0-RC2",
+  "com.typesafe.akka" %% "akka-http-spray-json-experimental" % "1.0-RC2",
+  "mysql" % "mysql-connector-java" % "5.1.35",
+  "org.apache.commons" % "commons-dbcp2" % "2.1",
+  "org.scalatra.scalate" %% "scalate-core" % "1.7.0",
+  "org.scalatest" %% "scalatest" % "2.2.4" % "test"
+)
+
+assemblyJarName in assembly := "akka-http-benchmark.jar"
+
+mainClass in assembly := Some("com.typesafe.akka.http.benchmark.Main")

+ 3 - 0
frameworks/Scala/akka-http/install.sh

@@ -0,0 +1,3 @@
+#!/bin/bash
+
+fw_depends java8 sbt

+ 1 - 0
frameworks/Scala/akka-http/project/assembly.sbt

@@ -0,0 +1 @@
+addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.13.0")

+ 1 - 0
frameworks/Scala/akka-http/project/build.properties

@@ -0,0 +1 @@
+sbt.version=0.13.8

+ 8 - 0
frameworks/Scala/akka-http/setup.sh

@@ -0,0 +1,8 @@
+#!/bin/bash
+
+source $IROOT/java8.installed
+export SBT_HOME=${IROOT}/sbt
+
+${SBT_HOME}/bin/sbt 'assembly'
+
+java -server -jar target/scala-2.11/akka-http-benchmark.jar

+ 30 - 0
frameworks/Scala/akka-http/source_code

@@ -0,0 +1,30 @@
+./akka-http/src/main
+./akka-http/src/main/resources
+./akka-http/src/main/resources/application.conf
+./akka-http/src/main/resources/templates
+./akka-http/src/main/resources/templates/fortunes.mustache
+./akka-http/src/main/scala
+./akka-http/src/main/scala/com
+./akka-http/src/main/scala/com/typesafe
+./akka-http/src/main/scala/com/typesafe/akka
+./akka-http/src/main/scala/com/typesafe/akka/http
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/Bootstrap.scala
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/Components.scala
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/datastore
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/datastore/DataStore.scala
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/datastore/MySqlDataStore.scala
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/entity
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/entity/Fortune.scala
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/entity/World.scala
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/DbHandler.scala
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/FortunesHandler.scala
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/JsonHandler.scala
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/PlaintextHandler.scala
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/QueriesHandler.scala
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/UpdatesHandler.scala
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/Main.scala
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/RequestMapping.scala
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/util
+./akka-http/src/main/scala/com/typesafe/akka/http/benchmark/util/RandomGenerator.scala

+ 24 - 0
frameworks/Scala/akka-http/src/main/resources/application.conf

@@ -0,0 +1,24 @@
+akka {
+  actor {
+    default-dispatcher {
+      throughput = 50
+    }
+  }
+  http {
+    benchmark {
+      host: 0.0.0.0
+      port: 9000
+      mysql {
+        dbhost: "0.0.0.0"
+        dbhost: ${?DATABASE_HOST}
+        dbuser: root
+        dbpass: ""
+        jdbc-url: "jdbc:mysql://"${akka.http.benchmark.mysql.dbhost}":3306/hello_world?jdbcCompliantTruncation=false&elideSetAutoCommits=true&useLocalSessionState=true&cachePrepStmts=true&cacheCallableStmts=true&alwaysSendSetIsolation=false&prepStmtCacheSize=4096&cacheServerConfiguration=true&prepStmtCacheSqlLimit=2048&zeroDateTimeBehavior=convertToNull&traceProtocol=false&useUnbufferedInput=false&useReadAheadInput=false&maintainTimeStats=false&useServerPrepStmts&cacheRSMetadata=true"
+        min-idle: 30
+        max-idle: 30
+        max-total: -1
+        thread-pool-size: 100
+      }
+    }
+  }
+}

+ 12 - 0
frameworks/Scala/akka-http/src/main/resources/templates/fortunes.mustache

@@ -0,0 +1,12 @@
+<!DOCTYPE html>
+<html>
+    <head><title>Fortunes</title></head>
+    <body>
+        <table>
+            <tr><th>id</th><th>message</th></tr>
+                {{#fortunes}}
+            <tr><td>{{id}}</td><td>{{message}}</td></tr>
+                {{/fortunes}}
+        </table>
+    </body>
+</html>

+ 37 - 0
frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/Bootstrap.scala

@@ -0,0 +1,37 @@
+package com.typesafe.akka.http.benchmark
+
+import akka.actor.ActorSystem
+import akka.http.scaladsl.Http
+import akka.http.scaladsl.server.{Route, RoutingLog, RoutingSettings, RoutingSetup}
+import akka.stream.ActorFlowMaterializer
+import akka.stream.scaladsl.Sink._
+import com.typesafe.config.Config
+
+trait Bootstrap {
+  def run(): Unit
+}
+
+class BenchmarkBootstrap(components: {
+  val config: Config
+  val system: ActorSystem
+  val route: Route
+}) extends Bootstrap {
+  implicit val system = components.system
+  val config = components.config
+
+  import system.dispatcher
+
+  override def run(): Unit = {
+
+    implicit val routingLog = RoutingLog(system.log)
+    implicit val materializer = ActorFlowMaterializer()
+    implicit val settings = RoutingSettings.default(system)
+    implicit val setup = RoutingSetup.apply
+    val server = Http(components.system).bind(config.getString("akka.http.benchmark.host"), config.getInt("akka.http.benchmark.port"))
+    server.to {
+      foreach { connection =>
+        connection.handleWithAsyncHandler(Route.asyncHandler(components.route))
+      }
+    }.run()
+  }
+}

+ 30 - 0
frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/Components.scala

@@ -0,0 +1,30 @@
+package com.typesafe.akka.http.benchmark
+
+import akka.actor.ActorSystem
+import akka.http.scaladsl.server.Route
+import com.typesafe.akka.http.benchmark.datastore.{DataStore, MySqlDataStore}
+import com.typesafe.akka.http.benchmark.handlers._
+import com.typesafe.akka.http.benchmark.util.RandomGenerator
+import com.typesafe.config.{Config, ConfigFactory}
+import org.fusesource.scalate.TemplateEngine
+
+import scala.concurrent.ExecutionContext
+
+trait Components {
+  implicit val system = ActorSystem("akka-http-benchmark")
+  implicit val executionContext: ExecutionContext = system.dispatcher
+  lazy val config: Config = ConfigFactory.load
+  lazy val randomGenerator = new RandomGenerator(this)
+  lazy val dataStore: DataStore = new MySqlDataStore(this)
+  lazy val plaintextHandler = new PlaintextHandler(this)
+  lazy val jsonHandler = new JsonHandler(this)
+  lazy val dbHandler = new DbHandler(this)
+  lazy val queriesHandler = new QueriesHandler(this)
+  lazy val fortunesHandler = new FortunesHandler(this)
+  lazy val updatesHandler = new UpdatesHandler(this)
+  lazy val route: Route = new RequestMapping(this).asRoute
+  lazy val bootstrap: Bootstrap = new BenchmarkBootstrap(this)
+  lazy val templateEngine: TemplateEngine = new TemplateEngine
+}
+
+object Components extends Components

+ 7 - 0
frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/Main.scala

@@ -0,0 +1,7 @@
+package com.typesafe.akka.http.benchmark
+
+object Main {
+  def main(args: Array[String]): Unit = {
+    Components.bootstrap.run()
+  }
+}

+ 28 - 0
frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/RequestMapping.scala

@@ -0,0 +1,28 @@
+package com.typesafe.akka.http.benchmark
+
+import akka.http.scaladsl.model.headers.Connection
+import akka.http.scaladsl.server.Directives._
+import akka.http.scaladsl.server.Route
+import com.typesafe.akka.http.benchmark.handlers._
+
+class RequestMapping(components: {
+  val plaintextHandler: PlaintextHandler
+  val jsonHandler: JsonHandler
+  val dbHandler: DbHandler
+  val queriesHandler: QueriesHandler
+  val fortunesHandler: FortunesHandler
+  val updatesHandler: UpdatesHandler
+}) {
+  val plaintext = components.plaintextHandler.endpoint
+  val json = components.jsonHandler.endpoint
+  val db = components.dbHandler.endpoint
+  val queries = components.queriesHandler.endpoint
+  val fortunes = components.fortunesHandler.endpoint
+  val updates = components.updatesHandler.endpoint
+
+  def asRoute: Route = {
+    respondWithHeader(Connection("Keep-Alive")) {
+       plaintext ~ json ~ db ~ queries ~ fortunes ~ updates
+    }
+  }
+}

+ 13 - 0
frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/datastore/DataStore.scala

@@ -0,0 +1,13 @@
+package com.typesafe.akka.http.benchmark.datastore
+
+import com.typesafe.akka.http.benchmark.entity.{Fortune, World}
+
+import scala.concurrent.Future
+
+trait DataStore {
+  def findOne(id: Int): Future[World]
+
+  def updateOne(id: Int, randomNumber: Int): Future[Boolean]
+
+  def getFortunes: Future[List[Fortune]]
+}

+ 118 - 0
frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/datastore/MySqlDataStore.scala

@@ -0,0 +1,118 @@
+package com.typesafe.akka.http.benchmark.datastore
+
+import java.sql.ResultSet
+import java.util.Properties
+import java.util.concurrent.Executors
+
+import akka.actor.ActorSystem
+import com.typesafe.akka.http.benchmark.entity.{Fortune, World}
+import com.typesafe.config.Config
+import org.apache.commons.dbcp2.{DriverManagerConnectionFactory, PoolableConnection, PoolableConnectionFactory, PoolingDataSource}
+import org.apache.commons.pool2.impl.GenericObjectPool
+
+import scala.concurrent.{ExecutionContext, Future, Promise}
+
+class MySqlDataStore(components: {
+  val system: ActorSystem
+  val config: Config
+}) extends DataStore {
+  val config = components.config.getConfig("akka.http.benchmark.mysql")
+
+  private val dataSource: PoolingDataSource[PoolableConnection] = {
+    val jdbcUrl = config.getString("jdbc-url")
+    val props = new Properties()
+    props.setProperty("user", config.getString("dbuser"))
+    props.setProperty("password", config.getString("dbpass"))
+
+    val connectionFactory = new DriverManagerConnectionFactory(jdbcUrl, props)
+    val poolableConnectionFactory = new PoolableConnectionFactory(connectionFactory, null)
+    val connectionPool = new GenericObjectPool[PoolableConnection](poolableConnectionFactory)
+    connectionPool.setTestOnBorrow(true)
+    connectionPool.setMinIdle(config.getString("min-idle").toInt)
+    connectionPool.setMaxIdle(config.getString("max-idle").toInt)
+    connectionPool.setMaxTotal(config.getString("max-total").toInt)
+    poolableConnectionFactory.setPool(connectionPool)
+    poolableConnectionFactory.setValidationQuery("select 1")
+    new PoolingDataSource[PoolableConnection](connectionPool)
+  }
+  private implicit val executionContext: ExecutionContext = {
+    val size = config.getInt("thread-pool-size")
+    val threadPool = Executors.newFixedThreadPool(size)
+    new ExecutionContext {
+      override def reportFailure(cause: Throwable): Unit = {
+        components.system.log.error(cause, "exception in mysql thread pool")
+      }
+
+      override def execute(runnable: Runnable): Unit = {
+        threadPool.execute(runnable)
+      }
+    }
+  }
+
+  override def findOne(id: Int): Future[World] = {
+    val select = "select id, randomNumber from World where id = ?"
+    val promise = Promise[World]()
+    executionContext.execute(new Runnable {
+      override def run(): Unit = {
+        val conn = dataSource.getConnection
+        val stmt = conn.prepareStatement(select)
+        stmt.setInt(1, id)
+        val rs = stmt.executeQuery()
+        val world = rs.next() match {
+          case true =>
+            World(rs.getInt("id"), rs.getInt("randomNumber"))
+        }
+        rs.close()
+        stmt.close()
+        conn.close()
+        promise.success(world)
+      }
+    })
+    promise.future
+  }
+
+  override def updateOne(id: Int, randomNumber: Int): Future[Boolean] = {
+    val update = "update World set randomNumber = ? where id = ?"
+    val promise = Promise[Boolean]()
+    executionContext.execute(new Runnable {
+      override def run(): Unit = {
+        val conn = dataSource.getConnection
+        val stmt = conn.prepareStatement(update)
+        stmt.setInt(1, randomNumber)
+        stmt.setInt(2, id)
+        val n = stmt.executeUpdate()
+        stmt.close()
+        conn.close()
+        promise.success(n > 0)
+      }
+    })
+    promise.future
+  }
+
+  override def getFortunes: Future[List[Fortune]] = {
+    val select = "select id, message from Fortune"
+    val promise = Promise[List[Fortune]]()
+    executionContext.execute(new Runnable {
+      override def run(): Unit = {
+        val conn = dataSource.getConnection
+        val stmt = conn.prepareStatement(select)
+        val rs = stmt.executeQuery()
+        val fortunes = {
+          rs.map(r => Fortune(r.getInt("id"), r.getString("message"))).toList :+ Fortune(0, "Additional fortune added at request time.")
+        }.sortBy(_.message)
+        rs.close()
+        stmt.close()
+        conn.close()
+        promise.success(fortunes)
+      }
+    })
+    promise.future
+  }
+
+  implicit class RsIterator(rs: ResultSet) extends Iterator[ResultSet] {
+    def hasNext: Boolean = rs.next()
+
+    def next(): ResultSet = rs
+  }
+
+}

+ 3 - 0
frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/entity/Fortune.scala

@@ -0,0 +1,3 @@
+package com.typesafe.akka.http.benchmark.entity
+
+case class Fortune(id: Int, message: String)

+ 3 - 0
frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/entity/World.scala

@@ -0,0 +1,3 @@
+package com.typesafe.akka.http.benchmark.entity
+
+case class World(id: Int, randomNumber: Int)

+ 58 - 0
frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/DbHandler.scala

@@ -0,0 +1,58 @@
+package com.typesafe.akka.http.benchmark.handlers
+
+import akka.http.scaladsl.model.HttpCharsets._
+import akka.http.scaladsl.model.MediaTypes._
+import akka.http.scaladsl.model.{HttpEntity, HttpResponse, StatusCodes}
+import akka.http.scaladsl.server.Directives._
+import com.typesafe.akka.http.benchmark.datastore.DataStore
+import com.typesafe.akka.http.benchmark.entity.World
+import com.typesafe.akka.http.benchmark.util.RandomGenerator
+import spray.json.{DefaultJsonProtocol, RootJsonFormat}
+
+import scala.concurrent.ExecutionContext
+import scala.util.{Failure, Success}
+
+class DbHandler(components: {
+  val executionContext: ExecutionContext
+  val dataStore: DataStore
+  val randomGenerator: RandomGenerator
+}) {
+  val randomGenerator = components.randomGenerator
+  val dataStore = components.dataStore
+  implicit val ec = components.executionContext
+
+  import DbHandler.Protocols._
+
+  def endpoint = get {
+    path("db") {
+      onComplete(response) {
+        case Success(record) => complete(record)
+        case Failure(t) => failWith(t)
+      }
+    }
+  }
+
+  def response = {
+    val id = randomGenerator.next
+    dataStore.findOne(id).map {
+      record => new HttpResponse(StatusCodes.OK, entity = HttpEntity(record.toResponse.toJson.toString()).withContentType(`application/json`.withCharset(`UTF-8`)))
+    }
+  }
+
+}
+
+object DbHandler {
+
+  object Protocols extends DefaultJsonProtocol {
+
+    case class Response(id: Int, randomNumber: Int)
+
+    implicit val responseFormat: RootJsonFormat[Response] = jsonFormat2(Response.apply)
+
+    implicit class ToResponse(record: World) {
+      def toResponse = Response(record.id, record.randomNumber)
+    }
+
+  }
+
+}

+ 42 - 0
frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/FortunesHandler.scala

@@ -0,0 +1,42 @@
+package com.typesafe.akka.http.benchmark.handlers
+
+import akka.actor.ActorSystem
+import akka.http.scaladsl.model.HttpCharsets._
+import akka.http.scaladsl.model.MediaTypes._
+import akka.http.scaladsl.model._
+import akka.http.scaladsl.server.Directives._
+import com.typesafe.akka.http.benchmark.datastore.DataStore
+import org.fusesource.scalate.TemplateEngine
+
+import scala.concurrent.Future
+import scala.util.{Failure, Success}
+
+class FortunesHandler(components: {
+  val system: ActorSystem
+  val dataStore: DataStore
+  val templateEngine: TemplateEngine
+}) {
+
+  import components.system.dispatcher
+
+  val dataStore = components.dataStore
+  val engine = components.templateEngine
+
+  def endpoint = get {
+    path("fortunes") {
+      onComplete(response) {
+        case Success(record) => complete(record)
+        case Failure(t) => failWith(t)
+      }
+    }
+  }
+
+  def response: Future[HttpResponse] = {
+    dataStore.getFortunes.map {
+      fortunes =>
+        val body = engine.layout("/templates/fortunes.mustache", Map("fortunes" -> fortunes))
+        new HttpResponse(StatusCodes.OK, entity = HttpEntity(body).withContentType(`text/html`.withCharset(`UTF-8`)))
+    }
+
+  }
+}

+ 37 - 0
frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/JsonHandler.scala

@@ -0,0 +1,37 @@
+package com.typesafe.akka.http.benchmark.handlers
+
+import akka.http.scaladsl.model.HttpCharsets._
+import akka.http.scaladsl.model.MediaTypes._
+import akka.http.scaladsl.model._
+import akka.http.scaladsl.server.Directives._
+import spray.json.{DefaultJsonProtocol, RootJsonFormat}
+
+
+class JsonHandler(components: {
+
+}) {
+
+  import JsonHandler.Protocols._
+
+  def endpoint = get {
+    path("json") {
+      complete(response)
+    }
+  }
+
+  def response = {
+    new HttpResponse(StatusCodes.OK, entity = HttpEntity(Response("Hello, World!").toJson.toString()).withContentType(`application/json`.withCharset(`UTF-8`)))
+  }
+}
+
+object JsonHandler {
+
+  object Protocols extends DefaultJsonProtocol {
+
+    case class Response(message: String)
+
+    implicit val responseFormat: RootJsonFormat[Response] = jsonFormat1(Response.apply)
+
+  }
+
+}

+ 18 - 0
frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/PlaintextHandler.scala

@@ -0,0 +1,18 @@
+package com.typesafe.akka.http.benchmark.handlers
+
+import akka.http.scaladsl.model.HttpCharsets._
+import akka.http.scaladsl.model.MediaTypes._
+import akka.http.scaladsl.model.{HttpEntity, HttpResponse, StatusCodes}
+import akka.http.scaladsl.server.Directives._
+
+class PlaintextHandler(components: {
+
+}) {
+  def endpoint = get {
+    path("plaintext") {
+      complete(response)
+    }
+  }
+
+  def response = new HttpResponse(StatusCodes.OK, entity = HttpEntity("Hello, World!").withContentType(`text/plain`.withCharset(`UTF-8`)))
+}

+ 80 - 0
frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/QueriesHandler.scala

@@ -0,0 +1,80 @@
+package com.typesafe.akka.http.benchmark.handlers
+
+import akka.actor.ActorSystem
+import akka.http.scaladsl.model.HttpCharsets._
+import akka.http.scaladsl.model.MediaTypes._
+import akka.http.scaladsl.model.{HttpEntity, HttpResponse, StatusCodes}
+import akka.http.scaladsl.server.Directives._
+import akka.http.scaladsl.server.directives.ParameterDirectives
+import com.typesafe.akka.http.benchmark.datastore.DataStore
+import com.typesafe.akka.http.benchmark.entity.World
+import com.typesafe.akka.http.benchmark.handlers.DbHandler.Protocols._
+import com.typesafe.akka.http.benchmark.util.RandomGenerator
+import spray.json.{DefaultJsonProtocol, RootJsonFormat}
+
+import scala.concurrent.Future
+import scala.util.control.Exception._
+import scala.util.{Failure, Success}
+
+class QueriesHandler(components: {
+  val dataStore: DataStore
+  val system: ActorSystem
+  val randomGenerator: RandomGenerator
+}) {
+  val dataStore = components.dataStore
+  val randomGenerator = components.randomGenerator
+
+  import ParameterDirectives._
+  import ParamDef._
+  import ParamMagnet._
+  import components.system.dispatcher
+
+
+  def endpoint = get {
+    path("queries") {
+      parameter('queries.?) { queries => onComplete(response(queries)) {
+        case Success(worlds) => complete(worlds)
+        case Failure(t) => failWith(t)
+      }
+      }
+    }
+  }
+
+  val catcher = catching(classOf[NumberFormatException]).withApply(t => 1)
+
+  def response(queries: Option[String]): Future[HttpResponse] = {
+
+    val range = queries.map(i => catcher {
+      i.toInt
+    }).getOrElse(1).min(500).max(1)
+    Future.sequence {
+      (0 until range).map {
+        _ => randomGenerator.next
+      }.map {
+        id => dataStore.findOne(id)
+      }
+    }.map {
+      worlds => new HttpResponse(StatusCodes.OK, entity = HttpEntity(worlds.toList.map(_.toResponse).toJson.toString()).withContentType(`application/json`.withCharset(`UTF-8`)))
+    }
+
+  }
+
+}
+
+object QueriesHandler {
+
+  object Protocols extends DefaultJsonProtocol {
+
+    case class Response(id: Int, randomNumber: Int)
+
+    implicit val responseFormat: RootJsonFormat[Response] = jsonFormat2(Response.apply)
+
+    implicit val responseListFormat: RootJsonFormat[List[Response]] = listFormat[Response]
+
+    implicit class ToResponse(record: World) {
+      def toResponse = Response(record.id, record.randomNumber)
+    }
+
+  }
+
+}

+ 76 - 0
frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/handlers/UpdatesHandler.scala

@@ -0,0 +1,76 @@
+package com.typesafe.akka.http.benchmark.handlers
+
+import akka.actor.ActorSystem
+import akka.http.scaladsl.model.HttpCharsets._
+import akka.http.scaladsl.model.MediaTypes._
+import akka.http.scaladsl.model.{HttpEntity, HttpResponse, StatusCodes}
+import akka.http.scaladsl.server.Directives._
+import com.typesafe.akka.http.benchmark.datastore.DataStore
+import com.typesafe.akka.http.benchmark.entity.World
+import com.typesafe.akka.http.benchmark.util.RandomGenerator
+import spray.json.{DefaultJsonProtocol, RootJsonFormat}
+
+import scala.concurrent.Future
+import scala.util.control.Exception._
+import scala.util.{Failure, Success}
+
+class UpdatesHandler(components: {
+  val dataStore: DataStore
+  val system: ActorSystem
+  val randomGenerator: RandomGenerator
+}) {
+  val dataStore = components.dataStore
+  val randomGenerator = components.randomGenerator
+
+  import UpdatesHandler.Protocols._
+  import components.system.dispatcher
+
+  def endpoint = get {
+    path("updates") {
+      parameter('queries.?) { queries => onComplete(response(queries)) {
+        case Success(worlds) => complete(worlds)
+        case Failure(t) => failWith(t)
+      }
+      }
+    }
+  }
+
+  val catcher = catching(classOf[NumberFormatException]).withApply(t => 1)
+
+  def response(queries: Option[String]): Future[HttpResponse] = {
+    val range = queries.map(i => catcher {
+      i.toInt
+    }).getOrElse(1).min(500).max(1)
+    Future.sequence {
+      (0 until range).toList.map {
+        _ => randomGenerator.next
+      }.map {
+        id => dataStore.findOne(id)
+      }
+    }.map {
+      worlds => worlds.map(_.copy(randomNumber = randomGenerator.next))
+    }.flatMap {
+      worlds => Future.sequence(worlds.map(world => dataStore.updateOne(world.id, world.randomNumber).map(_ => world)))
+    }.map {
+      worlds => new HttpResponse(StatusCodes.OK, entity = HttpEntity(worlds.map(_.toResponse).toJson.toString()).withContentType(`application/json`.withCharset(`UTF-8`)))
+    }
+  }
+}
+
+object UpdatesHandler {
+
+  object Protocols extends DefaultJsonProtocol {
+
+    case class Response(id: Int, randomNumber: Int)
+
+    implicit val responseFormat: RootJsonFormat[Response] = jsonFormat2(Response.apply)
+
+    implicit val responseListFormat: RootJsonFormat[List[Response]] = listFormat[Response]
+
+    implicit class ToResponse(record: World) {
+      def toResponse = Response(record.id, record.randomNumber)
+    }
+
+  }
+
+}

+ 10 - 0
frameworks/Scala/akka-http/src/main/scala/com/typesafe/akka/http/benchmark/util/RandomGenerator.scala

@@ -0,0 +1,10 @@
+package com.typesafe.akka.http.benchmark.util
+
+class RandomGenerator(components: {
+
+}) {
+
+  def next: Int = {
+    (Math.random() * 10000 + 1).toInt
+  }
+}

+ 32 - 0
frameworks/Scala/akka-http/src/test/scala/com/typesafe/akka/http/benchmark/DbHandlerSpec.scala

@@ -0,0 +1,32 @@
+package com.typesafe.akka.http.benchmark
+
+import akka.http.scaladsl.server.RequestContext
+import com.typesafe.akka.http.benchmark.datastore.DataStore
+import com.typesafe.akka.http.benchmark.entity.{Fortune, World}
+import com.typesafe.akka.http.benchmark.util.RandomGenerator
+import org.scalatest._
+
+import scala.concurrent.Future
+
+class DbHandlerSpec extends FlatSpec with Matchers {
+  val components = new Components {
+    self =>
+
+    import self.system.dispatcher
+
+    override lazy val randomGenerator: RandomGenerator = new RandomGenerator(self) {
+      override def next: Int = 1
+    }
+    override lazy val dataStore: DataStore = new DataStore {
+      override def findOne(id: Int): Future[World] = {
+        Future(World(1, 1))
+      }
+
+      override def updateOne(id: Int, randomNumber: Int): Future[Boolean] = Future(true)
+
+      override def getFortunes: Future[List[Fortune]] = Future(List(Fortune(0, "Additional fortune added at request time.")))
+    }
+  }
+  "A DbHandler" should "get random record" in {
+  }
+}