Browse Source

Merge branch 'master' of https://github.com/m20o/FrameworkBenchmarks into m20o-master

Patrick Falls 12 years ago
parent
commit
09bf146d57

+ 69 - 0
scalatra/.gitignore

@@ -0,0 +1,69 @@
+## generic files to ignore
+*~
+*.lock
+*.DS_Store
+*.swp
+*.out
+
+# rails specific
+*.sqlite3
+config/database.yml
+log/*
+tmp/*
+
+# java specific
+*.class
+
+# python specific
+*.pyc
+
+# xcode/iphone specific
+build/*
+*.pbxuser
+*.mode2v3
+*.mode1v3
+*.perspective
+*.perspectivev3
+*~.nib
+
+# akka specific
+logs/*
+
+# sbt specific
+target/
+project/boot
+lib_managed/*
+project/build/target
+project/build/lib_managed
+project/build/src_managed
+project/plugins/lib_managed
+project/plugins/target
+project/plugins/src_managed
+project/plugins/project
+
+core/lib_managed
+core/target
+pubsub/lib_managed
+pubsub/target
+
+# eclipse specific
+.metadata
+jrebel.lic
+.settings
+.classpath
+.project
+
+.ensime*
+*.sublime-*
+.cache
+
+# intellij
+*.eml
+*.iml
+*.ipr
+*.iws
+.*.sw?
+.idea
+
+# paulp script
+/.lib/

+ 27 - 0
scalatra/README.md

@@ -0,0 +1,27 @@
+#Scalatra Benchmarking Test
+
+This is the Scalatra portion of a [benchmarking test suite](../) comparing a variety of web development platforms.
+
+### JSON Encoding Test
+This example uses the built-in [JacksonJsonSupport](http://scalatra.org/2.2/guides/formats/json.html)
+
+* [JSON test source](src/main/scala/hello/JsonController.scala)
+
+### Data-Store/Database Mapping Test
+* [DB test source](src/main/scala/hello/DbController.scala)
+
+## Infrastructure Software Versions
+The tests were run with:
+
+* [Java OpenJDK 1.7.0_09](http://openjdk.java.net/)
+* [Scalatra 2.2.0](http://www.scalatra.org/)
+
+## Test URLs
+### JSON Encoding Test
+
+http://localhost:8080/json
+
+### Data-Store/Database Mapping Test
+
+http://localhost:8080/db?queries=5
+

+ 0 - 0
scalatra/__init__.py


+ 13 - 0
scalatra/benchmark_config

@@ -0,0 +1,13 @@
+{
+  "framework": "scalatra",
+  "tests": [{
+    "default": {
+      "setup_file": "setup",
+      "json_url": "/json",
+      "db_url": "/db",
+      "query_url": "/db?queries=",
+      "port": 8080,
+      "sort": 18
+    }
+  }]
+}

+ 1 - 0
scalatra/project/build.properties

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

+ 39 - 0
scalatra/project/build.scala

@@ -0,0 +1,39 @@
+import sbt._
+import Keys._
+import org.scalatra.sbt._
+import org.scalatra.sbt.PluginKeys._
+import com.mojolly.scalate.ScalatePlugin._
+import ScalateKeys._
+
+object BenchmarkBuild extends Build {
+  val Organization = "hello.benchmark"
+  val Name = "scalatra"
+  val Version = "1.0-SNAPSHOT"
+  val ScalaVersion = "2.10.0"
+  val ScalatraVersion = "2.2.0"
+
+  lazy val project = Project (
+    "scalatra-benchmark",
+    file("."),
+    settings = Defaults.defaultSettings ++ ScalatraPlugin.scalatraWithJRebel ++ scalateSettings ++ Seq(
+      organization := Organization,
+      name := Name,
+      version := Version,
+      scalaVersion := ScalaVersion,
+      resolvers += Classpaths.typesafeReleases,
+      libraryDependencies ++= Seq(
+        "org.scalatra" %% "scalatra" % ScalatraVersion,
+        "org.scalatra" %% "scalatra-json" % ScalatraVersion,
+        mysql,
+        json4s,
+        "ch.qos.logback" % "logback-classic" % "1.0.6" % "runtime",
+        "org.eclipse.jetty" % "jetty-webapp" % "8.1.8.v20121106" % "container",
+        "org.eclipse.jetty.orbit" % "javax.servlet" % "3.0.0.v201112011016" % "container;provided;test" artifacts (Artifact("javax.servlet", "jar", "jar"))
+      )
+    )
+  )
+
+  val mysql = "mysql" % "mysql-connector-java" % "5.1.23"
+
+  val json4s = "org.json4s"   %% "json4s-jackson" % "3.2.4"
+}

+ 6 - 0
scalatra/project/plugins.sbt

@@ -0,0 +1,6 @@
+addSbtPlugin("com.mojolly.scalate" % "xsbt-scalate-generator" % "0.4.2")
+
+addSbtPlugin("org.scalatra.sbt" % "scalatra-sbt" % "0.2.0")
+
+addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.2.0")
+

+ 461 - 0
scalatra/sbt

@@ -0,0 +1,461 @@
+#!/usr/bin/env bash
+#
+# A more capable sbt runner, coincidentally also called sbt.
+# Author: Paul Phillips <[email protected]>
+
+# todo - make this dynamic
+declare -r sbt_release_version=0.12.0
+declare -r sbt_snapshot_version=0.13.0-SNAPSHOT
+
+unset sbt_jar sbt_dir sbt_create sbt_snapshot sbt_launch_dir
+unset scala_version java_home sbt_explicit_version
+unset verbose debug quiet
+
+for arg in "$@"; do
+  case $arg in
+    -q|-quiet)  quiet=1 ;;
+            *)          ;;
+  esac
+done
+
+build_props_sbt () {
+  if [[ -f project/build.properties ]]; then
+    versionLine=$(grep ^sbt.version project/build.properties)
+    versionString=${versionLine##sbt.version=}
+    echo "$versionString"
+  fi
+}
+
+update_build_props_sbt () {
+  local ver="$1"
+  local old=$(build_props_sbt)
+
+  if [[ $ver == $old ]]; then
+    return
+  elif [[ -f project/build.properties ]]; then
+    perl -pi -e "s/^sbt\.version=.*\$/sbt.version=${ver}/" project/build.properties
+    grep -q '^sbt.version=' project/build.properties || echo "sbt.version=${ver}" >> project/build.properties
+
+    echo !!!
+    echo !!! Updated file project/build.properties setting sbt.version to: $ver
+    echo !!! Previous value was: $old
+    echo !!!
+  fi
+}
+
+sbt_version () {
+  if [[ -n $sbt_explicit_version ]]; then
+    echo $sbt_explicit_version
+  else
+    local v=$(build_props_sbt)
+    if [[ -n $v ]]; then
+      echo $v
+    else
+      echo $sbt_release_version
+    fi
+  fi
+}
+
+echoerr () {
+  [[ -z $quiet ]] && echo 1>&2 "$@"
+}
+vlog () {
+  [[ $verbose || $debug ]] && echoerr "$@"
+}
+dlog () {
+  [[ $debug ]] && echoerr "$@"
+}
+
+# this seems to cover the bases on OSX, and someone will
+# have to tell me about the others.
+get_script_path () {
+  local path="$1"
+  [[ -L "$path" ]] || { echo "$path" ; return; }
+
+  local target=$(readlink "$path")
+  if [[ "${target:0:1}" == "/" ]]; then
+    echo "$target"
+  else
+    echo "$(dirname $path)/$target"
+  fi
+}
+
+# a ham-fisted attempt to move some memory settings in concert
+# so they need not be dicked around with individually.
+get_mem_opts () {
+  local mem=${1:-1536}
+  local perm=$(( $mem / 4 ))
+  (( $perm > 256 )) || perm=256
+  (( $perm < 1024 )) || perm=1024
+  local codecache=$(( $perm / 2 ))
+
+  echo "-Xms${mem}m -Xmx${mem}m -XX:MaxPermSize=${perm}m -XX:ReservedCodeCacheSize=${codecache}m"
+}
+
+die() {
+  echo "Aborting: $@"
+  exit 1
+}
+
+make_url () {
+  groupid="$1"
+  category="$2"
+  version="$3"
+
+  echo "http://typesafe.artifactoryonline.com/typesafe/ivy-$category/$groupid/sbt-launch/$version/sbt-launch.jar"
+}
+
+declare -r default_jvm_opts="-Dfile.encoding=UTF8"
+declare -r default_sbt_opts="-XX:+CMSClassUnloadingEnabled"
+declare -r default_sbt_mem=1536
+declare -r noshare_opts="-Dsbt.global.base=project/.sbtboot -Dsbt.boot.directory=project/.boot -Dsbt.ivy.home=project/.ivy"
+declare -r sbt_opts_file=".sbtopts"
+declare -r jvm_opts_file=".jvmopts"
+declare -r latest_28="2.8.2"
+declare -r latest_29="2.9.2"
+declare -r latest_210="2.10.0-SNAPSHOT"
+
+declare -r script_path=$(get_script_path "$BASH_SOURCE")
+declare -r script_dir="$(dirname $script_path)"
+declare -r script_name="$(basename $script_path)"
+
+# some non-read-onlies set with defaults
+declare java_cmd=java
+declare sbt_launch_dir="$script_dir/.lib"
+declare sbt_universal_launcher="$script_dir/lib/sbt-launch.jar"
+declare sbt_mem=$default_sbt_mem
+declare sbt_jar=$sbt_universal_launcher
+
+# pull -J and -D options to give to java.
+declare -a residual_args
+declare -a java_args
+declare -a scalac_args
+declare -a sbt_commands
+
+build_props_scala () {
+  if [[ -f project/build.properties ]]; then
+    versionLine=$(grep ^build.scala.versions project/build.properties)
+    versionString=${versionLine##build.scala.versions=}
+    echo ${versionString%% .*}
+  fi
+}
+
+execRunner () {
+  # print the arguments one to a line, quoting any containing spaces
+  [[ $verbose || $debug ]] && echo "# Executing command line:" && {
+    for arg; do
+      if printf "%s\n" "$arg" | grep -q ' '; then
+        printf "\"%s\"\n" "$arg"
+      else
+        printf "%s\n" "$arg"
+      fi
+    done
+    echo ""
+  }
+
+  exec "$@"
+}
+
+sbt_groupid () {
+  case $(sbt_version) in
+        0.7.*) echo org.scala-tools.sbt ;;
+       0.10.*) echo org.scala-tools.sbt ;;
+    0.11.[12]) echo org.scala-tools.sbt ;;
+            *) echo org.scala-sbt ;;
+  esac
+}
+
+sbt_artifactory_list () {
+  local version0=$(sbt_version)
+  local version=${version0%-SNAPSHOT}
+  local url="http://typesafe.artifactoryonline.com/typesafe/ivy-snapshots/$(sbt_groupid)/sbt-launch/"
+  dlog "Looking for snapshot list at: $url "
+
+  curl -s --list-only "$url" | \
+    grep -F $version | \
+    perl -e 'print reverse <>' | \
+    perl -pe 's#^<a href="([^"/]+).*#$1#;'
+}
+
+make_release_url () {
+  make_url $(sbt_groupid) releases $(sbt_version)
+}
+
+# argument is e.g. 0.13.0-SNAPSHOT
+# finds the actual version (with the build id) at artifactory
+make_snapshot_url () {
+  for ver in $(sbt_artifactory_list); do
+    local url=$(make_url $(sbt_groupid) snapshots $ver)
+    dlog "Testing $url"
+    curl -s --head "$url" >/dev/null
+    dlog "curl returned: $?"
+    echo "$url"
+    return
+  done
+}
+
+jar_url () {
+  case $(sbt_version) in
+             0.7.*) echo "http://simple-build-tool.googlecode.com/files/sbt-launch-0.7.7.jar" ;;
+        *-SNAPSHOT) make_snapshot_url ;;
+                 *) make_release_url ;;
+  esac
+}
+
+jar_file () {
+  echo "$sbt_launch_dir/$1/sbt-launch.jar"
+}
+
+download_url () {
+  local url="$1"
+  local jar="$2"
+
+  echo "Downloading sbt launcher $(sbt_version):"
+  echo "  From  $url"
+  echo "    To  $jar"
+
+  mkdir -p $(dirname "$jar") && {
+    if which curl >/dev/null; then
+      curl --fail --silent "$url" --output "$jar"
+    elif which wget >/dev/null; then
+      wget --quiet -O "$jar" "$url"
+    fi
+  } && [[ -f "$jar" ]]
+}
+
+acquire_sbt_jar () {
+  sbt_url="$(jar_url)"
+  sbt_jar="$(jar_file $(sbt_version))"
+
+  [[ -f "$sbt_jar" ]] || download_url "$sbt_url" "$sbt_jar"
+}
+
+usage () {
+  cat <<EOM
+Usage: $script_name [options]
+
+  -h | -help         print this message
+  -v | -verbose      this runner is chattier
+  -d | -debug        set sbt log level to Debug
+  -q | -quiet        set sbt log level to Error
+  -no-colors         disable ANSI color codes
+  -sbt-create        start sbt even if current directory contains no sbt project
+  -sbt-dir   <path>  path to global settings/plugins directory (default: ~/.sbt/<version>)
+  -sbt-boot  <path>  path to shared boot directory (default: ~/.sbt/boot in 0.11+)
+  -ivy       <path>  path to local Ivy repository (default: ~/.ivy2)
+  -mem    <integer>  set memory options (default: $sbt_mem, which is
+                       $(get_mem_opts $sbt_mem) )
+  -no-share          use all local caches; no sharing
+  -offline           put sbt in offline mode
+  -jvm-debug <port>  Turn on JVM debugging, open at the given port.
+  -batch             Disable interactive mode
+
+  # sbt version (default: from project/build.properties if present, else latest release)
+  !!! The only way to accomplish this pre-0.12.0 if there is a build.properties file which
+  !!! contains an sbt.version property is to update the file on disk.  That's what this does.
+  -sbt-version  <version>   use the specified version of sbt
+  -sbt-jar      <path>      use the specified jar as the sbt launcher
+  -sbt-snapshot             use a snapshot version of sbt
+  -sbt-launch-dir <path>    directory to hold sbt launchers (default: $sbt_launch_dir)
+
+  # scala version (default: as chosen by sbt)
+  -28                       use $latest_28
+  -29                       use $latest_29
+  -210                      use $latest_210
+  -scala-home <path>        use the scala build at the specified directory
+  -scala-version <version>  use the specified version of scala
+
+  # java version (default: java from PATH, currently $(java -version |& grep version))
+  -java-home <path>         alternate JAVA_HOME
+
+  # jvm options and output control
+  JAVA_OPTS     environment variable holding jvm args, if unset uses "$default_jvm_opts"
+  SBT_OPTS      environment variable holding jvm args, if unset uses "$default_sbt_opts"
+  .jvmopts      if file is in sbt root, it is prepended to the args given to the jvm
+  .sbtopts      if file is in sbt root, it is prepended to the args given to **sbt**
+  -Dkey=val     pass -Dkey=val directly to the jvm
+  -J-X          pass option -X directly to the jvm (-J is stripped)
+  -S-X          add -X to sbt's scalacOptions (-J is stripped)
+
+In the case of duplicated or conflicting options, the order above
+shows precedence: JAVA_OPTS lowest, command line options highest.
+EOM
+}
+
+addJava () {
+  dlog "[addJava] arg = '$1'"
+  java_args=( "${java_args[@]}" "$1" )
+}
+addSbt () {
+  dlog "[addSbt] arg = '$1'"
+  sbt_commands=( "${sbt_commands[@]}" "$1" )
+}
+addScalac () {
+  dlog "[addScalac] arg = '$1'"
+  scalac_args=( "${scalac_args[@]}" "$1" )
+}
+addResidual () {
+  dlog "[residual] arg = '$1'"
+  residual_args=( "${residual_args[@]}" "$1" )
+}
+addResolver () {
+  addSbt "set resolvers in ThisBuild += $1"
+}
+addDebugger () {
+  addJava "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=n,address=$1"
+}
+
+jrebelAgent () {
+    SCALATRA_PROJECT_ROOT="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+    if [ -z "$SCALATRA_JREBEL" ];
+        then echo -n '';
+        else echo -n "-javaagent:$SCALATRA_JREBEL -Dscalatra_project_root=${SCALATRA_PROJECT_ROOT}";
+    fi
+}
+
+get_jvm_opts () {
+  echo "${JAVA_OPTS:-$default_jvm_opts}"
+  echo "`jrebelAgent` ${SBT_OPTS:-$default_sbt_opts}"
+
+  [[ -f "$jvm_opts_file" ]] && cat "$jvm_opts_file"
+}
+
+process_args ()
+{
+  require_arg () {
+    local type="$1"
+    local opt="$2"
+    local arg="$3"
+
+    if [[ -z "$arg" ]] || [[ "${arg:0:1}" == "-" ]]; then
+      die "$opt requires <$type> argument"
+    fi
+  }
+  while [[ $# -gt 0 ]]; do
+    case "$1" in
+       -h|-help) usage; exit 1 ;;
+    -v|-verbose) verbose=1 && shift ;;
+      -d|-debug) debug=1 && shift ;;
+      -q|-quiet) quiet=1 && shift ;;
+
+           -ivy) require_arg path "$1" "$2" && addJava "-Dsbt.ivy.home=$2" && shift 2 ;;
+           -mem) require_arg integer "$1" "$2" && sbt_mem="$2" && shift 2 ;;
+     -no-colors) addJava "-Dsbt.log.noformat=true" && shift ;;
+      -no-share) addJava "$noshare_opts" && shift ;;
+      -sbt-boot) require_arg path "$1" "$2" && addJava "-Dsbt.boot.directory=$2" && shift 2 ;;
+       -sbt-dir) require_arg path "$1" "$2" && sbt_dir="$2" && shift 2 ;;
+     -debug-inc) addJava "-Dxsbt.inc.debug=true" && shift ;;
+       -offline) addSbt "set offline := true" && shift ;;
+     -jvm-debug) require_arg port "$1" "$2" && addDebugger $2 && shift 2 ;;
+         -batch) exec </dev/null && shift ;;
+
+    -sbt-create) sbt_create=true && shift ;;
+  -sbt-snapshot) sbt_explicit_version=$sbt_snapshot_version && shift ;;
+       -sbt-jar) require_arg path "$1" "$2" && sbt_jar="$2" && shift 2 ;;
+   -sbt-version) require_arg version "$1" "$2" && sbt_explicit_version="$2" && shift 2 ;;
+-sbt-launch-dir) require_arg path "$1" "$2" && sbt_launch_dir="$2" && shift 2 ;;
+ -scala-version) require_arg version "$1" "$2" && addSbt "set scalaVersion := \"$2\"" && shift 2 ;;
+    -scala-home) require_arg path "$1" "$2" && addSbt "set scalaHome in ThisBuild := Some(file(\"$2\"))" && shift 2 ;;
+     -java-home) require_arg path "$1" "$2" && java_cmd="$2/bin/java" && shift 2 ;;
+
+            -D*) addJava "$1" && shift ;;
+            -J*) addJava "${1:2}" && shift ;;
+            -S*) addScalac "${1:2}" && shift ;;
+            -28) addSbt "++ $latest_28" && shift ;;
+            -29) addSbt "++ $latest_29" && shift ;;
+           -210) addSbt "++ $latest_210" && shift ;;
+
+              *) addResidual "$1" && shift ;;
+    esac
+  done
+
+  [[ $debug ]] && {
+    case $(sbt_version) in
+     0.7.*) addSbt "debug" ;;
+         *) addSbt "set logLevel in Global := Level.Debug" ;;
+    esac
+  }
+  [[ $quiet ]] && {
+    case $(sbt_version) in
+     0.7.*) ;;
+         *) addSbt "set logLevel in Global := Level.Error" ;;
+    esac
+  }
+}
+
+# if .sbtopts exists, prepend its contents to $@ so it can be processed by this runner
+[[ -f "$sbt_opts_file" ]] && {
+  sbtargs=()
+  while IFS= read -r arg; do
+    sbtargs=( "${sbtargs[@]}" "$arg" )
+  done <"$sbt_opts_file"
+
+  set -- "${sbtargs[@]}" "$@"
+}
+
+# process the combined args, then reset "$@" to the residuals
+process_args "$@"
+set -- "${residual_args[@]}"
+argumentCount=$#
+
+# set scalacOptions if we were given any -S opts
+[[ ${#scalac_args[@]} -eq 0 ]] || addSbt "set scalacOptions in ThisBuild += \"${scalac_args[@]}\""
+
+# Update build.properties no disk to set explicit version - sbt gives us no choice
+[[ -n "$sbt_explicit_version" ]] && update_build_props_sbt "$sbt_explicit_version"
+echoerr "Detected sbt version $(sbt_version)"
+
+[[ -n "$scala_version" ]] && echo "Overriding scala version to $scala_version"
+
+# no args - alert them there's stuff in here
+(( $argumentCount > 0 )) || echo "Starting $script_name: invoke with -help for other options"
+
+# verify this is an sbt dir or -create was given
+[[ -f ./build.sbt || -d ./project || -n "$sbt_create" ]] || {
+  cat <<EOM
+$(pwd) doesn't appear to be an sbt project.
+If you want to start sbt anyway, run:
+  $0 -sbt-create
+
+EOM
+  exit 1
+}
+
+# pick up completion if present; todo
+[[ -f .sbt_completion.sh ]] && source .sbt_completion.sh
+
+# no jar? download it.
+[[ -f "$sbt_jar" ]] || acquire_sbt_jar || {
+  # still no jar? uh-oh.
+  echo "Download failed. Obtain the jar manually and place it at $sbt_jar"
+  exit 1
+}
+
+[[ -n "$sbt_dir" ]] || {
+  sbt_dir=~/.sbt/$(sbt_version)
+  addJava "-Dsbt.global.base=$sbt_dir"
+  echoerr "Using $sbt_dir as sbt dir, -sbt-dir to override."
+}
+
+# since sbt 0.7 doesn't understand iflast
+(( ${#residual_args[@]} == 0 )) && residual_args=( "shell" )
+
+if [ ! -z "$SCALATRA_JREBEL" ]; then
+    if [ ! -r "$SCALATRA_JREBEL" ]; then
+        echo "################################################################################"
+        echo "                             SCALATRA WARNING"
+        echo "Cannot find and/or read $SCALATRA_JREBEL."
+        echo 'Update $SCALATRA_JREBEL to a new JRebel jar path, or unset the variable.'
+        echo "################################################################################"
+        exit 1
+    fi
+fi
+
+# run sbt
+execRunner "$java_cmd" \
+  $(get_mem_opts $sbt_mem) \
+  $(get_jvm_opts) \
+  ${java_args[@]} \
+  -jar "$sbt_jar" \
+  "${sbt_commands[@]}" \
+  "${residual_args[@]}"

+ 22 - 0
scalatra/setup.py

@@ -0,0 +1,22 @@
+
+import subprocess
+import sys
+import setup_util
+
+def start(args):
+  setup_util.replace_text("scalatra/src/main/webapp/WEB-INF/resin-web.xml", "mysql:\/\/.*:3306", "mysql://" + args.database_host + ":3306")
+
+  try:
+    subprocess.check_call("./sbt clean package", shell=True, cwd="scalatra")
+    subprocess.check_call("rm -rf $RESIN_HOME/webapps/*", shell=True)
+    subprocess.check_call("cp scalatra/target/scala-2.10/scalatra*.war $RESIN_HOME/webapps/", shell=True)
+    subprocess.check_call("$RESIN_HOME/bin/resinctl start", shell=True)
+    return 0
+  except subprocess.CalledProcessError:
+    return 1
+def stop():
+  try:
+    subprocess.check_call("$RESIN_HOME/bin/resinctl shutdown", shell=True)
+    return 0
+  except subprocess.CalledProcessError:
+    return 1

+ 13 - 0
scalatra/src/main/scala/ScalatraBootstrap.scala

@@ -0,0 +1,13 @@
+import hello.{DbController, JsonController}
+import javax.servlet.ServletContext
+import org.scalatra.LifeCycle
+
+class ScalatraBootstrap extends LifeCycle {
+
+  override def init(context: ServletContext) {
+
+    context.mount(new JsonController(), "/json")
+    context.mount(new DbController(), "/db")
+
+  }
+}

+ 34 - 0
scalatra/src/main/scala/hello/DataSourceProvider.scala

@@ -0,0 +1,34 @@
+package hello
+
+import javax.sql.DataSource
+import com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource
+import javax.naming.InitialContext
+
+
+sealed trait DataSourceProvider {
+
+  def dataSource: DataSource
+
+}
+
+// Used for tests
+trait TestDataSourceProvider extends DataSourceProvider {
+
+  val dataSource: DataSource = {
+    val ds = new MysqlConnectionPoolDataSource
+    ds.setUser("root")
+    ds.setServerName("localhost")
+    ds.setDatabaseName("hello_world")
+    ds
+  }
+
+}
+
+/**
+ * JNDI lookup
+ */
+trait JndiDataSourceProvider extends DataSourceProvider {
+
+  lazy val dataSource = new InitialContext().lookup("jdbc/hello_world").asInstanceOf[DataSource]
+
+}

+ 41 - 0
scalatra/src/main/scala/hello/DbController.scala

@@ -0,0 +1,41 @@
+package hello
+
+import org.scalatra.ScalatraServlet
+import hello.model.{JdbcQuery, World, SingleResultQuery}
+import java.util.concurrent.ThreadLocalRandom
+import scala.annotation.tailrec
+
+class DbController extends ScalatraServlet with JsonSetup with DbSetup with JndiDataSourceProvider {
+
+  val maxRows = 10000
+
+  val query = SingleResultQuery.byID[World]("SELECT * FROM World WHERE id = ?") {
+    rs => World(rs.getInt("id"), rs.getInt("randomNumber"))
+  }
+
+  get("/") {
+    val count: Int = params.getAs[Int]("queries").getOrElse(1)
+    useQuery(query) {
+      q =>
+        buildResultList(count, q)
+    }
+  }
+
+
+  private def buildResultList[T](n: Int, q: JdbcQuery[T]): List[T] = {
+    val first = fetchSingle(random, q)
+    recursiveFetch(n - 1, q, first, Nil)
+  }
+
+  private def fetchSingle[T](id: Int, q: JdbcQuery[T]): T = q.execute(id).getOrElse(null.asInstanceOf[T])
+
+  @tailrec
+  private def recursiveFetch[T](n: Int, q: JdbcQuery[T], last: T, fetched: List[T]): List[T] =
+    if (n == 0) {
+      last :: fetched
+    } else {
+      recursiveFetch(n - 1, q, fetchSingle(random, q), last :: fetched)
+    }
+
+  private def random = ThreadLocalRandom.current().nextInt(maxRows) + 1
+}

+ 19 - 0
scalatra/src/main/scala/hello/DbSetup.scala

@@ -0,0 +1,19 @@
+package hello
+
+import javax.sql.DataSource
+import hello.model.JdbcQuery
+import java.sql.Connection
+
+trait DbSetup  {
+
+  def dataSource: DataSource
+
+  def useQuery[A, B](query: JdbcQuery[A])(f: (JdbcQuery[A]) => B): B = {
+    val connection: Connection = if (dataSource != null) dataSource.getConnection else throw new IllegalStateException("DataSource not found")
+    val opened = query.open(connection)
+    val res = f(opened)
+    opened.close()
+    res
+  }
+
+}

+ 13 - 0
scalatra/src/main/scala/hello/JsonController.scala

@@ -0,0 +1,13 @@
+package hello
+
+import org.scalatra.ScalatraServlet
+import java.lang.String
+
+class JsonController extends ScalatraServlet with JsonSetup {
+
+  case class Message(message: String)
+
+  get("/") {
+    Message("Hello, World!")
+  }
+}

+ 23 - 0
scalatra/src/main/scala/hello/JsonSetup.scala

@@ -0,0 +1,23 @@
+package hello
+
+import org.scalatra.json.JacksonJsonSupport
+import org.json4s.{DefaultFormats, Formats}
+import org.scalatra.ScalatraServlet
+
+/**
+ * Created with IntelliJ IDEA.
+ * User: mmazzarolo
+ * Date: 4/10/13
+ * Time: 6:57 PM
+ * To change this template use File | Settings | File Templates.
+ */
+trait JsonSetup extends JacksonJsonSupport {
+
+  that: ScalatraServlet =>
+
+  protected implicit val jsonFormats: Formats = DefaultFormats
+
+  before() {
+    contentType = formats("json")
+  }
+}

+ 48 - 0
scalatra/src/main/scala/hello/model/JdbcQuery.scala

@@ -0,0 +1,48 @@
+package hello.model
+
+import java.sql.{PreparedStatement, ResultSet, Connection}
+
+case class Param[T](idx: Int, value: T)
+
+abstract class JdbcQuery[T](mapper: (ResultSet) => T) {
+
+  def statement: PreparedStatement
+
+  def open(conn: Connection): JdbcQuery[T]
+
+  def close(): JdbcQuery[T]
+
+  def execute(param: Int) = {
+    statement.setInt(1, param)
+    val rs = statement.executeQuery()
+    if (rs.next()) Some(mapper(rs)) else None
+  }
+}
+
+sealed class ClosedQuery[T](query: String, m: (ResultSet) => T) extends JdbcQuery[T](m) {
+
+  def statement = throw new RuntimeException("Cannot execute a closed query")
+
+  def open(conn: Connection) = new OpenQuery[T](conn, query, m)
+
+  def close() = this
+}
+
+sealed class OpenQuery[T](c: Connection, query: String, m: (ResultSet) => T) extends JdbcQuery[T](m) {
+
+  val statement = c.prepareStatement(query, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY)
+
+  def open(conn: Connection) = this
+
+  def close() = {
+    c.close()
+    new ClosedQuery[T](query, m)
+  }
+}
+
+
+object SingleResultQuery {
+
+  def byID[T](query: String)(mapper: (ResultSet) => T): JdbcQuery[T] = new ClosedQuery[T](query, mapper)
+
+}

+ 3 - 0
scalatra/src/main/scala/hello/model/World.scala

@@ -0,0 +1,3 @@
+package hello.model
+
+case class World(id: Int, randomNumber: Int)

+ 13 - 0
scalatra/src/main/webapp/WEB-INF/resin-web.xml

@@ -0,0 +1,13 @@
+<web-app xmlns="http://caucho.com/ns/resin">
+
+<database jndi-name='jdbc/hello_world'>
+  <driver>
+    <type>com.mysql.jdbc.jdbc2.optional.MysqlConnectionPoolDataSource</type>
+    <url>jdbc:mysql://localhost:3306/hello_world?jdbcCompliantTruncation=false&amp;elideSetAutoCommits=true&amp;useLocalSessionState=true&amp;cachePrepStmts=true&amp;cacheCallableStmts=true&amp;alwaysSendSetIsolation=false&amp;prepStmtCacheSize=4096&amp;cacheServerConfiguration=true&amp;prepStmtCacheSqlLimit=2048&amp;zeroDateTimeBehavior=convertToNull&amp;traceProtocol=false&amp;useUnbufferedInput=false&amp;useReadAheadInput=false&amp;maintainTimeStats=false&amp;useServerPrepStmts&amp;cacheRSMetadata=true</url>
+    <user>benchmarkdbuser</user>
+    <password>benchmarkdbpass</password>
+    <useUnicode/>
+  </driver>
+</database>
+
+</web-app>

+ 15 - 0
scalatra/src/main/webapp/WEB-INF/web.xml

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+      xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+      version="3.0">
+
+  <!--
+    This listener loads a class in the default package called Scalatra.
+    That class should implement org.scalatra.LifeCycle.  Your app can be
+    configured in Scala code there.
+  -->
+  <listener>
+    <listener-class>org.scalatra.servlet.ScalatraListener</listener-class>
+  </listener>
+</web-app>