Browse Source

Merged in fix #248 - play-activate-mysql - fwbrasil

Michael Robertson 12 years ago
parent
commit
b4df4891f2

+ 33 - 0
play-activate-mysql/.gitignore

@@ -0,0 +1,33 @@
+logs
+project/project
+project/target
+public
+target
+test
+tmp
+.history
+dist
+conf/evolutions
+
+# Ignore all dotfiles...
+.*
+# except for .gitignore
+!.gitignore
+
+# Ignore Play! working directory #
+db
+eclipse
+lib
+log
+logs
+modules
+precompiled
+project/project
+project/target
+target
+tmp
+test-result
+server.pid
+*.iml
+*.eml
+

+ 27 - 0
play-activate-mysql/README.md

@@ -0,0 +1,27 @@
+#Play Benchmarking Test
+
+This is the Play portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
+
+### JSON Encoding Test
+
+* [JSON test source](app/controllers/Application.scala)
+
+### Data-Store/Database Mapping Test
+
+* [Database test controller](app/controllers/Application.scala)
+* [Database test model](app/models/World.scala)
+
+## Infrastructure Software Versions
+The tests were run with:
+
+* [Java OpenJDK 1.7.0_09](http://openjdk.java.net/)
+* [Play 2.1.0](http://http://www.playframework.com/)
+
+## Test URLs
+### JSON Encoding Test
+
+http://localhost/json
+
+### Data-Store/Database Mapping Test
+
+http://localhost/db?queries=5

+ 7 - 0
play-activate-mysql/app/Global.scala

@@ -0,0 +1,7 @@
+import play.api._
+import com.typesafe.config.ConfigFactory
+
+object Global extends GlobalSettings {
+	System.setProperty("activate.offlineLocking.enable", "true")
+	System.setProperty("activate.offlineLocking.validateReads", "true")
+}

+ 62 - 0
play-activate-mysql/app/controllers/Application.scala

@@ -0,0 +1,62 @@
+package controllers
+
+import play.api.Play.current
+import play.api.mvc._
+import play.api.libs.json.Json
+import java.util.concurrent._
+import scala.concurrent._
+import scala.concurrent.Future
+import play.api.libs.concurrent.Execution.Implicits._
+import play.core.NamedThreadFactory
+import models.ActivateWorld
+import models.Models._
+import models.ActivateFortune
+import models.persistenceContext._
+
+object Application extends Controller {
+
+    private val TestDatabaseRows = 10000
+
+    def db(queries: Int) =
+        Action {
+            transactional {
+                val random = ThreadLocalRandom.current()
+
+                val worlds =
+                    for (_ <- 1 to queries) yield {
+                        ActivateWorld.fingByLegacyId(random.nextInt(TestDatabaseRows) + 1)
+                    }
+
+                Ok(Json.toJson(worlds))
+            }
+        }
+
+    def fortunes() =
+        Action {
+            val transaction = new Transaction
+            try
+                transactional(transaction) {
+                    val fortunes =
+                        ActivateFortune.all ++ List(new ActivateFortune(-1, "Additional fortune added at request time."))
+                    Ok(views.html.fortune(fortunes))
+                }
+            finally
+                transaction.rollback
+        }
+
+    def update(queries: Int) =
+        Action {
+
+            val random = ThreadLocalRandom.current()
+
+            transactional {
+                val worlds =
+                    for (_ <- 1 to queries) yield {
+                        val world = ActivateWorld.fingByLegacyId(random.nextInt(TestDatabaseRows) + 1)
+                        world.randomNumber = random.nextInt(TestDatabaseRows) + 1
+                        world
+                    }
+                Ok(Json.toJson(worlds)).withHeaders("Server" -> "Netty")
+            }
+        }
+}

+ 50 - 0
play-activate-mysql/app/models/Migrations.scala

@@ -0,0 +1,50 @@
+package models
+
+import persistenceContext._
+
+class CreateSchema extends Migration {
+
+    def timestamp = System.currentTimeMillis + 100
+
+    def up = {
+        removeAllEntitiesTables.cascade.ifExists
+        createTableForAllEntities.ifNotExists
+    }
+
+}
+
+class MigrateFortunes extends Migration {
+
+    def timestamp = System.currentTimeMillis + 200
+
+    def up = {
+        customScript {
+            val con = storage.directAccess
+            try {
+                val rs = con.createStatement.executeQuery("SELECT id, message FROM Fortune")
+                while (rs.next)
+                    new ActivateFortune(rs.getLong(1), rs.getString(2))
+            } finally
+                con.close
+        }
+    }
+
+}
+
+class MigrateWorlds extends Migration {
+
+    def timestamp = System.currentTimeMillis + 300
+
+    def up = {
+        customScript {
+            val con = storage.directAccess
+            try {
+                val rs = con.createStatement.executeQuery("SELECT id, randomNumber FROM World")
+                while (rs.next)
+                    new ActivateWorld(rs.getLong(1), rs.getInt(2))
+            } finally
+                con.close
+        }
+    }
+
+}

+ 37 - 0
play-activate-mysql/app/models/Models.scala

@@ -0,0 +1,37 @@
+package models
+
+import persistenceContext._
+import play.api.libs.json.JsValue
+import play.api.libs.json.Json
+import play.api.libs.json.Json.toJsFieldJsValueWrapper
+import play.api.libs.json.Writes
+
+class ActivateFortune(val legacyId: Long, val message: String) extends Entity
+
+object ActivateFortune {
+    def all =
+        cachedQuery {
+            (fortune: ActivateFortune) => where()
+        }
+}
+
+class ActivateWorld(val legacyId: Long, var randomNumber: Long) extends Entity
+
+object ActivateWorld {
+    def fingByLegacyId(legacyId: Long) = {
+        val lazyList = indexWorldByLegacyId.get(legacyId)
+        lazyList.headOption.getOrElse(throw new IllegalStateException("invalid id " + legacyId))
+    }
+}
+
+object Models {
+
+    implicit val worldToJson =
+        new Writes[ActivateWorld] {
+            def writes(w: ActivateWorld): JsValue =
+                Json.obj(
+                    "id" -> w.legacyId,
+                    "randomNumber" -> w.randomNumber)
+        }
+
+}

+ 26 - 0
play-activate-mysql/app/models/PersistenceContext.scala

@@ -0,0 +1,26 @@
+package models
+
+import net.fwbrasil.activate.ActivateContext
+import net.fwbrasil.activate.storage.relational.idiom.postgresqlDialect
+import net.fwbrasil.activate.storage.relational.PooledJdbcRelationalStorage
+import net.fwbrasil.activate.storage.relational.idiom.mySqlDialect
+import play.api.Play
+import net.fwbrasil.activate.OptimisticOfflineLocking
+
+object persistenceContext extends ActivateContext {
+	require(OptimisticOfflineLocking.isEnabled)
+	require(OptimisticOfflineLocking.validateReads)
+    
+    private def config = Play.current.configuration
+    
+    val storage = new PooledJdbcRelationalStorage {
+        val jdbcDriver = config.getString("db.default.driver").get
+        val user = config.getString("db.default.user").get
+        val password = config.getString("db.default.password").get
+        val url = config.getString("db.default.url").get
+        val dialect = mySqlDialect
+    }
+    
+    val indexWorldByLegacyId = memoryIndex[ActivateWorld].on(_.legacyId)
+
+}

+ 18 - 0
play-activate-mysql/app/views/fortune.scala.html

@@ -0,0 +1,18 @@
+@(fortunes: List[ActivateFortune])
+
+@main("Testcase 4 :: Fortune :: FrameworkBenchmark") {
+    <table>
+        <tr>
+            <th>id</th>
+            <th>message</th>
+        </tr>
+
+        @fortunes.sortBy(_.message).map { fortune => 
+            <tr>
+                <td>@fortune.legacyId</td>
+                <td>@fortune.message</td>
+            </tr>
+        }
+
+    </table>
+}

+ 13 - 0
play-activate-mysql/app/views/main.scala.html

@@ -0,0 +1,13 @@
+@(title: String)(content: Html)
+
+<!DOCTYPE html>
+
+<html>
+<head>
+    <title>@title</title>
+    <meta charset=utf-8>
+</head>
+<body>
+    @content
+</body>
+</html>

+ 26 - 0
play-activate-mysql/benchmark_config

@@ -0,0 +1,26 @@
+{
+  "framework": "play-activate-mysql",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "db_url": "/db",
+      "query_url": "/db?queries=",
+      "fortune_url": "/fortunes",
+      "update_url": "/update?queries=",
+      "port": 9000,
+      "approach": "Realistic",
+      "classification": "Fullstack",
+      "database": "MySQL",
+      "framework": "play-activate-mysql",
+      "language": "Scala",
+      "orm": "Full",
+      "platform": "Netty",
+      "webserver": "None",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "play-activate-mysql",
+      "notes": "",
+      "versus": "netty"
+    }
+  }]
+}

+ 86 - 0
play-activate-mysql/conf/application.conf

@@ -0,0 +1,86 @@
+# This is the main configuration file for the application.
+# ~~~~~
+
+# 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"
+
+# The application languages
+# ~~~~~
+application.langs="en"
+
+# Global object class
+# ~~~~~
+# Define the Global object class for this application.
+# Default to Global in the root package.
+# global=Global
+
+# 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
+
+# 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"]
+    loglevel = WARNING
+    actor {
+      default-dispatcher = {
+        fork-join-executor {
+          parallelism-factor = 1.0
+          parallelism-max = 50
+        }
+      }
+      application = {
+        fork-join-executor {
+          parallelism-max = 300
+        }
+      }
+    }
+  }
+}

+ 1 - 0
play-activate-mysql/conf/play.plugins

@@ -0,0 +1 @@
+100:net.fwbrasil.activate.play.ActivatePlayPlugin

+ 11 - 0
play-activate-mysql/conf/routes

@@ -0,0 +1,11 @@
+# Routes
+# This file defines all application routes (Higher priority routes first)
+# ~~~~
+
+# Home page
+GET     /db                             controllers.Application.db(queries: Int ?= 1)
+GET     /fortunes                       controllers.Application.fortunes
+GET     /update                         controllers.Application.update(queries: Int ?= 1)
+
+# Map static resources from the /public folder to the /assets URL path
+GET     /assets/*file                   controllers.Assets.at(path="/public", file)

+ 29 - 0
play-activate-mysql/project/Build.scala

@@ -0,0 +1,29 @@
+import sbt._
+import Keys._
+import PlayProject._
+
+object ApplicationBuild extends Build {
+
+  val appName         = "play-activate-mysql"
+  val appVersion      = "1.0-SNAPSHOT"
+
+  val activateVersion = "1.4.2"
+  val activateCore = "net.fwbrasil" %% "activate-core" % activateVersion
+  val activateJdbc = "net.fwbrasil" %% "activate-jdbc" % activateVersion
+  val activatePlay = "net.fwbrasil" %% "activate-play" % "1.4.2-PLAY-2.1.2-RC1"
+
+  val mysql = "mysql" % "mysql-connector-java" % "5.1.16"
+
+  val appDependencies = Seq(
+    jdbc,
+    mysql,
+    activateCore,
+    activateJdbc,
+    activatePlay
+  )
+
+  val main = play.Project(appName, appVersion, appDependencies).settings(
+	resolvers ++= Seq("fwbrasil.net" at "http://fwbrasil.net/maven/")
+  )
+
+}

+ 1 - 0
play-activate-mysql/project/build.properties

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

+ 8 - 0
play-activate-mysql/project/plugins.sbt

@@ -0,0 +1,8 @@
+// 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("play" % "sbt-plugin" % "2.1.2-RC1")

+ 42 - 0
play-activate-mysql/setup.py

@@ -0,0 +1,42 @@
+
+import subprocess
+import sys
+import setup_util
+import os
+from zipfile import ZipFile
+
+def start(args):
+  setup_util.replace_text("play-activate-mysql/conf/application.conf", "jdbc:mysql:\/\/.*:3306", "jdbc:mysql://" + args.database_host + ":3306")
+
+  subprocess.check_call("play dist", shell=True, cwd="play-activate-mysql")
+
+  if os.name == 'nt':
+    ZipFile("./play-activate-mysql/dist/play-activate-mysql-1.0-SNAPSHOT.zip").extractall("./play-activate-mysql/dist")
+    with open("./play-activate-mysql/dist/play-activate-mysql-1.0-SNAPSHOT/start.bat", "w+") as f:
+      f.write("java %1 -cp \"./lib/*;\" play.core.server.NettyServer .")
+    subprocess.Popen("start.bat", shell=True, cwd="play-activate-mysql/dist/play-activate-mysql-1.0-SNAPSHOT")
+  else:
+    subprocess.check_call("unzip play-activate-mysql-1.0-SNAPSHOT.zip", shell=True, cwd="play-activate-mysql/dist")
+    subprocess.check_call("chmod +x start", shell=True, cwd="play-activate-mysql/dist/play-activate-mysql-1.0-SNAPSHOT")
+    subprocess.Popen("./start", shell=True, cwd="play-activate-mysql/dist/play-activate-mysql-1.0-SNAPSHOT")
+
+  return 0
+def stop():
+  if os.name == 'nt':
+    with open("./play-activate-mysql/dist/play-activate-mysql-1.0-SNAPSHOT/RUNNING_PID") as f:
+      pid = int(f.read())
+      os.kill(pid, 9)
+  else:
+    p = subprocess.Popen(['ps', 'aux'], stdout=subprocess.PIPE)
+    out, err = p.communicate()
+    for line in out.splitlines():
+      if './start' in line or ('play' in line and 'java' in line):
+        pid = int(line.split(None, 2)[1])
+        os.kill(pid, 9)
+
+  try:
+    os.remove("play-activate-mysql/RUNNING_PID")
+  except OSError:
+    pass
+
+  return 0