Browse Source

Update play2-scala, play2-scala-anorm, play2-scala-slick and play2-scala-reactivemongo. All moved to play 2.4.6

Taylor Robison 9 years ago
parent
commit
e8b635938d
46 changed files with 672 additions and 498 deletions
  1. 17 27
      frameworks/Scala/play2-scala/benchmark_config.json
  2. 23 0
      frameworks/Scala/play2-scala/play2-scala-anorm/app/Filters.scala
  3. 44 27
      frameworks/Scala/play2-scala/play2-scala-anorm/app/controllers/Application.scala
  4. 10 7
      frameworks/Scala/play2-scala/play2-scala-anorm/app/models/Fortune.scala
  5. 22 18
      frameworks/Scala/play2-scala/play2-scala-anorm/app/models/World.scala
  6. 9 8
      frameworks/Scala/play2-scala/play2-scala-anorm/app/utils/DbOperation.scala
  7. 9 3
      frameworks/Scala/play2-scala/play2-scala-anorm/build.sbt
  8. 39 39
      frameworks/Scala/play2-scala/play2-scala-anorm/conf/application.conf
  9. 6 4
      frameworks/Scala/play2-scala/play2-scala-anorm/conf/routes
  10. 1 1
      frameworks/Scala/play2-scala/play2-scala-anorm/project/build.properties
  11. 1 7
      frameworks/Scala/play2-scala/play2-scala-anorm/project/plugins.sbt
  12. 12 11
      frameworks/Scala/play2-scala/play2-scala-anorm/source_code
  13. 23 0
      frameworks/Scala/play2-scala/play2-scala-reactivemongo/app/Filters.scala
  14. 118 57
      frameworks/Scala/play2-scala/play2-scala-reactivemongo/app/controllers/Application.scala
  15. 20 0
      frameworks/Scala/play2-scala/play2-scala-reactivemongo/app/views/fortune.scala.html
  16. 12 0
      frameworks/Scala/play2-scala/play2-scala-reactivemongo/app/views/main.scala.html
  17. 4 2
      frameworks/Scala/play2-scala/play2-scala-reactivemongo/build.sbt
  18. 26 44
      frameworks/Scala/play2-scala/play2-scala-reactivemongo/conf/application.conf
  19. 0 1
      frameworks/Scala/play2-scala/play2-scala-reactivemongo/conf/play.plugins
  20. 7 2
      frameworks/Scala/play2-scala/play2-scala-reactivemongo/conf/routes
  21. 1 1
      frameworks/Scala/play2-scala/play2-scala-reactivemongo/project/build.properties
  22. 1 7
      frameworks/Scala/play2-scala/play2-scala-reactivemongo/project/plugins.sbt
  23. 4 0
      frameworks/Scala/play2-scala/play2-scala-reactivemongo/source_code
  24. 23 0
      frameworks/Scala/play2-scala/play2-scala-slick/app/Filters.scala
  25. 47 32
      frameworks/Scala/play2-scala/play2-scala-slick/app/controllers/Application.scala
  26. 30 17
      frameworks/Scala/play2-scala/play2-scala-slick/app/models/Fortune.scala
  27. 32 18
      frameworks/Scala/play2-scala/play2-scala-slick/app/models/World.scala
  28. 0 47
      frameworks/Scala/play2-scala/play2-scala-slick/app/utils/DbOperation.scala
  29. 5 3
      frameworks/Scala/play2-scala/play2-scala-slick/build.sbt
  30. 9 42
      frameworks/Scala/play2-scala/play2-scala-slick/conf/application.conf
  31. 6 4
      frameworks/Scala/play2-scala/play2-scala-slick/conf/routes
  32. 1 1
      frameworks/Scala/play2-scala/play2-scala-slick/project/build.properties
  33. 1 1
      frameworks/Scala/play2-scala/play2-scala-slick/project/plugins.sbt
  34. 10 11
      frameworks/Scala/play2-scala/play2-scala-slick/source_code
  35. 23 0
      frameworks/Scala/play2-scala/play2-scala/app/Filters.scala
  36. 18 12
      frameworks/Scala/play2-scala/play2-scala/app/controllers/Application.scala
  37. 6 0
      frameworks/Scala/play2-scala/play2-scala/build.sbt
  38. 29 16
      frameworks/Scala/play2-scala/play2-scala/conf/application.conf
  39. 2 2
      frameworks/Scala/play2-scala/play2-scala/conf/routes
  40. 1 1
      frameworks/Scala/play2-scala/play2-scala/project/build.properties
  41. 1 1
      frameworks/Scala/play2-scala/play2-scala/project/plugins.sbt
  42. 2 9
      frameworks/Scala/play2-scala/play2-scala/source_code
  43. 4 4
      frameworks/Scala/play2-scala/setup_scala.sh
  44. 4 4
      frameworks/Scala/play2-scala/setup_scala_activate.sh
  45. 4 4
      frameworks/Scala/play2-scala/setup_scala_reactivemongo.sh
  46. 5 3
      frameworks/Scala/play2-scala/setup_scala_slick.sh

+ 17 - 27
frameworks/Scala/play2-scala/benchmark_config.json

@@ -21,27 +21,6 @@
         "json_url": "/json",
         "plaintext_url": "/plaintext"
       }, 
-      "activate": {
-        "display_name": "play2-scala-activate", 
-        "setup_file": "setup_scala_activate", 
-        "framework": "play2", 
-        "language": "Scala", 
-        "orm": "Full", 
-        "os": "Linux", 
-        "database": "MySQL", 
-        "approach": "Realistic", 
-        "classification": "Fullstack", 
-        "platform": "Netty", 
-        "webserver": "None", 
-        "database_os": "Linux", 
-        "notes": "", 
-        "versus": "netty", 
-        "port": "9000", 
-        "db_url": "/db", 
-        "query_url": "/queries?queries=", 
-        "fortune_url": "/fortunes", 
-        "update_url": "/update?queries="
-      }, 
       "anorm-linux": {
         "display_name": "play2-scala-anorm-linux", 
         "setup_file": "setup_scala_anorm", 
@@ -61,7 +40,9 @@
         "db_url": "/db", 
         "query_url": "/queries?queries=", 
         "fortune_url": "/fortunes", 
-        "update_url": "/update?queries="
+        "update_url": "/update?queries=",
+        "json_url": "/json",
+        "plaintext_url": "/plaintext"
       }, 
       "anorm-windows": {
         "display_name": "play2-scala-anorm-windows", 
@@ -82,7 +63,9 @@
         "db_url": "/db", 
         "query_url": "/queries?queries=", 
         "fortune_url": "/fortunes", 
-        "update_url": "/update?queries="
+        "update_url": "/update?queries=",
+        "json_url": "/json",
+        "plaintext_url": "/plaintext"
       }, 
       "reactivemongo": {
         "display_name": "play2-scala-reactivemongo", 
@@ -99,9 +82,14 @@
         "database_os": "Linux", 
         "notes": "", 
         "versus": "netty", 
-        "port": "9000", 
+        "port": "9000",
+        "sleep":"8",
         "db_url": "/db", 
-        "query_url": "/queries?queries="
+        "query_url": "/queries?queries=",
+        "fortune_url": "/fortunes", 
+        "update_url": "/update?queries=",
+        "json_url": "/json",
+        "plaintext_url": "/plaintext"
       }, 
       "slick": {
         "display_name": "play2-scala-slick", 
@@ -122,8 +110,10 @@
         "db_url": "/db", 
         "query_url": "/queries?queries=", 
         "fortune_url": "/fortunes", 
-        "update_url": "/update?queries="
+        "update_url": "/update?queries=",
+        "json_url": "/json",
+        "plaintext_url": "/plaintext"
       }
     }
   ]
-}
+}

+ 23 - 0
frameworks/Scala/play2-scala/play2-scala-anorm/app/Filters.scala

@@ -0,0 +1,23 @@
+import javax.inject.{Singleton, Inject}
+
+import play.api.http.HttpFilters
+import play.api.mvc.{RequestHeader, EssentialAction, EssentialFilter}
+import play.api.libs.concurrent.Execution.Implicits.defaultContext
+import play.mvc.Http
+
+@Singleton()
+class Filters @Inject() (headerFilter: HeaderFilter) extends HttpFilters {
+  override def filters: Seq[EssentialFilter] = Seq(
+    headerFilter)
+}
+
+@Singleton
+class HeaderFilter extends EssentialFilter {
+  def apply(next: EssentialAction) = new EssentialAction {
+    def apply(request: RequestHeader) = {
+      next(request).map(result =>
+        result.withHeaders(Http.HeaderNames.SERVER -> "EXAMPLE")  // Only here to address the WARN about this header.
+      )
+    }
+  }
+}

+ 44 - 27
frameworks/Scala/play2-scala/play2-scala-anorm/app/controllers/Application.scala

@@ -1,75 +1,92 @@
 package controllers
 
-import play.api.Play.current
-import play.api.db.DB
+import javax.inject.{Inject, Singleton}
+
 import play.api.mvc._
 import play.api.libs.json.Json
 import java.util.concurrent._
-import scala.concurrent._
-import models.{World, Fortune}
-import utils._
-import utils.DbOperation._
+import models.{WorldDAO, FortunesDAO, World, Fortune}
+import utils.DbOperation
 import scala.concurrent.Future
 
 import play.api.libs.concurrent.Execution.Implicits._
-import play.core.NamedThreadFactory
 
-object Application extends Controller {
+@Singleton()
+class Application @Inject() (fortunesDAO: FortunesDAO, worldDAO: WorldDAO, dbOperation: DbOperation)  extends Controller {
 
   // Anorm code
 
-  def getRandomWorlds(n: Int): Future[Seq[World]] = asyncDbOp { implicit connection =>
-    val random = ThreadLocalRandom.current()
+  def getRandomWorlds(n: Int): Future[Seq[World]] = dbOperation.asyncDbOp { implicit connection =>
     for (_ <- 1 to n) yield {
-      val randomId: Long = random.nextInt(TestDatabaseRows) + 1
-      World.findById(randomId)
+      worldDAO.findById(getNextRandom)
     }
   }
 
-  def getFortunes(): Future[Seq[Fortune]] = asyncDbOp { implicit connection =>
-    Fortune.getAll()
+  def getFortunes: Future[Seq[Fortune]] = dbOperation.asyncDbOp { implicit connection =>
+    fortunesDAO.getAll
   }
 
-  def updateWorlds(n: Int): Future[Seq[World]] = asyncDbOp { implicit connection =>
-    val random = ThreadLocalRandom.current()
+  def updateWorlds(n: Int): Future[Seq[World]] = dbOperation.asyncDbOp { implicit connection =>
     for(_ <- 1 to n) yield {
-      val randomId: Long = random.nextInt(TestDatabaseRows) + 1
-      val world = World.findById(random.nextInt(TestDatabaseRows) + 1)
-      val randomNumber: Long = random.nextInt(10000) + 1
-      val updatedWorld = world.copy(randomNumber = randomNumber)
-      World.updateRandom(updatedWorld)
+      val world = worldDAO.findById(getNextRandom)
+      val updatedWorld = world.copy(randomNumber = getNextRandom)
+      worldDAO.updateRandom(updatedWorld)
       updatedWorld
     }
   }
 
+  def getNextRandom: Int = {
+    ThreadLocalRandom.current().nextInt(TestDatabaseRows) + 1
+  }
+
+  // Test seems picky about headers.  Doesn't like character set being there for JSON.  Always wants Server header set.
+  // There is a Filter which adds the Server header for all types.  Below I set Content-Type as needed to get rid of
+  // warnings.
+
+  // Easy ones
+  case class HelloWorld(message: String)
+
+  def getJsonMessage = Action {
+    val helloWorld = HelloWorld(message = "Hello, World!")
+    Ok(Json.toJson(helloWorld)(Json.writes[HelloWorld])).withHeaders(CONTENT_TYPE -> "application/json")
+  }
+
+  val plaintext = Action {
+    // default headers are correct according to docs: charset included.
+    // BUT the test harness has a WARN state and says we don't need it.
+    Ok("Hello, World!").withHeaders(CONTENT_TYPE -> "text/plain")
+  }
+
   // Common code between Scala database code
 
   protected val TestDatabaseRows = 10000
 
+  import models.WorldJsonHelpers.toJson
+
   def db = Action.async {
     getRandomWorlds(1).map { worlds =>
-      Ok(Json.toJson(worlds.head))
+      Ok(Json.toJson(worlds.head)).withHeaders(CONTENT_TYPE -> "application/json")
     }
   }
 
   def queries(countString: String) = Action.async {
     val n = parseCount(countString)
     getRandomWorlds(n).map { worlds =>
-      Ok(Json.toJson(worlds))
+      Ok(Json.toJson(worlds)).withHeaders(CONTENT_TYPE -> "application/json")
     }
   }
 
   def fortunes() = Action.async {
-    getFortunes().map { dbFortunes =>
-      val appendedFortunes =  Fortune(0, "Additional fortune added at request time.") :: (dbFortunes.to[List])
-      Ok(views.html.fortune(appendedFortunes))
+    getFortunes.map { dbFortunes =>
+      val appendedFortunes =  Fortune(0, "Additional fortune added at request time.") :: dbFortunes.to[List]
+      Ok(views.html.fortune(appendedFortunes)).withHeaders(CONTENT_TYPE -> "text/html")
     }
   }
 
   def update(queries: String) = Action.async {
     val n = parseCount(queries)
     updateWorlds(n).map { worlds =>
-      Ok(Json.toJson(worlds))
+      Ok(Json.toJson(worlds)).withHeaders(CONTENT_TYPE -> "application/json")
     }
   }
 

+ 10 - 7
frameworks/Scala/play2-scala/play2-scala-anorm/app/models/Fortune.scala

@@ -1,25 +1,28 @@
 package models
 
-import play.api.db._
-import play.api.Play.current
+import javax.inject.{Inject, Singleton}
+
 import anorm._
 import anorm.SqlParser._
+import play.api.db.Database
+import play.db.NamedDatabase
 import scala.language.postfixOps
 
 case class Fortune(id: Long, message: String)
 
-object Fortune {
+@Singleton()
+class FortunesDAO @Inject()(@NamedDatabase("hello_world") protected val db: Database) {
 
-  val simpleRowParser = {
+  private val simpleRowParser = {
     get[Long]("fortune.id") ~
     get[String]("fortune.message") map {
       case id~message => Fortune(id, message)
     }
   }
 
-  def getAll(): List[Fortune] = {
-    DB.withConnection { implicit connection =>
-      SQL("SELECT * FROM Fortune").as(Fortune.simpleRowParser *)
+  def getAll: List[Fortune] = {
+    db.withConnection { implicit connection =>
+      SQL("SELECT * FROM Fortune").as(simpleRowParser *)
     }
   }
 }

+ 22 - 18
frameworks/Scala/play2-scala/play2-scala-anorm/app/models/World.scala

@@ -1,32 +1,22 @@
 package models
 
+import javax.inject.{Inject, Singleton}
+
 import anorm._
 import anorm.SqlParser._
 import java.sql.Connection
-import play.api.db._
-import play.api.libs.functional.syntax._
+import play.api.db.Database
 import play.api.libs.json._
-import play.api.Play.current
+import play.db.NamedDatabase
 
 case class World(id: Id[Long], randomNumber: Long)
 
-object World {
-    /**
-    * Convert a World to Json object
-    */
-  implicit val toJson = new Writes[World] {
-    def writes(w: World): JsValue = {
-      Json.obj(
-        "id" -> w.id.get,
-        "randomNumber" -> w.randomNumber
-      )
-    }
-  }
-
+@Singleton()
+class WorldDAO @Inject()(@NamedDatabase("hello_world") protected val db: Database) {
   /**
    * Parse a World from a ResultSet
    */
-  val simpleRowParser = {
+  private val simpleRowParser = {
     get[Long]("world.id") ~
     get[Long]("world.randomNumber") map {
       case id~randomNumber => World(Id(id), randomNumber)
@@ -37,10 +27,24 @@ object World {
    * Retrieve a World by id.
    */
   def findById(id: Long)(implicit connection: Connection): World = {
-    SQL"SELECT * FROM World WHERE id = ${id}".as(World.simpleRowParser.single)
+    SQL"SELECT * FROM World WHERE id = $id".as(simpleRowParser.single)
   }
 
   def updateRandom(world: World)(implicit connection: Connection) {
     SQL"UPDATE World SET randomNumber = ${world.randomNumber} WHERE id = ${world.id.get}".executeUpdate()
   }
+}
+
+object WorldJsonHelpers {
+  /**
+   * Convert a World to Json object
+   */
+  implicit val toJson = new Writes[World] {
+    def writes(w: World): JsValue = {
+      Json.obj(
+        "id" -> w.id.get,
+        "randomNumber" -> w.randomNumber
+      )
+    }
+  }
 }

+ 9 - 8
frameworks/Scala/play2-scala/play2-scala-anorm/app/utils/DbOperation.scala

@@ -2,23 +2,24 @@ package utils
 
 import java.sql.Connection
 import java.util.concurrent._
-import play.api.db.DB
+import javax.inject.{Singleton, Inject}
+import play.api.db.Database
 import play.api.Play.current
 import play.core.NamedThreadFactory
+import play.db.NamedDatabase
 import scala.concurrent._
 import scala.concurrent.Future
 
-object DbOperation {
-
-  // Common code between Anorm and Slick
+@Singleton
+class DbOperation @Inject()(@NamedDatabase("hello_world") protected val db: Database) {
 
   private val maxDbOperations = current.configuration.underlying.getInt("max-db-ops")
 
-  private val partitionCount = current.configuration.getInt("db.default.partitionCount").getOrElse(2)
+  private val partitionCount = current.configuration.getInt("db.hello_world.partitionCount").getOrElse(2)
   private val maxConnections =
-    partitionCount * current.configuration.getInt("db.default.maxConnectionsPerPartition").getOrElse(5)
+    partitionCount * current.configuration.getInt("db.hello_world.maxConnectionsPerPartition").getOrElse(5)
   private val minConnections =
-    partitionCount * current.configuration.getInt("db.default.minConnectionsPerPartition").getOrElse(5)
+    partitionCount * current.configuration.getInt("db.hello_world.minConnectionsPerPartition").getOrElse(5)
 
   private val tpe = new ThreadPoolExecutor(minConnections, maxConnections,
     0L, TimeUnit.MILLISECONDS,
@@ -40,7 +41,7 @@ object DbOperation {
     // number of concurrent connections that we expect to be handling.
     if (tpe.getQueue.size > maxDbOperations) sys.error(s"Aborted DB operation because queue is > $maxDbOperations")
     Future {
-      DB.withConnection { connection => op(connection) }
+      db.withConnection { connection => op(connection) }
     }(dbEc)
   }
 

+ 9 - 3
frameworks/Scala/play2-scala/play2-scala-anorm/build.sbt

@@ -2,11 +2,17 @@ name := "play2-scala-anorm"
 
 version := "1.0-SNAPSHOT"
 
-scalaVersion := "2.11.2"
+scalaVersion := "2.11.7"
 
 lazy val root = (project in file(".")).enablePlugins(PlayScala)
 
 libraryDependencies ++= Seq(
   jdbc,
-  anorm,
-  "mysql" % "mysql-connector-java" % "5.1.32")
+  "com.typesafe.play" %% "anorm" % "2.4.0",
+  "mysql" % "mysql-connector-java" % "5.1.37")
+
+resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases"
+
+// Play provides two styles of routers, one expects its actions to be injected, the
+// other, legacy style, accesses its actions statically.
+routesGenerator := InjectedRoutesGenerator

+ 39 - 39
frameworks/Scala/play2-scala/play2-scala-anorm/conf/application.conf

@@ -1,69 +1,69 @@
+
 # This is the main configuration file for the application.
 # ~~~~~
 
-# The max size of the queue for running db operations
-max-db-ops = 1024
-
 # Secret key
 # ~~~~~
 # The secret key is used to secure cryptographics functions.
-# If you deploy your application to several instances be sure to use the same key!
-application.secret="RItx1I:80?W@]8GAtPDuF8Ydd3mXM85p/<7og]Q;uBOdijQAauRDgu73B6`wQP59"
+#
+# This must be changed for production, but we recommend not changing it in this file.
+#
+# See http://www.playframework.com/documentation/latest/ApplicationSecret for more details.
+play.crypto.secret = "RItx1I:80?W@]8GAtPDuF8Ydd3mXM85p/<7og]Q;uBOdijQAauRDgu73B6`wQP59"
 
 # The application languages
 # ~~~~~
-application.langs="en"
+play.i18n.langs = [ "en" ]
 
-# Global object class
+# Router
 # ~~~~~
-# Define the Global object class for this application.
-# Default to Global in the root package.
-# global=Global
+# Define the Router object to use for this application.
+# This router will be looked up first when the application is starting up,
+# so make sure this is the entry point.
+# Furthermore, it's assumed your route file is named properly.
+# So for an application router like `my.application.Router`,
+# you may need to define a router file `conf/my.application.routes`.
+# Default to Routes in the root package (and conf/routes)
+# play.http.router = my.application.Routes
 
 # Database configuration
-# ~~~~~ 
+# ~~~~~
 # You can declare as many datasources as you want.
 # By convention, the default datasource is named `default`
 #
-#db.default.driver=org.h2.Driver
-#db.default.url="jdbc:h2:mem:play"
-#db.default.user=sa
-# db.default.password=
-#
+# db.default.driver=org.h2.Driver
+# db.default.url="jdbc:h2:mem:play"
+# db.default.username=sa
+# db.default.password=""
+
+max-db-ops = 1024
+
 # You can expose this datasource via JNDI if needed (Useful for JPA)
 # db.default.jndiName=DefaultDS
-db.default.driver= com.mysql.jdbc.Driver
-db.default.url="jdbc:mysql://localhost: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"
-db.default.user=benchmarkdbuser
-db.default.password=benchmarkdbpass
-db.default.jndiName=DefaultDS
+db.hello_world.driver= com.mysql.jdbc.Driver
+db.hello_world.url="jdbc:mysql://127.0.0.1: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"
+db.hello_world.username=benchmarkdbuser
+db.hello_world.password=benchmarkdbpass
+db.hello_world.jndiName=DefaultDS
 
-db.default.partitionCount=4
+db.hello_world.partitionCount=4
 
-# The number of connections to create per partition. Setting this to 
-# 5 with 3 partitions means you will have 15 unique connections to the 
-# database. Note that BoneCP will not create all these connections in 
-# one go but rather start off with minConnectionsPerPartition and 
+# The number of connections to create per partition. Setting this to
+# 5 with 3 partitions means you will have 15 unique connections to the
+# database. Note that BoneCP will not create all these connections in
+# one go but rather start off with minConnectionsPerPartition and
 # gradually increase connections as required.
-db.default.maxConnectionsPerPartition=64
+db.hello_world.maxConnectionsPerPartition=64
 
 # The number of initial connections, per partition.
-db.default.minConnectionsPerPartition=64
+db.hello_world.minConnectionsPerPartition=64
 
 # Evolutions
 # ~~~~~
 # You can disable evolutions if needed
-# evolutionplugin=disabled
-
-# Logger
-# ~~~~~
-# You can also configure logback (http://logback.qos.ch/), by providing a logger.xml file in the conf directory .
+# play.evolutions.enabled=false
 
-# Root logger:
-logger.root=ERROR
+# You can disable evolutions for a specific datasource if necessary
+# play.evolutions.db.default.enabled=false
 
-# Logger used by the framework:
-logger.play=ERROR
 
-# Logger provided to your application:
-logger.application=ERROR

+ 6 - 4
frameworks/Scala/play2-scala/play2-scala-anorm/conf/routes

@@ -3,10 +3,12 @@
 # ~~~~
 
 # Home page
-GET     /db                             controllers.Application.db
-GET     /queries                        controllers.Application.queries(queries ?= "1")
-GET     /fortunes                       controllers.Application.fortunes
-GET     /update                         controllers.Application.update(queries ?= "1")
+GET     /json                           @controllers.Application.getJsonMessage
+GET     /db                             @controllers.Application.db
+GET     /queries                        @controllers.Application.queries(queries ?= "1")
+GET     /fortunes                       @controllers.Application.fortunes
+GET     /update                         @controllers.Application.update(queries ?= "1")
+GET     /plaintext                      @controllers.Application.plaintext
 
 # Map static resources from the /public folder to the /assets URL path
 GET     /assets/*file                   controllers.Assets.at(path="/public", file)

+ 1 - 1
frameworks/Scala/play2-scala/play2-scala-anorm/project/build.properties

@@ -1 +1 @@
-sbt.version=0.13.5
+sbt.version=0.13.9

+ 1 - 7
frameworks/Scala/play2-scala/play2-scala-anorm/project/plugins.sbt

@@ -1,8 +1,2 @@
-// Comment to get more information during initialization
-logLevel := Level.Warn
-
-// The Typesafe repository 
-resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
-
 // Use the Play sbt plugin for Play projects
-addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.3")
+addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.6")

+ 12 - 11
frameworks/Scala/play2-scala/play2-scala-anorm/source_code

@@ -1,11 +1,12 @@
-./play-scala/app/
-./play-scala/app/controllers
-./play-scala/app/controllers/Application.scala
-./play-scala/app/views
-./play-scala/app/views/main.scala.html
-./play-scala/app/views/fortune.scala.html
-./play-scala/app/utils
-./play-scala/app/utils/PredicatedAction.scala
-./play-scala/app/models
-./play-scala/app/models/Fortune.scala
-./play-scala/app/models/World.scala
+./play2-scala-anorm/app/
+./play2-scala-anorm/app/Filters.scala
+./play2-scala-anorm/app/controllers
+./play2-scala-anorm/app/controllers/Application.scala
+./play2-scala-anorm/app/views
+./play2-scala-anorm/app/views/main.scala.html
+./play2-scala-anorm/app/views/fortune.scala.html
+./play2-scala-anorm/app/utils
+./play2-scala-anorm/app/utils/DbOperation.scala
+./play2-scala-anorm/app/models
+./play2-scala-anorm/app/models/Fortune.scala
+./play2-scala-anorm/app/models/World.scala

+ 23 - 0
frameworks/Scala/play2-scala/play2-scala-reactivemongo/app/Filters.scala

@@ -0,0 +1,23 @@
+import javax.inject.{Singleton, Inject}
+
+import play.api.http.HttpFilters
+import play.api.mvc.{RequestHeader, EssentialAction, EssentialFilter}
+import play.api.libs.concurrent.Execution.Implicits.defaultContext
+import play.mvc.Http
+
+@Singleton()
+class Filters @Inject() (headerFilter: HeaderFilter) extends HttpFilters {
+  override def filters: Seq[EssentialFilter] = Seq(
+    headerFilter)
+}
+
+@Singleton
+class HeaderFilter extends EssentialFilter {
+  def apply(next: EssentialAction) = new EssentialAction {
+    def apply(request: RequestHeader) = {
+      next(request).map(result =>
+        result.withHeaders(Http.HeaderNames.SERVER -> "EXAMPLE")  // Only here to address the WARN about this header.
+      )
+    }
+  }
+}

+ 118 - 57
frameworks/Scala/play2-scala/play2-scala-reactivemongo/app/controllers/Application.scala

@@ -1,77 +1,138 @@
 package controllers
 
-import play.api.Play.current
-import play.api.mvc._
-import play.api.libs.json._
-import scala.concurrent.forkjoin.ThreadLocalRandom
-import scala.concurrent.{Future, ExecutionContext}
-import scala.collection.convert.WrapAsScala.collectionAsScalaIterable
-import play.modules.reactivemongo.ReactiveMongoPlugin
+import java.util.concurrent.ThreadLocalRandom
+import javax.inject.{Singleton, Inject}
+
+import play.api.libs.json.{JsObject, Json, JsValue}
+import play.api.libs.concurrent.Execution.Implicits.defaultContext
+import reactivemongo.api.ReadPreference
+
+import scala.concurrent.Future
+
+import play.api.mvc.{ Action, Controller }
+
+import play.modules.reactivemongo.{
+MongoController, ReactiveMongoApi, ReactiveMongoComponents
+}
+import play.modules.reactivemongo.json._
 import play.modules.reactivemongo.json.collection.JSONCollection
-import play.api.libs.concurrent.Execution.Implicits._
-import play.api.libs.concurrent.Akka
 
-object Application extends Controller {
+@Singleton
+class Application @Inject() (val reactiveMongoApi: ReactiveMongoApi)
+  extends Controller with MongoController with ReactiveMongoComponents {
+
+  private def worldCollection: JSONCollection = reactiveMongoApi.db.collection[JSONCollection]("world")
+  private def fortuneCollection: JSONCollection = reactiveMongoApi.db.collection[JSONCollection]("fortune")
+  private val projection = Json.obj("_id" -> 0)
+
+  def getRandomWorlds(queries: Int): Future[Seq[Option[JsObject]]] = {
+    val futureWorlds: Seq[Future[Option[JsObject]]] = for {
+      _ <- 1 to queries
+    } yield { worldCollection
+      .find(Json.obj("_id" -> getNextRandom), projection)
+      .one[JsObject]
+    }
+    Future.sequence(futureWorlds)
+  }
+
+  def getRandomWorld = {
+    val futureWorld = worldCollection
+      .find(Json.obj("id" -> getNextRandom), projection)
+      .one[JsValue]
+    futureWorld
+  }
+
+  def getFortunes: Future[List[JsObject]] = {
+    val futureFortunes: Future[List[JsObject]] =
+      fortuneCollection.find(Json.obj())
+        .cursor[JsObject](ReadPreference.primaryPreferred, false).collect[List]()
+    futureFortunes
+  }
 
-  private val TestDatabaseRows = 10000
+  def updateWorlds(queries: Int): Future[Seq[Option[JsObject]]] = {
+    val futureWorlds: Future[Seq[Option[JsObject]]] = getRandomWorlds(queries)
+    val futureNewWorlds: Future[Seq[Option[JsObject]]] = futureWorlds.map( worlds => {
+      worlds.map(worldOption => {
+        worldOption.map(world => {
+          val newWorld = world ++ Json.obj("randomNumber" -> getNextRandom)
+          worldCollection.update(world, newWorld)
+          newWorld
+        })
+      })
+    })
+    futureNewWorlds
+  }
 
-  val DEFAULT_HOST = "localhost:27017"
-  val servers = current.configuration.getStringList("mongodb.servers") match {
-    case Some(servers) => collectionAsScalaIterable(servers).toList
-    case None => List(DEFAULT_HOST)
+  def getNextRandom: Int = {
+    ThreadLocalRandom.current().nextInt(TestDatabaseRows) + 1
   }
 
-  val DEFAULT_DB = "hello_world"
-  val db = current.configuration.getString("mongodb.db").getOrElse(DEFAULT_DB)
+  // Test seems picky about headers.  Doesn't like character set being there for JSON.  Always wants Server header set.
+  // There is a Filter which adds the Server header for all types.  Below I set Content-Type as needed to get rid of
+  // warnings.
 
-  //private val dbExecutionContext: ExecutionContext = Akka.system.dispatchers.lookup("dbExecutionContext")
-  private val database = ReactiveMongoPlugin
-    .driver
-    .connection(servers, nbChannelsPerNode = 10)
-    .db(db)//(dbExecutionContext)
+  // Easy ones
+  case class HelloWorld(message: String)
 
-  private def collection: JSONCollection = database.collection[JSONCollection]("world")
-  private val projection = Json.obj("_id" -> 0)
-  /**
-   * Returns the closest number to <code>toRestrict</code> that is within the
-   * specified bounds, inclusive on both ends.
-   */
-  private def restrictWithin(toRestrict: String, lowerBound: Int, upperBound: Int): Option[Int] = {
-    try {
-      Some(math.min(upperBound, math.max(toRestrict.toInt, lowerBound)))
-    } catch {
-      case e: Exception => None
+  def getJsonMessage = Action {
+    val helloWorld = HelloWorld(message = "Hello, World!")
+    Ok(Json.toJson(helloWorld)(Json.writes[HelloWorld])).withHeaders(CONTENT_TYPE -> "application/json")
+  }
+
+  val plaintext = Action {
+    // default headers are correct according to docs: charset included.
+    // BUT the test harness has a WARN state and says we don't need it.
+    Ok("Hello, World!").withHeaders(CONTENT_TYPE -> "text/plain")
+  }
+
+  // Semi-Common code between Scala database code
+
+  protected val TestDatabaseRows = 10000
+
+  def doDb = Action.async {
+    getRandomWorld.map { worlds =>
+      Ok(Json.toJson(worlds.head)).withHeaders(CONTENT_TYPE -> "application/json")
     }
   }
 
-  def dbqueries(requestedQueries: String) = Action.async {
-    import scala.concurrent.ExecutionContext.Implicits.global
+  def queries(countString: String) = Action.async {
+    val n = parseCount(countString)
+    getRandomWorlds(n).map { worlds =>
+      Ok(Json.toJson(worlds)).withHeaders(CONTENT_TYPE -> "application/json")
+    }
+  }
 
-    val random = ThreadLocalRandom.current()
-    val queries = restrictWithin(requestedQueries, 1, 500).getOrElse(1)
-    val futureWorlds = Future.sequence((for {
-      _ <- 1 to queries
-    } yield { collection
-      .find(Json.obj("id" -> (random.nextInt(TestDatabaseRows) + 1)), projection)
-      .one[JsValue]
-    }))
-    futureWorlds.map { worlds =>
-      Ok(Json.toJson(worlds.map {maybeWorld =>
-        maybeWorld.map {world =>
-          world.as[Map[String, Int]]
-        }
-      }))
+  private def byMessage(item: JsValue): String = {
+    (item \ "message").as[String]
+  }
+
+  def fortunes() = Action.async {
+    getFortunes.map { dbFortunes =>
+      val appendedFortunes =  Json.obj("_id" -> 0, "message" -> "Additional fortune added at request time.") :: dbFortunes
+
+      val sorted = appendedFortunes.sortBy(byMessage(_))
+
+      Ok(views.html.fortune(sorted)).withHeaders(CONTENT_TYPE -> "text/html")
     }
   }
-  def singledb() = Action.async {
-    import scala.concurrent.ExecutionContext.Implicits.global
 
-    val random = ThreadLocalRandom.current()
-    val futureWorld = collection
-      .find(Json.obj("id" -> (random.nextInt(TestDatabaseRows) + 1)), projection)
-      .one[JsValue]
-    futureWorld.map { world =>
-      Ok(Json.toJson(world.head.as[Map[String, Int]]))
+  def update(queries: String) = Action.async {
+    val n = parseCount(queries)
+    updateWorlds(n).map { worlds =>
+      Ok(Json.toJson(worlds)).withHeaders(CONTENT_TYPE -> "application/json")
+    }
+  }
+
+  private def parseCount(s: String): Int = {
+    try {
+      val parsed = java.lang.Integer.parseInt(s, 10)
+      parsed match {
+        case i if i < 1 => 1
+        case i if i > 500 => 500
+        case i => i
+      }
+    } catch {
+      case _: NumberFormatException => 1
     }
   }
 }

+ 20 - 0
frameworks/Scala/play2-scala/play2-scala-reactivemongo/app/views/fortune.scala.html

@@ -0,0 +1,20 @@
+@import play.api.libs.json.JsValue
+@(fortunes: Seq[JsValue])
+
+@main("Fortunes") {
+    <table>
+        <tr>
+            <th>id</th>
+            <th>message</th>
+        </tr>
+
+        @fortunes.map { fortune =>
+
+            <tr>
+                <td>@{(fortune \ "_id").as[Int]}</td>
+                <td>@{(fortune \ "message").as[String]}</td>
+            </tr>
+        }
+
+    </table>
+}

+ 12 - 0
frameworks/Scala/play2-scala/play2-scala-reactivemongo/app/views/main.scala.html

@@ -0,0 +1,12 @@
+@(title: String)(content: Html)
+
+<!DOCTYPE html>
+
+<html>
+<head>
+    <title>@title</title>
+</head>
+<body>
+    @content
+</body>
+</html>

+ 4 - 2
frameworks/Scala/play2-scala/play2-scala-reactivemongo/build.sbt

@@ -2,10 +2,12 @@ name := "play2-scala-reactivemongo"
 
 version := "1.0-SNAPSHOT"
 
-scalaVersion := "2.11.4"
+scalaVersion := "2.11.7"
 
 lazy val root = (project in file(".")).enablePlugins(PlayScala)
 
 libraryDependencies ++= Seq(
-  "org.reactivemongo" %% "play2-reactivemongo" % "0.10.5.0.akka23"
+  "org.reactivemongo" %% "play2-reactivemongo" % "0.11.9",
+  "org.reactivemongo" %% "reactivemongo-play-json" % "0.11.9"
 )
+

+ 26 - 44
frameworks/Scala/play2-scala/play2-scala-reactivemongo/conf/application.conf

@@ -4,65 +4,47 @@
 # Secret key
 # ~~~~~
 # The secret key is used to secure cryptographics functions.
-# If you deploy your application to several instances be sure to use the same key!
-application.secret="RItx1I:80?W@]8GAtPDuF8Ydd3mXM85p/<7og]Q;uBOdijQAauRDgu73B6`wQP59"
+#
+# This must be changed for production, but we recommend not changing it in this file.
+#
+# See http://www.playframework.com/documentation/latest/ApplicationSecret for more details.
+play.crypto.secret = "RItx1I:80?W@]8GAtPDuF8Ydd3mXM85p/<7og]Q;uBOdijQAauRDgu73B6`wQP59"
 
 # The application languages
 # ~~~~~
-application.langs="en"
+play.i18n.langs = [ "en" ]
 
-# Global object class
+# Router
 # ~~~~~
-# Define the Global object class for this application.
-# Default to Global in the root package.
-# global=Global
+# Define the Router object to use for this application.
+# This router will be looked up first when the application is starting up,
+# so make sure this is the entry point.
+# Furthermore, it's assumed your route file is named properly.
+# So for an application router like `my.application.Router`,
+# you may need to define a router file `conf/my.application.routes`.
+# Default to Routes in the root package (and conf/routes)
+# play.http.router = my.application.Routes
 
 # Database configuration
-# ~~~~~ 
+# ~~~~~
 # You can declare as many datasources as you want.
 # By convention, the default datasource is named `default`
 #
-#db.default.driver=org.h2.Driver
-#db.default.url="jdbc:h2:mem:play"
-#db.default.user=sa
-# db.default.password=
-#
-# You can expose this datasource via JNDI if needed (Useful for JPA)
-# db.default.jndiName=DefaultDS
-
-#mongodb.servers = ["192.168.100.101:27017"]
-mongodb.servers = ["localhost:27017"]
-mongodb.db = "hello_world"
+# db.default.driver=org.h2.Driver
+# db.default.url="jdbc:h2:mem:play"
+# db.default.username=sa
+# db.default.password=""
 
 # Evolutions
 # ~~~~~
 # You can disable evolutions if needed
-# evolutionplugin=disabled
-
-# Logger
-# ~~~~~
-# You can also configure logback (http://logback.qos.ch/), by providing a logger.xml file in the conf directory .
+# play.evolutions.enabled=false
 
-# Root logger:
-logger.root=ERROR
+# You can disable evolutions for a specific datasource if necessary
+# play.evolutions.db.default.enabled=false
 
-# Logger used by the framework:
-logger.play=ERROR
+play.modules.enabled += "play.modules.reactivemongo.ReactiveMongoModule"
 
-# Logger provided to your application:
-logger.application=ERROR
+mongodb.servers = ["localhost:27017"]
+mongodb.db = "hello_world"
 
-#play {
-#  akka {
-#    event-handlers = ["akka.event.slf4j.Slf4jEventHandler"]
-#    loglevel = ERROR
-#    actor {
-#      dbExecutionContext = {
-#        fork-join-executor {
-#          parallelism-min = 128
-#          parallelism-max = 128
-#        }
-#      }
-#    }
-#  }
-#}

+ 0 - 1
frameworks/Scala/play2-scala/play2-scala-reactivemongo/conf/play.plugins

@@ -1 +0,0 @@
-400:play.modules.reactivemongo.ReactiveMongoPlugin

+ 7 - 2
frameworks/Scala/play2-scala/play2-scala-reactivemongo/conf/routes

@@ -2,8 +2,13 @@
 # This file defines all application routes (Higher priority routes first)
 # ~~~~
 
-GET     /db                             controllers.Application.singledb
-GET     /queries                        controllers.Application.dbqueries(queries: String ?= "1")
+# Home page
+GET     /json                           @controllers.Application.getJsonMessage
+GET     /db                             @controllers.Application.doDb
+GET     /queries                        @controllers.Application.queries(queries: String ?= "1")
+GET     /fortunes                       @controllers.Application.fortunes
+GET     /update                         @controllers.Application.update(queries ?= "1")
+GET     /plaintext                      @controllers.Application.plaintext
 
 # Map static resources from the /public folder to the /assets URL path
 GET     /assets/*file                   controllers.Assets.at(path="/public", file)

+ 1 - 1
frameworks/Scala/play2-scala/play2-scala-reactivemongo/project/build.properties

@@ -1 +1 @@
-sbt.version=0.13.5
+sbt.version=0.13.9

+ 1 - 7
frameworks/Scala/play2-scala/play2-scala-reactivemongo/project/plugins.sbt

@@ -1,8 +1,2 @@
-// Comment to get more information during initialization
-logLevel := Level.Warn
-
-// The Typesafe repository 
-resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
-
 // Use the Play sbt plugin for Play projects
-addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.6")
+addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.6")

+ 4 - 0
frameworks/Scala/play2-scala/play2-scala-reactivemongo/source_code

@@ -1,3 +1,7 @@
 ./play2-scala-reactivemongo/app/
+./play2-scala-reactivemongo/app/Filters.scala
 ./play2-scala-reactivemongo/app/controllers
 ./play2-scala-reactivemongo/app/controllers/Application.scala
+./play2-scala-reactivemongo/app/views
+./play2-scala-reactivemongo/app/views/main.scala.html
+./play2-scala-reactivemongo/app/views/fortune.scala.html

+ 23 - 0
frameworks/Scala/play2-scala/play2-scala-slick/app/Filters.scala

@@ -0,0 +1,23 @@
+import javax.inject.{Singleton, Inject}
+
+import play.api.http.HttpFilters
+import play.api.mvc.{RequestHeader, EssentialAction, EssentialFilter}
+import play.api.libs.concurrent.Execution.Implicits.defaultContext
+import play.mvc.Http
+
+@Singleton()
+class Filters @Inject() (headerFilter: HeaderFilter) extends HttpFilters {
+  override def filters: Seq[EssentialFilter] = Seq(
+    headerFilter)
+}
+
+@Singleton
+class HeaderFilter extends EssentialFilter {
+  def apply(next: EssentialAction) = new EssentialAction {
+    def apply(request: RequestHeader) = {
+      next(request).map(result =>
+        result.withHeaders(Http.HeaderNames.SERVER -> "EXAMPLE")  // Only here to address the WARN about this header.
+      )
+    }
+  }
+}

+ 47 - 32
frameworks/Scala/play2-scala/play2-scala-slick/app/controllers/Application.scala

@@ -1,77 +1,92 @@
 package controllers
 
 import java.util.concurrent._
-import play.api.Play.current
+import javax.inject.{Singleton, Inject}
 import play.api.mvc._
 import play.api.libs.json.Json
-import scala.concurrent._
-import models.{Worlds, World, Fortunes, Fortune, WorldsTableQuery, FortunesTableQuery}
-import utils._
-import utils.DbOperation._
+import models._
 import scala.concurrent.Future
-import scala.slick.jdbc.JdbcBackend.Session
 
 import play.api.libs.concurrent.Execution.Implicits._
 
-object Application extends Controller {
+@Singleton()
+class Application @Inject() (fortunesDAO: FortunesDAO, worldDAO: WorldDAO) extends Controller {
 
   // Slick code
 
-  private val worldsTable = new WorldsTableQuery
-  private val fortunesTable = new FortunesTableQuery
-
-  def getRandomWorlds(n: Int): Future[Seq[World]] = asyncDbOp { implicit session =>
-    val random = ThreadLocalRandom.current()
-    for (_ <- 1 to n) yield {
-      val randomId = random.nextInt(TestDatabaseRows) + 1
-      worldsTable.findById(randomId)
+  def getRandomWorlds(n: Int): Future[Seq[World]] = {
+    val worlds: Seq[Future[World]] = for (_ <- 1 to n) yield {
+      worldDAO.findById(getNextRandom)
     }
+    Future.sequence(worlds)
   }
 
-  def getFortunes(): Future[Seq[Fortune]] = asyncDbOp { implicit session =>
-    fortunesTable.getAll()
+  def getFortunes: Future[Seq[Fortune]] = {
+    fortunesDAO.getAll()
   }
 
-  def updateWorlds(n: Int): Future[Seq[World]] = asyncDbOp { implicit session =>
-    val random = ThreadLocalRandom.current()
-    for (_ <- 1 to n) yield {
-      val randomId = random.nextInt(TestDatabaseRows) + 1
-      val world = worldsTable.findById(randomId)
-      val randomNumber = random.nextInt(10000) + 1
-      val updatedWorld = world.copy(randomNumber = randomNumber)
-      worldsTable.updateRandom(updatedWorld)
-      updatedWorld
+  def updateWorlds(n: Int): Future[Seq[World]] = {
+    val worlds: Seq[Future[World]] = for (_ <- 1 to n) yield {
+      val futureWorld: Future[World] = worldDAO.findById(getNextRandom)
+      val futureUpdatedWorld: Future[World] = futureWorld.map(_.copy(randomNumber = getNextRandom))
+      futureUpdatedWorld.map(world => worldDAO.updateRandom(world))
+      futureUpdatedWorld
     }
+    Future.sequence(worlds)
+  }
+
+  def getNextRandom: Int = {
+    ThreadLocalRandom.current().nextInt(TestDatabaseRows) + 1
+  }
+
+  // Test seems picky about headers.  Doesn't like character set being there for JSON.  Always wants Server header set.
+  // There is a Filter which adds the Server header for all types.  Below I set Content-Type as needed to get rid of
+  // warnings.
+
+  // Easy ones
+  case class HelloWorld(message: String)
+
+  def getJsonMessage = Action {
+    val helloWorld = HelloWorld(message = "Hello, World!")
+    Ok(Json.toJson(helloWorld)(Json.writes[HelloWorld])).withHeaders(CONTENT_TYPE -> "application/json")
+  }
+
+  val plaintext = Action {
+    // default headers are correct according to docs: charset included.
+    // BUT the test harness has a WARN state and says we don't need it.
+    Ok("Hello, World!").withHeaders(CONTENT_TYPE -> "text/plain")
   }
 
   // Common code between Scala database code
 
   protected val TestDatabaseRows = 10000
 
+  import models.WorldJsonHelpers.toJson
+
   def db = Action.async {
     getRandomWorlds(1).map { worlds =>
-      Ok(Json.toJson(worlds.head))
+      Ok(Json.toJson(worlds.head)).withHeaders(CONTENT_TYPE -> "application/json")
     }
   }
 
   def queries(countString: String) = Action.async {
     val n = parseCount(countString)
     getRandomWorlds(n).map { worlds =>
-      Ok(Json.toJson(worlds))
+      Ok(Json.toJson(worlds)).withHeaders(CONTENT_TYPE -> "application/json")
     }
   }
 
   def fortunes() = Action.async {
-    getFortunes().map { dbFortunes =>
-      val appendedFortunes =  Fortune(0, "Additional fortune added at request time.") :: (dbFortunes.to[List])
-      Ok(views.html.fortune(appendedFortunes))
+    getFortunes.map { dbFortunes =>
+      val appendedFortunes =  Fortune(0, "Additional fortune added at request time.") :: dbFortunes.to[List]
+      Ok(views.html.fortune(appendedFortunes)).withHeaders(CONTENT_TYPE -> "text/html")
     }
   }
 
   def update(queries: String) = Action.async {
     val n = parseCount(queries)
     updateWorlds(n).map { worlds =>
-      Ok(Json.toJson(worlds))
+      Ok(Json.toJson(worlds)).withHeaders(CONTENT_TYPE -> "application/json")
     }
   }
 

+ 30 - 17
frameworks/Scala/play2-scala/play2-scala-slick/app/models/Fortune.scala

@@ -1,21 +1,34 @@
 package models
 
-import play.api.Play.current
-import play.api.db.slick.Config.driver.simple._
-import play.api.db.slick.DB
-
-
-class Fortunes(tag: Tag) extends Table[Fortune](tag, "Fortune") {
-  def id = column[Long]("id", O.PrimaryKey)
-  def message = column[String]("message")
-  def * = (id, message) <> (Fortune.tupled, Fortune.unapply _)
-}
-class FortunesTableQuery extends TableQuery(new Fortunes(_)){
-  val byId = this.findBy(_.id)
-  val all = Compiled{this:Query[Fortunes,Fortune,Seq]}
-  def getAll(): List[Fortune] = DB.withSession { implicit session =>
-    all.list
-  }
-}
+import javax.inject.{Inject, Singleton}
+
+import play.api.db.slick.{HasDatabaseConfigProvider, DatabaseConfigProvider}
+import play.db.NamedDatabase
+import slick.driver.JdbcProfile
+
+import scala.concurrent.Future
 
 case class Fortune(id: Long, message: String)
+
+@Singleton()
+class FortunesDAO @Inject()(@NamedDatabase("hello_world") protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile] {
+
+  import driver.api._
+
+  private val Fortunes = TableQuery[FortunesTable]
+
+  def getAll(): Future[Seq[Fortune]] = db.run(Fortunes.result)
+
+
+  def findById(id: Long): Future[Option[Fortune]] = {
+    db.run(Fortunes.filter(_.id === id).result.headOption)
+  }
+
+  private class FortunesTable(tag: Tag) extends Table[Fortune](tag, Some("hello_world"), "Fortune") {
+
+    def id = column[Long]("id", O.PrimaryKey)
+    def message = column[String]("message")
+    def * = (id, message) <>(Fortune.tupled, Fortune.unapply)
+
+  }
+}

+ 32 - 18
frameworks/Scala/play2-scala/play2-scala-slick/app/models/World.scala

@@ -1,33 +1,47 @@
 package models
 
-import play.api.Play.current
-import play.api.db.slick.Config.driver.simple._
-import play.api.db.slick.DB
 import play.api.libs.json._
-import play.api.libs.functional.syntax._
 
-class Worlds(tag: Tag) extends Table[World](tag, "World") {
-  def id = column[Int]("id", O.PrimaryKey)
-  def randomNumber = column[Long]("randomNumber")
-  def * = (id, randomNumber) <> ((World.apply _).tupled, World.unapply _)
-}
-class WorldsTableQuery extends TableQuery(new Worlds(_)){
-  val byId = this.findBy(_.id)
+import scala.concurrent.Future
+
+import javax.inject.{Singleton, Inject}
+import play.api.db.slick.DatabaseConfigProvider
+import play.api.db.slick.HasDatabaseConfigProvider
+import play.api.libs.concurrent.Execution.Implicits.defaultContext
+import play.db.NamedDatabase
+import slick.driver.JdbcProfile
+
+@Singleton()
+class WorldDAO @Inject()(@NamedDatabase("hello_world") protected val dbConfigProvider: DatabaseConfigProvider) extends HasDatabaseConfigProvider[JdbcProfile] {
+  import driver.api._
+
+  private val Worlds = TableQuery[WorldsTable]
+
+  def all(): Future[Seq[World]] = db.run(Worlds.result)
+
+  def insert(world: World): Future[Unit] = db.run(Worlds += world).map { _ => () }
 
-  def findById(id: Int)(implicit session: Session): World = {
-    byId(id).first
+  private class WorldsTable(tag: Tag) extends Table[World](tag, Some("hello_world"), "World") {
+
+    def id = column[Int]("id", O.PrimaryKey)
+    def randomNumber = column[Long]("randomNumber")
+
+    def * = (id, randomNumber) <> (World.tupled, World.unapply)
   }
 
-  val updateQuery = Compiled{ (id: Column[Int]) => this.filter(_.id === id) }
+  def findById(id: Int): Future[World] = {
+    db.run(Worlds.filter(_.id === id).result.head)
+  }
 
-  def updateRandom(world: World)(implicit session: Session) {
-    updateQuery(world.id).update(world)
-  }  
+  def updateRandom(world: World) = {
+    db.run(Worlds.filter(_.id === world.id).map(_.randomNumber).update(world.randomNumber))
+  }
 }
 
+
 case class World(id: Int, randomNumber: Long)
 
-object World {
+object WorldJsonHelpers {
   implicit val toJson = new Writes[World] {
     def writes(w: World): JsValue = {
       Json.obj(

+ 0 - 47
frameworks/Scala/play2-scala/play2-scala-slick/app/utils/DbOperation.scala

@@ -1,47 +0,0 @@
-package utils
-
-import java.util.concurrent._
-import play.api.db.slick.DB
-import play.api.Play.current
-import play.core.NamedThreadFactory
-import scala.concurrent._
-import scala.concurrent.Future
-import scala.slick.jdbc.JdbcBackend.Session
-
-object DbOperation {
-
-  // Common code between Anorm and Slick
-
-  private val maxDbOperations = current.configuration.underlying.getInt("max-db-ops")
-
-  private val partitionCount = current.configuration.getInt("db.default.partitionCount").getOrElse(2)
-  private val maxConnections =
-    partitionCount * current.configuration.getInt("db.default.maxConnectionsPerPartition").getOrElse(5)
-  private val minConnections =
-    partitionCount * current.configuration.getInt("db.default.minConnectionsPerPartition").getOrElse(5)
-
-  private val tpe = new ThreadPoolExecutor(minConnections, maxConnections,
-    0L, TimeUnit.MILLISECONDS,
-    new LinkedBlockingQueue[Runnable](), // TODO: Could use ArrayBlockingQueue?
-    new NamedThreadFactory("dbEc"))
-  private val dbEc = ExecutionContext.fromExecutorService(tpe)
-
-  // Slick code
-
-  /**
-   * Run a DB operation in the DB context. Automatically
-   * provides a Session.
-   */
-  def asyncDbOp[T](op: Session => T): Future[T] = {
-    // If the thread-pool queue used by the database grows too large then our server
-    // is probably struggling, and we should start dropping requests. If we don't
-    // then we'll just slow everything down and it will fail anyway. Better to fail
-    // quickly rather than slowly. Set the max size of our queue something above the
-    // number of concurrent connections that we expect to be handling.
-    if (tpe.getQueue.size > maxDbOperations) sys.error(s"Aborted DB operation because queue is > $maxDbOperations")
-    Future {
-      DB.withSession { session => op(session) }
-    }(dbEc)
-  }
-
-}

+ 5 - 3
frameworks/Scala/play2-scala/play2-scala-slick/build.sbt

@@ -2,10 +2,12 @@ name := "play2-scala-slick"
 
 version := "1.0-SNAPSHOT"
 
-scalaVersion := "2.11.2"
+scalaVersion := "2.11.7"
 
 lazy val root = (project in file(".")).enablePlugins(PlayScala)
 
 libraryDependencies ++= Seq(
-  "com.typesafe.play" %% "play-slick" % "0.8.0",
-  "mysql" % "mysql-connector-java" % "5.1.32")
+  "com.typesafe.play" %% "play-slick" % "1.1.1",
+  "mysql" % "mysql-connector-java" % "5.1.37",
+  filters
+)

+ 9 - 42
frameworks/Scala/play2-scala/play2-scala-slick/conf/application.conf

@@ -2,7 +2,7 @@
 # ~~~~~
 
 # The max size of the queue for running db operations
-max-db-ops = 1024
+#max-db-ops = 1024
 
 # Secret key
 # ~~~~~
@@ -22,54 +22,21 @@ application.langs="en"
 
 # Database configuration
 # ~~~~~ 
-# You can declare as many datasources as you want.
-# By convention, the default datasource is named `default`
-#
-#db.default.driver=org.h2.Driver
-#db.default.url="jdbc:h2:mem:play"
-#db.default.user=sa
-# db.default.password=
-#
-# You can expose this datasource via JNDI if needed (Useful for JPA)
-# db.default.jndiName=DefaultDS
-db.default.driver= com.mysql.jdbc.Driver
-db.default.url="jdbc:mysql://localhost: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"
-db.default.user=benchmarkdbuser
-db.default.password=benchmarkdbpass
-db.default.jndiName=DefaultDS
 
-db.default.partitionCount=4
-
-# The number of connections to create per partition. Setting this to 
-# 5 with 3 partitions means you will have 15 unique connections to the 
-# database. Note that BoneCP will not create all these connections in 
-# one go but rather start off with minConnectionsPerPartition and 
-# gradually increase connections as required.
-db.default.maxConnectionsPerPartition=64
-
-# The number of initial connections, per partition.
-db.default.minConnectionsPerPartition=64
-
-slick.default="models.*"
+slick.dbs.hello_world.driver="slick.driver.MySQLDriver$"
+slick.dbs.hello_world.db.driver="com.mysql.jdbc.Driver"
+slick.dbs.hello_world.db.url="jdbc:mysql://127.0.0.1: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"
+slick.dbs.hello_world.db.user="benchmarkdbuser"
+slick.dbs.hello_world.db.password="benchmarkdbpass"
+slick.dbs.hello_world.db.connectionPool = HikariCP
+slick.dbs.hello_world.db.keepAliveConnection = true
+#slick.dbs.hello_world.db.connectionTestQuery="select 1"
 
 # Evolutions
 # ~~~~~
 # You can disable evolutions if needed
 evolutionplugin=disabled
 
-# Logger
-# ~~~~~
-# You can also configure logback (http://logback.qos.ch/), by providing a logger.xml file in the conf directory .
-
-# Root logger:
-logger.root=ERROR
-
-# Logger used by the framework:
-logger.play=ERROR
-
-# Logger provided to your application:
-logger.application=ERROR
-
 play {
   akka {
     event-handlers = ["akka.event.slf4j.Slf4jEventHandler"]

+ 6 - 4
frameworks/Scala/play2-scala/play2-scala-slick/conf/routes

@@ -3,10 +3,12 @@
 # ~~~~
 
 # Home page
-GET     /db                             controllers.Application.db
-GET     /queries                        controllers.Application.queries(queries ?= "1")
-GET     /fortunes                       controllers.Application.fortunes
-GET     /update                         controllers.Application.update(queries ?= "1")
+GET     /json                           @controllers.Application.getJsonMessage
+GET     /db                             @controllers.Application.db
+GET     /queries                        @controllers.Application.queries(queries ?= "1")
+GET     /fortunes                       @controllers.Application.fortunes
+GET     /update                         @controllers.Application.update(queries ?= "1")
+GET     /plaintext                      @controllers.Application.plaintext
 
 # Map static resources from the /public folder to the /assets URL path
 GET     /assets/*file                   controllers.Assets.at(path="/public", file)

+ 1 - 1
frameworks/Scala/play2-scala/play2-scala-slick/project/build.properties

@@ -1 +1 @@
-sbt.version=0.13.5
+sbt.version=0.13.9

+ 1 - 1
frameworks/Scala/play2-scala/play2-scala-slick/project/plugins.sbt

@@ -5,4 +5,4 @@ logLevel := Level.Warn
 resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
 
 // Use the Play sbt plugin for Play projects
-addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.3")
+addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.6")

+ 10 - 11
frameworks/Scala/play2-scala/play2-scala-slick/source_code

@@ -1,11 +1,10 @@
-./play-slick/app/
-./play-slick/app/controllers
-./play-slick/app/controllers/Application.scala
-./play-slick/app/views
-./play-slick/app/views/main.scala.html
-./play-slick/app/views/fortune.scala.html
-./play-slick/app/utils
-./play-slick/app/utils/PredicatedAction.scala
-./play-slick/app/models
-./play-slick/app/models/Fortune.scala
-./play-slick/app/models/World.scala
+./play2-scala-slick/app/
+./play2-scala-slick/app/Filters.scala
+./play2-scala-slick/app/controllers
+./play2-scala-slick/app/controllers/Application.scala
+./play2-scala-slick/app/views
+./play2-scala-slick/app/views/main.scala.html
+./play2-scala-slick/app/views/fortune.scala.html
+./play2-scala-slick/app/models
+./play2-scala-slick/app/models/Fortune.scala
+./play2-scala-slick/app/models/World.scala

+ 23 - 0
frameworks/Scala/play2-scala/play2-scala/app/Filters.scala

@@ -0,0 +1,23 @@
+import javax.inject.{Singleton, Inject}
+
+import play.api.http.HttpFilters
+import play.api.mvc.{RequestHeader, EssentialAction, EssentialFilter}
+import play.api.libs.concurrent.Execution.Implicits.defaultContext
+import play.mvc.Http
+
+@Singleton()
+class Filters @Inject() (headerFilter: HeaderFilter) extends HttpFilters {
+  override def filters: Seq[EssentialFilter] = Seq(
+    headerFilter)
+}
+
+@Singleton
+class HeaderFilter extends EssentialFilter {
+  def apply(next: EssentialAction) = new EssentialAction {
+    def apply(request: RequestHeader) = {
+      next(request).map(result =>
+        result.withHeaders(Http.HeaderNames.SERVER -> "EXAMPLE")  // Only here to address the WARN about this header.
+      )
+    }
+  }
+}

+ 18 - 12
frameworks/Scala/play2-scala/play2-scala/app/controllers/Application.scala

@@ -1,22 +1,28 @@
 package controllers
 
+import javax.inject.Singleton
+
 import play.api.mvc._
 import play.api.libs.json.Json
 
-object Application extends Controller {
+@Singleton
+class Application extends Controller {
 
-  def json() = Action {
-    Ok(Json.obj("message" -> "Hello, World!"))
-  }
+  // Test seems picky about headers.  Doesn't like character set being there for JSON.  Always wants Server header set.
+  // There is a Filter which adds the Server header for all types.  Below I set Content-Type as needed to get rid of
+  // warnings.
 
-  def plaintext() = Action {
-    import java.util.Date
-    import java.text.SimpleDateFormat
+  // Easy ones
+  case class HelloWorld(message: String)
+
+  def getJsonMessage = Action {
+    val helloWorld = HelloWorld(message = "Hello, World!")
+    Ok(Json.toJson(helloWorld)(Json.writes[HelloWorld])).withHeaders(CONTENT_TYPE -> "application/json")
+  }
 
-    val sdf = new SimpleDateFormat("EEE, MMM d yyyy HH:MM:ss z")
-    Ok("Hello, World!")
-      .withHeaders(
-        DATE -> sdf.format(new Date()),
-        SERVER -> "Play Framework 2.3.3")
+  val plaintext = Action {
+    // default headers are correct according to docs: charset included.
+    // BUT the test harness has a WARN state and says we don't need it.
+    Ok("Hello, World!").withHeaders(CONTENT_TYPE -> "text/plain")
   }
 }

+ 6 - 0
frameworks/Scala/play2-scala/play2-scala/build.sbt

@@ -5,3 +5,9 @@ version := "1.0-SNAPSHOT"
 scalaVersion := "2.11.7"
 
 lazy val root = (project in file(".")).enablePlugins(PlayScala)
+
+resolvers += "scalaz-bintray" at "http://dl.bintray.com/scalaz/releases"
+
+// Play provides two styles of routers, one expects its actions to be injected, the
+// other, legacy style, accesses its actions statically.
+routesGenerator := InjectedRoutesGenerator

+ 29 - 16
frameworks/Scala/play2-scala/play2-scala/conf/application.conf

@@ -4,28 +4,41 @@
 # Secret key
 # ~~~~~
 # The secret key is used to secure cryptographics functions.
-# If you deploy your application to several instances be sure to use the same key!
-application.secret="RItx1I:80?W@]8GAtPDuF8Ydd3mXM85p/<7og]Q;uBOdijQAauRDgu73B6`wQP59"
+#
+# This must be changed for production, but we recommend not changing it in this file.
+#
+# See http://www.playframework.com/documentation/latest/ApplicationSecret for more details.
+play.crypto.secret = "RItx1I:80?W@]8GAtPDuF8Ydd3mXM85p/<7og]Q;uBOdijQAauRDgu73B6`wQP59"
 
 # The application languages
 # ~~~~~
-application.langs="en"
+play.i18n.langs = [ "en" ]
 
-# Global object class
+# Router
 # ~~~~~
-# Define the Global object class for this application.
-# Default to Global in the root package.
-# global=Global
+# Define the Router object to use for this application.
+# This router will be looked up first when the application is starting up,
+# so make sure this is the entry point.
+# Furthermore, it's assumed your route file is named properly.
+# So for an application router like `my.application.Router`,
+# you may need to define a router file `conf/my.application.routes`.
+# Default to Routes in the root package (and conf/routes)
+# play.http.router = my.application.Routes
 
-# Logger
+# Database configuration
 # ~~~~~
-# You can also configure logback (http://logback.qos.ch/), by providing a logger.xml file in the conf directory .
+# You can declare as many datasources as you want.
+# By convention, the default datasource is named `default`
+#
+# db.default.driver=org.h2.Driver
+# db.default.url="jdbc:h2:mem:play"
+# db.default.username=sa
+# db.default.password=""
 
-# Root logger:
-logger.root=ERROR
-
-# Logger used by the framework:
-logger.play=ERROR
+# Evolutions
+# ~~~~~
+# You can disable evolutions if needed
+# play.evolutions.enabled=false
 
-# Logger provided to your application:
-logger.application=ERROR
+# You can disable evolutions for a specific datasource if necessary
+# play.evolutions.db.default.enabled=false

+ 2 - 2
frameworks/Scala/play2-scala/play2-scala/conf/routes

@@ -3,8 +3,8 @@
 # ~~~~
 
 # Home page
-GET     /json                           controllers.Application.json
-GET     /plaintext                      controllers.Application.plaintext
+GET     /json                           @controllers.Application.getJsonMessage
+GET     /plaintext                      @controllers.Application.plaintext
 
 # Map static resources from the /public folder to the /assets URL path
 GET     /assets/*file                   controllers.Assets.at(path="/public", file)

+ 1 - 1
frameworks/Scala/play2-scala/play2-scala/project/build.properties

@@ -1 +1 @@
-sbt.version=0.13.5
+sbt.version=0.13.9

+ 1 - 1
frameworks/Scala/play2-scala/play2-scala/project/plugins.sbt

@@ -5,4 +5,4 @@ logLevel := Level.Warn
 resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
 
 // Use the Play sbt plugin for Play projects
-addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.10")
+addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.4.6")

+ 2 - 9
frameworks/Scala/play2-scala/play2-scala/source_code

@@ -1,11 +1,4 @@
 ./play-scala/app/
+./play-scala/app/Filters.scala
 ./play-scala/app/controllers
-./play-scala/app/controllers/Application.scala
-./play-scala/app/views
-./play-scala/app/views/main.scala.html
-./play-scala/app/views/fortune.scala.html
-./play-scala/app/utils
-./play-scala/app/utils/PredicatedAction.scala
-./play-scala/app/models
-./play-scala/app/models/Fortune.scala
-./play-scala/app/models/World.scala
+./play-scala/app/controllers/Application.scala

+ 4 - 4
frameworks/Scala/play2-scala/setup_scala.sh

@@ -1,15 +1,15 @@
 #!/bin/bash
 
-fw_depends java7 sbt
+fw_depends java8 sbt
 
 cd play2-scala
-sed -i "s|jdbc:mysql:\/\/.*:3306|jdbc:mysql://${DBHOST}:3306|g" $TROOT/play2-scala/conf/application.conf
+sed -i "s|jdbc:mysql:\/\/.*:3306|jdbc:mysql://${DBHOST}:3306|g" ${TROOT}/play2-scala/conf/application.conf
 
 # Clear old running app.
-rm -rf $TROOT/play2-scala/target/universal/stage/RUNNING_PID
+rm -rf ${TROOT}/play2-scala/target/universal/stage/RUNNING_PID
 
 # Stage application.
 sbt stage
 
 # Execute Start script in background.
-$TROOT/play2-scala/target/universal/stage/bin/play2-scala &
+${TROOT}/play2-scala/target/universal/stage/bin/play2-scala &

+ 4 - 4
frameworks/Scala/play2-scala/setup_scala_activate.sh

@@ -1,14 +1,14 @@
 #!/bin/bash
 
-fw_depends java7 sbt
+fw_depends java8 sbt
 
 cd play2-scala-anorm
-sed -i "s|jdbc:mysql:\/\/.*:3306|jdbc:mysql://${DBHOST}:3306|g" $TROOT/play2-scala-anorm/conf/application.conf
+sed -i "s|jdbc:mysql:\/\/.*:3306|jdbc:mysql://${DBHOST}:3306|g" ${TROOT}/play2-scala-anorm/conf/application.conf
 
-rm -f -r $TROOT/play2-scala-anorm/target/universal/stage/RUNNING_PID
+rm -f -r ${TROOT}/play2-scala-anorm/target/universal/stage/RUNNING_PID
 
 # Stage application.
 sbt stage
 
 # Execute Start script in background.
-$TROOT/play2-scala-anorm/target/universal/stage/bin/play2-scala-anorm &
+${TROOT}/play2-scala-anorm/target/universal/stage/bin/play2-scala-anorm &

+ 4 - 4
frameworks/Scala/play2-scala/setup_scala_reactivemongo.sh

@@ -1,14 +1,14 @@
 #!/bin/bash
 
-fw_depends java7 sbt
+fw_depends java8 sbt
 
 cd play2-scala-reactivemongo
-sed -i 's|jdbc:mysql://.*:3306|jdbc:mysql://'"${DBHOST}"':3306|g' $TROOT/play2-scala-reactivemongo/conf/application.conf
+sed -i 's|jdbc:mysql://.*:3306|jdbc:mysql://'"${DBHOST}"':3306|g' ${TROOT}/play2-scala-reactivemongo/conf/application.conf
 
-rm -rf $TROOT/play2-scala-reactivemongo/target/universal/stage/RUNNING_PID
+rm -rf ${TROOT}/play2-scala-reactivemongo/target/universal/stage/RUNNING_PID
 
 # Stage application.
 sbt stage
 
 # Execute Start script in background.
-$TROOT/play2-scala-reactivemongo/target/universal/stage/bin/play2-scala-reactivemongo &
+${TROOT}/play2-scala-reactivemongo/target/universal/stage/bin/play2-scala-reactivemongo &

+ 5 - 3
frameworks/Scala/play2-scala/setup_scala_slick.sh

@@ -1,9 +1,11 @@
 #!/bin/bash
 
-fw_depends java7 sbt
+export JAVA_HOME=/opt/java8
+
+fw_depends java8 sbt
 
 cd play2-scala-slick
-sed -i "s|jdbc:mysql:\/\/.*:3306|jdbc:mysql://${DBHOST}:3306|g" $TROOT/play2-scala-slick/conf/application.conf
+sed -i "s|jdbc:mysql:\/\/.*:3306|jdbc:mysql://${DBHOST}:3306|g" ${TROOT}/play2-scala-slick/conf/application.conf
 
 rm -rf ${TROOT}/play2-scala-slick/target/universal/stage/RUNNING_PID
 
@@ -11,4 +13,4 @@ rm -rf ${TROOT}/play2-scala-slick/target/universal/stage/RUNNING_PID
 sbt stage
 
 # Execute Start script in background.
-$TROOT/play2-scala-slick/target/universal/stage/bin/play2-scala-slick &
+${TROOT}/play2-scala-slick/target/universal/stage/bin/play2-scala-slick &