Browse Source

Updated all benchmarks to Play 2.3.6 and Scala 2.11.4.
Updated Ebean benchmarks to include missing tests as well as include HikariCP version.
Moved `/plaintext` tests from JPA/Ebean benchmarks to the `play2-java` benchmark as per https://github.com/TechEmpower/FrameworkBenchmarks/pull/1149#dis
Added BoneCP qualifier as per https://github.com/TechEmpower/FrameworkBenchmarks/pull/1149#discussion_r19584629 and changed names accordingly.

Donovan Muller 10 years ago
parent
commit
0a57b1d18c
100 changed files with 1798 additions and 197 deletions
  1. 1 0
      .travis.yml
  2. 6 9
      benchmark.cfg.example
  3. 23 1
      frameworks/C/lwan/benchmark_config
  4. 4 2
      frameworks/C/lwan/install.sh
  5. 14 1
      frameworks/C/lwan/setup.py
  6. 1 1
      frameworks/Dart/dart-start/server.dart
  7. 1 1
      frameworks/Dart/dart-stream/server.dart
  8. 1 2
      frameworks/Elixir/WeberFramework/benchmark_config
  9. 1 1
      frameworks/Erlang/cowboy/rebar.config
  10. 1 0
      frameworks/Erlang/cowboy/setup_erlang.py
  11. 1 2
      frameworks/Go/revel-jet/benchmark_config
  12. 57 10
      frameworks/Java/play2-java/benchmark_config
  13. 13 10
      frameworks/Java/play2-java/generate_config.py
  14. 0 0
      frameworks/Java/play2-java/play2-java-ebean-bonecp/.gitignore
  15. 35 0
      frameworks/Java/play2-java/play2-java-ebean-bonecp/README.md
  16. 70 12
      frameworks/Java/play2-java/play2-java-ebean-bonecp/app/controllers/Application.java
  17. 26 0
      frameworks/Java/play2-java/play2-java-ebean-bonecp/app/models/Fortune.java
  18. 30 0
      frameworks/Java/play2-java/play2-java-ebean-bonecp/app/models/World.java
  19. 21 0
      frameworks/Java/play2-java/play2-java-ebean-bonecp/app/utils/Headers.java
  20. 0 0
      frameworks/Java/play2-java/play2-java-ebean-bonecp/app/utils/Predicate.java
  21. 0 0
      frameworks/Java/play2-java/play2-java-ebean-bonecp/app/utils/Predicated.java
  22. 0 0
      frameworks/Java/play2-java/play2-java-ebean-bonecp/app/utils/PredicatedAction.java
  23. 16 0
      frameworks/Java/play2-java/play2-java-ebean-bonecp/app/views/fortunes.scala.html
  24. 12 0
      frameworks/Java/play2-java/play2-java-ebean-bonecp/app/views/main.scala.html
  25. 13 0
      frameworks/Java/play2-java/play2-java-ebean-bonecp/build.sbt
  26. 3 3
      frameworks/Java/play2-java/play2-java-ebean-bonecp/conf/application.conf
  27. 13 0
      frameworks/Java/play2-java/play2-java-ebean-bonecp/conf/routes
  28. 1 0
      frameworks/Java/play2-java/play2-java-ebean-bonecp/project/build.properties
  29. 1 1
      frameworks/Java/play2-java/play2-java-ebean-bonecp/project/plugins.sbt
  30. 11 0
      frameworks/Java/play2-java/play2-java-ebean-bonecp/source_code
  31. 0 0
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/.gitignore
  32. 35 0
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/README.md
  33. 155 0
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/controllers/Application.java
  34. 26 0
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/models/Fortune.java
  35. 30 0
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/models/World.java
  36. 21 0
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/utils/Headers.java
  37. 0 0
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/utils/Predicate.java
  38. 0 0
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/utils/Predicated.java
  39. 3 3
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/utils/PredicatedAction.java
  40. 16 0
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/views/fortunes.scala.html
  41. 12 0
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/views/main.scala.html
  42. 16 0
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/build.sbt
  43. 78 0
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/conf/application.conf
  44. 1 0
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/conf/play.plugins
  45. 13 0
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/conf/routes
  46. 1 0
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/project/build.properties
  47. 1 1
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/project/plugins.sbt
  48. 2 0
      frameworks/Java/play2-java/play2-java-ebean-hikaricp/source_code
  49. 0 20
      frameworks/Java/play2-java/play2-java-ebean/README.md
  50. 0 20
      frameworks/Java/play2-java/play2-java-ebean/app/models/World.java
  51. 0 11
      frameworks/Java/play2-java/play2-java-ebean/build.sbt
  52. 0 10
      frameworks/Java/play2-java/play2-java-ebean/conf/routes
  53. 0 1
      frameworks/Java/play2-java/play2-java-ebean/project/build.properties
  54. 0 9
      frameworks/Java/play2-java/play2-java-ebean/source_code
  55. 30 0
      frameworks/Java/play2-java/play2-java-jpa-bonecp/.gitignore
  56. 35 0
      frameworks/Java/play2-java/play2-java-jpa-bonecp/README.md
  57. 66 10
      frameworks/Java/play2-java/play2-java-jpa-bonecp/app/controllers/Application.java
  58. 34 0
      frameworks/Java/play2-java/play2-java-jpa-bonecp/app/models/Fortune.java
  59. 38 0
      frameworks/Java/play2-java/play2-java-jpa-bonecp/app/models/World.java
  60. 21 0
      frameworks/Java/play2-java/play2-java-jpa-bonecp/app/utils/Headers.java
  61. 8 0
      frameworks/Java/play2-java/play2-java-jpa-bonecp/app/utils/Predicate.java
  62. 26 0
      frameworks/Java/play2-java/play2-java-jpa-bonecp/app/utils/Predicated.java
  63. 23 0
      frameworks/Java/play2-java/play2-java-jpa-bonecp/app/utils/PredicatedAction.java
  64. 16 0
      frameworks/Java/play2-java/play2-java-jpa-bonecp/app/views/fortunes.scala.html
  65. 12 0
      frameworks/Java/play2-java/play2-java-jpa-bonecp/app/views/main.scala.html
  66. 2 2
      frameworks/Java/play2-java/play2-java-jpa-bonecp/build.sbt
  67. 0 0
      frameworks/Java/play2-java/play2-java-jpa-bonecp/conf/META-INF/persistence.xml
  68. 1 1
      frameworks/Java/play2-java/play2-java-jpa-bonecp/conf/application.conf
  69. 2 1
      frameworks/Java/play2-java/play2-java-jpa-bonecp/conf/routes
  70. 0 0
      frameworks/Java/play2-java/play2-java-jpa-bonecp/project/build.properties
  71. 8 0
      frameworks/Java/play2-java/play2-java-jpa-bonecp/project/plugins.sbt
  72. 11 0
      frameworks/Java/play2-java/play2-java-jpa-bonecp/source_code
  73. 29 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/.gitignore
  74. 36 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/README.md
  75. 165 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/controllers/Application.java
  76. 34 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/models/Fortune.java
  77. 38 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/models/World.java
  78. 21 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/utils/Headers.java
  79. 8 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/utils/Predicate.java
  80. 26 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/utils/Predicated.java
  81. 23 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/utils/PredicatedAction.java
  82. 16 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/views/fortunes.scala.html
  83. 12 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/views/main.scala.html
  84. 17 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/build.sbt
  85. 14 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/conf/META-INF/persistence.xml
  86. 95 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/conf/application.conf
  87. 1 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/conf/play.plugins
  88. 12 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/conf/routes
  89. 1 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/project/build.properties
  90. 4 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/project/plugins.sbt
  91. 11 0
      frameworks/Java/play2-java/play2-java-jpa-hikaricp/source_code
  92. 0 28
      frameworks/Java/play2-java/play2-java-jpa/README.md
  93. 34 0
      frameworks/Java/play2-java/play2-java-jpa/app/models/Fortune.java
  94. 0 23
      frameworks/Java/play2-java/play2-java-jpa/app/models/World.java
  95. 21 0
      frameworks/Java/play2-java/play2-java-jpa/app/utils/Headers.java
  96. 16 0
      frameworks/Java/play2-java/play2-java-jpa/app/views/fortunes.scala.html
  97. 12 0
      frameworks/Java/play2-java/play2-java-jpa/app/views/main.scala.html
  98. 5 1
      frameworks/Java/play2-java/play2-java/README.md
  99. 7 0
      frameworks/Java/play2-java/play2-java/app/controllers/Application.java
  100. 21 0
      frameworks/Java/play2-java/play2-java/app/utils/Headers.java

+ 1 - 0
.travis.yml

@@ -123,6 +123,7 @@ env:
     - "TESTDIR=PHP/php-yaf"
     - "TESTDIR=PHP/php-yii2"
     - "TESTDIR=PHP/php-zend-framework"
+    - "TESTDIR=PHP/php-zend-framework1"
     - "TESTDIR=PHP/phreeze"
     - "TESTDIR=Python/bottle"
     - "TESTDIR=Python/django"

+ 6 - 9
benchmark.cfg.example

@@ -12,21 +12,18 @@ exclude=None
 install=server
 install_error_action=continue
 install_strategy=unified
-install_only=False
-list_test_metadata=False
-list_tests=False
-max_concurrency=256
-max_queries=20
-max_threads=8
+#install_only=True
+#list_test_metadata=True
+#list_tests=True
+concurrency_levels=[8, 16, 32, 64, 128, 256]
+query_levels=[1, 5,10,15,20]
+threads=8
 mode=benchmark
 name=ec2
 os=linux
-parse=None
 password_prompt=False
-query_interval=5
 server_host=localhost
 sleep=60
-starting_concurrency=8
 test=None
 type=all
 verbose=True

+ 23 - 1
frameworks/C/lwan/benchmark_config

@@ -1,7 +1,7 @@
 {
   "framework": "lwan",
   "tests": [{
-    "raw": {
+    "sqlite": {
       "setup_file": "setup",
       "db_url": "/db",
       "query_url": "/queries?queries=",
@@ -22,6 +22,28 @@
       "display_name": "Lwan",
       "notes": "",
       "versus": ""
+    },
+    "mysql": {
+      "setup_file": "setup",
+      "db_url": "/db",
+      "query_url": "/queries?queries=",
+      "fortune_url": "/fortunes",
+      "plaintext_url": "/plaintext",
+      "json_url": "/json",
+      "port": 8080,
+      "approach": "Realistic",
+      "classification": "Platform",
+      "database": "MySQL",
+      "framework": "lwan",
+      "language": "C",
+      "orm": "Raw",
+      "platform": "Lwan",
+      "webserver": "Lwan",
+      "os": "Linux",
+      "database_os": "Linux",
+      "display_name": "Lwan",
+      "notes": "",
+      "versus": ""
     }
   }]
 }

+ 4 - 2
frameworks/C/lwan/install.sh

@@ -1,14 +1,16 @@
 #!/bin/bash
 
-REV='aa6c15fbdf63d9db722ddc72bd736b23381331be'
+REV='eb96604657dd940ecb70b56fef4279077e3f9c21'
 
 INSTALLED_FILE="${IROOT}/lwan-${REV}.installed"
 RETCODE=$(fw_exists ${INSTALLED_FILE})
 [ ! "$RETCODE" == 0 ] || { return 0; }
 
+[ ! -e ${INSTALLED_FILE} -a -d ${IROOT}/lwan ] && rm -rf ${IROOT}/lwan
+
 # Lwan is only built during installation as a dependency sanity check.
 sudo apt-get update && \
-	sudo apt-get install libjemalloc-dev && \
+	sudo apt-get install -qqy libjemalloc-dev libmysqlclient-dev libsqlite3-dev && \
 	git clone git://github.com/lpereira/lwan.git && \
         cd lwan && \
         git checkout ${REV} && \

+ 14 - 1
frameworks/C/lwan/setup.py

@@ -2,6 +2,17 @@ import subprocess
 import sys
 import os
 
+def get_env_for_database(args):
+  if args.database == 'MySQL':
+    return {
+      'USE_MYSQL': '1',
+      'MYSQL_USER': 'benchmarkdbuser',
+      'MYSQL_PASS': 'benchmarkdbpass',
+      'MYSQL_HOST': args.database_host,
+      'MYSQL_DB': 'hello_world'
+    }
+  return None
+
 def start(args, logfile, errfile):
   subprocess.call('rm -rf ${LWAN_BUILD}', shell=True, stderr=errfile, stdout=logfile)
   subprocess.call('mkdir -p ${LWAN_BUILD}', shell=True, stderr=errfile, stdout=logfile)
@@ -10,7 +21,9 @@ def start(args, logfile, errfile):
 
   db_dir = os.path.join(os.environ['LWAN_ROOT'], 'techempower')
   exe_path = os.path.join(os.environ['LWAN_BUILD'], 'techempower', 'techempower')
-  subprocess.Popen(exe_path, cwd=db_dir, stderr=errfile, stdout=logfile, shell=True)
+
+  subprocess.Popen(exe_path, cwd=db_dir, stderr=errfile, stdout=logfile, shell=True,
+      env = get_env_for_database(args))
 
   return 0
 

+ 1 - 1
frameworks/Dart/dart-start/server.dart

@@ -288,7 +288,7 @@ main(List<String> args) {
           
           _fortuneCollection.find().toList().then((fortunes) {
             fortunes = fortunes.map((fortune) {
-              return new Fortune(fortune["_id"], fortune["message"]);
+              return new Fortune(fortune["_id"].toInt(), fortune["message"]);
             }).toList();
             fortunes.add(new Fortune(0, 'Additional fortune added at request time.'));
             fortunes.sort();

+ 1 - 1
frameworks/Dart/dart-stream/server.dart

@@ -243,7 +243,7 @@ _fortunesMongoTest(HttpConnect connect) {
   
   return _fortuneCollection.find().toList().then((fortunes) {
     fortunes = fortunes.map((fortune) {
-      return new Fortune(fortune["_id"], fortune["message"]);
+      return new Fortune(fortune["_id"].toInt(), fortune["message"]);
     }).toList();
     fortunes.add(new Fortune(0, 'Additional fortune added at request time.'));
     fortunes.sort();

+ 1 - 2
frameworks/Elixir/WeberFramework/benchmark_config

@@ -18,7 +18,6 @@
       "database_os": "Linux",
       "display_name": "weber",
       "notes": "",
-      "versus": "",
-      "skip": "false"
+      "versus": ""
   }}]
 }

+ 1 - 1
frameworks/Erlang/cowboy/rebar.config

@@ -1,6 +1,6 @@
 {deps,
  [
-  {jiffy, ".*", {git, "https://github.com/davisp/jiffy.git", {tag, "0.8.3"}}},
+  {jiffy, ".*", {git, "https://github.com/davisp/jiffy.git", {tag, "0.8.5"}}},
   {mimetypes, ".*", {git, "http://github.com/spawngrid/mimetypes.git", {branch, master}}},
   {emysql, ".*", {git, "https://github.com/Eonblast/Emysql.git"}},
   {cowboy, ".*", {git, "https://github.com/extend/cowboy.git", {tag, "0.8.3"}}}

+ 1 - 0
frameworks/Erlang/cowboy/setup_erlang.py

@@ -6,6 +6,7 @@ def start(args, logfile, errfile):
   setup_util.replace_text("cowboy/src/hello_world_app.erl", "\"benchmarkdbpass\", \".*\", 3306", "\"benchmarkdbpass\", \"" + args.database_host + "\", 3306")
 
   try:
+    subprocess.check_call("rm -rf deps/*", shell=True, cwd="cowboy", stderr=errfile, stdout=logfile)
     subprocess.check_call("./rebar get-deps", shell=True, cwd="cowboy", stderr=errfile, stdout=logfile)
     subprocess.check_call("./rebar compile", shell=True, cwd="cowboy", stderr=errfile, stdout=logfile)
     subprocess.check_call("erl -pa ebin deps/*/ebin +sbwt very_long +swt very_low -s hello_world -noshell -detached", shell=True, cwd="cowboy", stderr=errfile, stdout=logfile)

+ 1 - 2
frameworks/Go/revel-jet/benchmark_config

@@ -20,8 +20,7 @@
       "database_os": "Linux",
       "display_name": "revel-jet",
       "notes": "",
-      "versus": "go",
-      "skip": "true"
+      "versus": "go"
     }
   }]
 }

+ 57 - 10
frameworks/Java/play2-java/benchmark_config

@@ -18,11 +18,12 @@
         "notes": "", 
         "versus": "netty", 
         "port": "9000", 
-        "json_url": "/json"
+        "json_url": "/json", 
+        "plaintext_url": "/plaintext"
       }, 
-      "java-ebean": {
-        "display_name": "play2-java-ebean", 
-        "setup_file": "setup_java_ebean", 
+      "java-ebean-bonecp": {
+        "display_name": "play2-java-ebean-bonecp", 
+        "setup_file": "setup_java_ebean_bonecp", 
         "framework": "play2", 
         "language": "Java", 
         "orm": "Full", 
@@ -37,11 +38,13 @@
         "versus": "netty", 
         "port": "9000", 
         "db_url": "/db", 
-        "query_url": "/queries?queries="
+        "query_url": "/queries?queries=", 
+        "fortune_url": "/fortunes", 
+        "update_url": "/update?queries="
       }, 
-      "java-jpa": {
-        "display_name": "play2-java-jpa", 
-        "setup_file": "setup_java_jpa", 
+      "java-ebean-hikaricp": {
+        "display_name": "play2-java-ebean-hikaricp", 
+        "setup_file": "setup_java_ebean_hikaricp", 
         "framework": "play2", 
         "language": "Java", 
         "orm": "Full", 
@@ -56,8 +59,52 @@
         "versus": "netty", 
         "port": "9000", 
         "db_url": "/db", 
-        "query_url": "/queries?queries="
+        "query_url": "/queries?queries=", 
+        "fortune_url": "/fortunes", 
+        "update_url": "/update?queries="
+      }, 
+      "java-jpa-bonecp": {
+        "display_name": "play2-java-jpa-bonecp", 
+        "setup_file": "setup_java_jpa_bonecp", 
+        "framework": "play2", 
+        "language": "Java", 
+        "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="
+      }, 
+      "java-jpa-hikaricp": {
+        "display_name": "play2-java-jpa-hikaricp", 
+        "setup_file": "setup_java_jpa_hikaricp", 
+        "framework": "play2", 
+        "language": "Java", 
+        "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="
       }
     }
   ]
-}
+}

+ 13 - 10
frameworks/Java/play2-java/generate_config.py

@@ -9,12 +9,14 @@ import collections, json, os, textwrap
 # Format is: (language, orm, (opsys, ...), (test, ...))
 # See the dir_name logic below to see the directory name for each test application.
 configurations = [
-  ('Java',  None,    ['Linux'],            ['json']),
-  ('Java',  'Ebean', ['Linux'],            ['db', 'query']),
-  ('Java',  'JPA',   ['Linux'],            ['db', 'query']),
-  ('Scala', None,    ['Linux'],            ['json']),
-  ('Scala', 'Anorm', ['Linux', 'Windows'], ['db', 'query', 'fortune', 'update']),
-  ('Scala', 'Slick', ['Linux'],            ['db', 'query', 'fortune', 'update']),
+  ('Java',  None,    			['Linux'],            ['json', 'plaintext']),
+  ('Java',  'Ebean BoneCP', 	['Linux'],            ['db', 'query', 'fortune', 'update']),
+  ('Java',  'Ebean HikariCP', 	['Linux'],            ['db', 'query', 'fortune', 'update']),
+  ('Java',  'JPA BoneCP',		['Linux'],            ['db', 'query', 'fortune', 'update']),
+  ('Java',  'JPA HikariCP',		['Linux'],            ['db', 'query', 'fortune', 'update']),
+  ('Scala', None,    			['Linux'],            ['json']),
+  ('Scala', 'Anorm', 			['Linux', 'Windows'], ['db', 'query', 'fortune', 'update']),
+  ('Scala', 'Slick', 			['Linux'],            ['db', 'query', 'fortune', 'update']),
 ]
 
 # All play2 test applications must use the same URLs.
@@ -24,6 +26,7 @@ test_urls = {
   'query': '/queries?queries=',
   'fortune': '/fortunes',
   'update': '/update?queries=',
+  'plaintext': '/plaintext',
 }
 
 langs = {
@@ -41,8 +44,8 @@ for lang, _ in langs.iteritems():
   lang_test_configs[lang] = collections.OrderedDict()
 
 for lang, orm, opsyses, tests in configurations:
-  dir_name = 'play2-' + lang.lower() + (('-'+orm.lower()) if orm else '')
-  setup_name = 'setup_' + lang.lower() + (('_'+orm.lower()) if orm else '')
+  dir_name = 'play2-' + lang.lower() + (('-'+orm.replace(' ', '-').lower()) if orm else '')
+  setup_name = 'setup_' + lang.lower() + (('_'+orm.replace(' ', '_').lower()) if orm else '')
 
   setup_path = os.path.join(pathForLang(lang), setup_name+'.py')
   print 'Generating', setup_path
@@ -57,9 +60,9 @@ for lang, orm, opsyses, tests in configurations:
 
   for opsys in opsyses:
     if len(opsyses) == 1:
-      test_name = lang.lower() + (('-'+orm.lower()) if orm else '')
+      test_name = lang.lower() + (('-'+orm.replace(' ', '-').lower()) if orm else '')
     else:
-      test_name = lang.lower() + (('-'+orm.lower()) if orm else '') + '-'+opsys.lower()
+      test_name = lang.lower() + (('-'+orm.replace(' ', '-').lower()) if orm else '') + '-'+opsys.lower()
     test_config_json = collections.OrderedDict([
       ('display_name', 'play2-'+test_name),
       ('setup_file', setup_name),

+ 0 - 0
frameworks/Java/play2-java/play2-java-ebean/.gitignore → frameworks/Java/play2-java/play2-java-ebean-bonecp/.gitignore


+ 35 - 0
frameworks/Java/play2-java/play2-java-ebean-bonecp/README.md

@@ -0,0 +1,35 @@
+#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 controller](app/controllers/Application.java)
+
+### Data-Store/Database Mapping Test
+
+* [Database test controller](app/controllers/Application.java)
+* [Database World test model](app/models/World.java)
+* [Database Fortune test model](app/models/Fortune.java)
+
+### Plain Text Test
+
+* [Plain text test controller](app/controllers/Application.java)
+
+## Infrastructure Software Versions
+The tests were run with:
+
+* [Java OpenJDK 1.7](http://openjdk.java.net/)
+* [Play 2.3.6](http://http://www.playframework.com/)
+
+## Test URLs
+### JSON Encoding Test
+
+* http://localhost/json
+
+### Data-Store/Database Mapping Test
+
+* http://localhost/db
+* http://localhost/queries?queries=10
+* http://localhost/fortunes
+* http://localhost/update?queries=10

+ 70 - 12
frameworks/Java/play2-java/play2-java-ebean/app/controllers/Application.java → frameworks/Java/play2-java/play2-java-ebean-bonecp/app/controllers/Application.java

@@ -1,26 +1,29 @@
 package controllers;
 
 import akka.dispatch.ExecutionContexts;
+import models.Fortune;
 import models.World;
 import play.Play;
 import play.core.NamedThreadFactory;
 import play.libs.F;
 import play.libs.Json;
-
 import play.mvc.Controller;
 import play.mvc.Result;
+import play.mvc.With;
 import scala.concurrent.ExecutionContext;
+import utils.Headers;
 import utils.Predicate;
 import utils.Predicated;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
-import java.util.concurrent.*;
+import java.util.*;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
 
+@With(Headers.class)
 public class Application extends Controller {
 
-    private static final int MAX_QUERIES_PER_REQUEST = 20;
     private static final int TEST_DATABASE_ROWS = 10000;
 
     private static final int partitionCount = Play.application().configuration().getInt("db.default.partitionCount");
@@ -58,6 +61,48 @@ public class Application extends Controller {
 
     @Predicated(predicate = IsDbAvailable.class, failed = SERVICE_UNAVAILABLE)
     public static F.Promise<Result> queries(final String queryCountString) {
+        return getRandomWorlds(queryCount(queryCountString)).map(new F.Function<List<World>, Result>() {
+
+            @Override
+            public Result apply(List<World> worlds) {
+                return ok(Json.toJson(worlds));
+            }
+        });
+    }
+
+    @Predicated(predicate = IsDbAvailable.class, failed = SERVICE_UNAVAILABLE)
+    public static F.Promise<Result> fortunes() {
+        return F.Promise.promise(new F.Function0<Result>() {
+
+            @Override
+            public Result apply() throws Throwable {
+                List<Fortune> fortunes = Fortune.find.all();
+                fortunes.add(new Fortune("Additional fortune added at request time."));
+                Collections.sort(fortunes, new Comparator<Fortune>() {
+
+                    @Override
+                    public int compare(Fortune f1, Fortune f2) {
+                        return f1.message.compareTo(f2.message);
+                    }
+                });
+
+                return ok(views.html.fortunes.render(fortunes));
+            }
+        }, dbEc);
+    }
+
+    @Predicated(predicate = IsDbAvailable.class, failed = SERVICE_UNAVAILABLE)
+    public static F.Promise<Result> update(final String queryCountString) {
+        return getRandomWorlds(queryCount(queryCountString)).flatMap(new F.Function<List<World>, F.Promise<Result>>() {
+
+            @Override
+            public F.Promise<Result> apply(List<World> worlds) throws Throwable {
+                return updateWorlds(worlds);
+            }
+        });
+    }
+
+    private static int queryCount(String queryCountString) {
         int queryCount;
         try {
             queryCount = Integer.parseInt(queryCountString, 10);
@@ -70,16 +115,12 @@ public class Application extends Controller {
             queryCount = 500;
         }
 
-        return getRandomWorlds(queryCount).map(new F.Function<List<World>, Result>() {
-            @Override
-            public Result apply(List<World> worlds) {
-                return ok(Json.toJson(worlds));
-            }
-        });
+        return queryCount;
     }
 
     private static F.Promise<List<World>> getRandomWorlds(final int n) {
         return F.Promise.promise(new F.Function0<List<World>>() {
+
             @Override
             public List<World> apply() {
                 Random random = ThreadLocalRandom.current();
@@ -94,4 +135,21 @@ public class Application extends Controller {
         }, dbEc);
     }
 
+    private static F.Promise<Result> updateWorlds(final List<World> worlds) {
+        return F.Promise.promise(new F.Function0<Result>() {
+
+            @Override
+            public Result apply() throws Throwable {
+                Random random = ThreadLocalRandom.current();
+                for (World world : worlds) {
+                    world.randomNumber = (long) (random.nextInt(10000) + 1);
+                }
+
+                List<World> updatedWorlds = World.save(worlds);
+                return ok(Json.toJson(updatedWorlds));
+            }
+        }, dbEc);
+    }
+
+
 }

+ 26 - 0
frameworks/Java/play2-java/play2-java-ebean-bonecp/app/models/Fortune.java

@@ -0,0 +1,26 @@
+package models;
+
+import play.db.ebean.Model;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public class Fortune extends Model {
+
+    @Id
+    public Long id = 0L;
+
+    public String message;
+
+    public Fortune() {
+    }
+
+    public Fortune(String message) {
+        this.message = message;
+    }
+
+    public static Finder<Long, Fortune> find = new Finder<Long, Fortune>(
+            Long.class, Fortune.class
+    );
+}

+ 30 - 0
frameworks/Java/play2-java/play2-java-ebean-bonecp/app/models/World.java

@@ -0,0 +1,30 @@
+package models;
+
+import com.avaje.ebean.Ebean;
+import play.db.ebean.Model;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import java.util.List;
+
+@Entity
+public class World extends Model {
+
+    @Id
+    public Long id;
+
+    @Column(name = "randomNumber")
+    public Long randomNumber;
+
+    public static Finder<Long, World> find = new Finder<Long, World>(
+            Long.class, World.class
+    );
+
+    public static List<World> save(final List<World> worlds) throws Throwable {
+        Ebean.save(worlds);
+
+        return worlds;
+    }
+
+}

+ 21 - 0
frameworks/Java/play2-java/play2-java-ebean-bonecp/app/utils/Headers.java

@@ -0,0 +1,21 @@
+package utils;
+
+import org.joda.time.DateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+import play.libs.F;
+import play.mvc.Action;
+import play.mvc.Http;
+import play.mvc.Result;
+
+public class Headers extends Action.Simple {
+
+    private static final DateTimeFormatter RFC_1123_DATE_TIME = DateTimeFormat.forPattern("EEE, dd MMM yyyy HH:mm:ss 'GMT'").withZoneUTC();
+
+    @Override
+    public F.Promise<Result> call(Http.Context context) throws Throwable {
+        context.response().setHeader("Server", "play2-java-jpa");
+        context.response().setHeader("Date", RFC_1123_DATE_TIME.print(new DateTime()));
+        return delegate.call(context);
+    }
+}

+ 0 - 0
frameworks/Java/play2-java/play2-java-ebean/app/utils/Predicate.java → frameworks/Java/play2-java/play2-java-ebean-bonecp/app/utils/Predicate.java


+ 0 - 0
frameworks/Java/play2-java/play2-java-ebean/app/utils/Predicated.java → frameworks/Java/play2-java/play2-java-ebean-bonecp/app/utils/Predicated.java


+ 0 - 0
frameworks/Java/play2-java/play2-java-jpa/app/utils/PredicatedAction.java → frameworks/Java/play2-java/play2-java-ebean-bonecp/app/utils/PredicatedAction.java


+ 16 - 0
frameworks/Java/play2-java/play2-java-ebean-bonecp/app/views/fortunes.scala.html

@@ -0,0 +1,16 @@
+@(fortunes: List[Fortune])
+
+@main() {
+    <table>
+        <tr>
+            <th>id</th>
+            <th>message</th>
+        </tr>
+        @for(fortune <- fortunes) {
+        <tr>
+            <td>@fortune.id</td>
+            <td>@fortune.message</td>
+        </tr>
+        }
+    </table>
+}

+ 12 - 0
frameworks/Java/play2-java/play2-java-ebean-bonecp/app/views/main.scala.html

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

+ 13 - 0
frameworks/Java/play2-java/play2-java-ebean-bonecp/build.sbt

@@ -0,0 +1,13 @@
+name := "play2-java-ebean-bonecp"
+
+version := "1.0-SNAPSHOT"
+
+lazy val root = (project in file(".")).enablePlugins(PlayJava)
+
+scalaVersion := "2.11.4"
+
+libraryDependencies ++= Seq(
+  javaJdbc,
+  javaEbean,
+  "mysql" % "mysql-connector-java" % "5.1.33"
+)

+ 3 - 3
frameworks/Java/play2-java/play2-java-ebean/conf/application.conf → frameworks/Java/play2-java/play2-java-ebean-bonecp/conf/application.conf

@@ -30,7 +30,7 @@ application.langs="en"
 # 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.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.default.user=benchmarkdbuser
 db.default.password=benchmarkdbpass
 db.default.jndiName=DefaultDS
@@ -64,10 +64,10 @@ ebean.default="models.*"
 # 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.root=INFO
 
 # Logger used by the framework:
-logger.play=ERROR
+logger.play=INFO
 
 # Logger provided to your application:
 logger.application=ERROR

+ 13 - 0
frameworks/Java/play2-java/play2-java-ebean-bonecp/conf/routes

@@ -0,0 +1,13 @@
+# Routes
+# This file defines all application routes (Higher priority routes first)
+# ~~~~
+
+# 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")
+
+
+# Map static resources from the /public folder to the /assets URL path
+GET        /assets/*file        controllers.Assets.at(path="/public", file)

+ 1 - 0
frameworks/Java/play2-java/play2-java-ebean-bonecp/project/build.properties

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

+ 1 - 1
frameworks/Java/play2-java/play2-java-ebean/project/plugins.sbt → frameworks/Java/play2-java/play2-java-ebean-bonecp/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.2.0")
+addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.6")

+ 11 - 0
frameworks/Java/play2-java/play2-java-ebean-bonecp/source_code

@@ -0,0 +1,11 @@
+./play-java-jpa/app/
+./play-java-jpa/app/controllers
+./play-java-jpa/app/controllers/Application.java
+./play-java-jpa/app/utils
+./play-java-jpa/app/utils/Headers.java
+./play-java-jpa/app/utils/Predicate.java
+./play-java-jpa/app/utils/PredicatedAction.java
+./play-java-jpa/app/utils/Predicated.java
+./play-java-jpa/app/models
+./play-java-jpa/app/models/World.java
+./play-java-jpa/app/models/Fortune.java

+ 0 - 0
frameworks/Java/play2-java/play2-java-jpa/.gitignore → frameworks/Java/play2-java/play2-java-ebean-hikaricp/.gitignore


+ 35 - 0
frameworks/Java/play2-java/play2-java-ebean-hikaricp/README.md

@@ -0,0 +1,35 @@
+#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 controller](app/controllers/Application.java)
+
+### Data-Store/Database Mapping Test
+
+* [Database test controller](app/controllers/Application.java)
+* [Database World test model](app/models/World.java)
+* [Database Fortune test model](app/models/Fortune.java)
+
+### Plain Text Test
+
+* [Plain text test controller](app/controllers/Application.java)
+
+## Infrastructure Software Versions
+The tests were run with:
+
+* [Java OpenJDK 1.7](http://openjdk.java.net/)
+* [Play 2.3.6](http://http://www.playframework.com/)
+
+## Test URLs
+### JSON Encoding Test
+
+* http://localhost/json
+
+### Data-Store/Database Mapping Test
+
+* http://localhost/db
+* http://localhost/queries?queries=10
+* http://localhost/fortunes
+* http://localhost/update?queries=10

+ 155 - 0
frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/controllers/Application.java

@@ -0,0 +1,155 @@
+package controllers;
+
+import akka.dispatch.ExecutionContexts;
+import models.Fortune;
+import models.World;
+import play.Play;
+import play.core.NamedThreadFactory;
+import play.libs.F;
+import play.libs.Json;
+import play.mvc.Controller;
+import play.mvc.Result;
+import play.mvc.With;
+import scala.concurrent.ExecutionContext;
+import utils.Headers;
+import utils.Predicate;
+import utils.Predicated;
+
+import java.util.*;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+@With(Headers.class)
+public class Application extends Controller {
+
+    private static final int TEST_DATABASE_ROWS = 10000;
+
+    private static final int partitionCount = Play.application().configuration().getInt("db.default.partitionCount");
+    private static final int maxConnections =
+            partitionCount * Play.application().configuration().getInt("db.default.maxConnectionsPerPartition");
+    private static final int minConnections =
+            partitionCount * Play.application().configuration().getInt("db.default.minConnectionsPerPartition");
+
+    private static final ThreadPoolExecutor tpe = new ThreadPoolExecutor(minConnections, maxConnections,
+            0L, TimeUnit.MILLISECONDS,
+            new LinkedBlockingQueue<Runnable>(),
+            new NamedThreadFactory("dbEc"));
+    private static final ExecutionContext dbEc = ExecutionContexts.fromExecutorService(tpe);
+
+    // If the thread-pool used by the database grows too large then our server
+    // is probably struggling, and we should start dropping requests. Set
+    // the max size of our queue something above the number of concurrent
+    // connections that we need to handle.
+    public static class IsDbAvailable implements Predicate {
+        @Override
+        public boolean condition() {
+            return tpe.getQueue().size() <= 1024;
+        }
+    }
+
+    @Predicated(predicate = IsDbAvailable.class, failed = SERVICE_UNAVAILABLE)
+    public static F.Promise<Result> db() {
+        return getRandomWorlds(1).map(new F.Function<List<World>, Result>() {
+            @Override
+            public Result apply(List<World> worlds) {
+                return ok(Json.toJson(worlds.get(0)));
+            }
+        });
+    }
+
+    @Predicated(predicate = IsDbAvailable.class, failed = SERVICE_UNAVAILABLE)
+    public static F.Promise<Result> queries(final String queryCountString) {
+        return getRandomWorlds(queryCount(queryCountString)).map(new F.Function<List<World>, Result>() {
+
+            @Override
+            public Result apply(List<World> worlds) {
+                return ok(Json.toJson(worlds));
+            }
+        });
+    }
+
+    @Predicated(predicate = IsDbAvailable.class, failed = SERVICE_UNAVAILABLE)
+    public static F.Promise<Result> fortunes() {
+        return F.Promise.promise(new F.Function0<Result>() {
+
+            @Override
+            public Result apply() throws Throwable {
+                List<Fortune> fortunes = Fortune.find.all();
+                fortunes.add(new Fortune("Additional fortune added at request time."));
+                Collections.sort(fortunes, new Comparator<Fortune>() {
+
+                    @Override
+                    public int compare(Fortune f1, Fortune f2) {
+                        return f1.message.compareTo(f2.message);
+                    }
+                });
+
+                return ok(views.html.fortunes.render(fortunes));
+            }
+        }, dbEc);
+    }
+
+    @Predicated(predicate = IsDbAvailable.class, failed = SERVICE_UNAVAILABLE)
+    public static F.Promise<Result> update(final String queryCountString) {
+        return getRandomWorlds(queryCount(queryCountString)).flatMap(new F.Function<List<World>, F.Promise<Result>>() {
+
+            @Override
+            public F.Promise<Result> apply(List<World> worlds) throws Throwable {
+                return updateWorlds(worlds);
+            }
+        });
+    }
+
+    private static int queryCount(String queryCountString) {
+        int queryCount;
+        try {
+            queryCount = Integer.parseInt(queryCountString, 10);
+        } catch (NumberFormatException e) {
+            queryCount = 1;
+        }
+        if (queryCount < 1) {
+            queryCount = 1;
+        } else if (queryCount > 500) {
+            queryCount = 500;
+        }
+
+        return queryCount;
+    }
+
+    private static F.Promise<List<World>> getRandomWorlds(final int n) {
+        return F.Promise.promise(new F.Function0<List<World>>() {
+
+            @Override
+            public List<World> apply() {
+                Random random = ThreadLocalRandom.current();
+                List<World> worlds = new ArrayList<World>(n);
+                for (int i = 0; i < n; ++i) {
+                    long randomId = random.nextInt(TEST_DATABASE_ROWS) + 1;
+                    World world = World.find.byId(randomId);
+                    worlds.add(world);
+                }
+                return worlds;
+            }
+        }, dbEc);
+    }
+
+    private static F.Promise<Result> updateWorlds(final List<World> worlds) {
+        return F.Promise.promise(new F.Function0<Result>() {
+
+            @Override
+            public Result apply() throws Throwable {
+                Random random = ThreadLocalRandom.current();
+                for (World world : worlds) {
+                    world.randomNumber = (long) (random.nextInt(10000) + 1);
+                }
+
+                List<World> updatedWorlds = World.save(worlds);
+                return ok(Json.toJson(updatedWorlds));
+            }
+        }, dbEc);
+    }
+
+
+}

+ 26 - 0
frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/models/Fortune.java

@@ -0,0 +1,26 @@
+package models;
+
+import play.db.ebean.Model;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+
+@Entity
+public class Fortune extends Model {
+
+    @Id
+    public Long id = 0L;
+
+    public String message;
+
+    public Fortune() {
+    }
+
+    public Fortune(String message) {
+        this.message = message;
+    }
+
+    public static Finder<Long, Fortune> find = new Finder<Long, Fortune>(
+            Long.class, Fortune.class
+    );
+}

+ 30 - 0
frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/models/World.java

@@ -0,0 +1,30 @@
+package models;
+
+import com.avaje.ebean.Ebean;
+import play.db.ebean.Model;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import java.util.List;
+
+@Entity
+public class World extends Model {
+
+    @Id
+    public Long id;
+
+    @Column(name = "randomNumber")
+    public Long randomNumber;
+
+    public static Finder<Long, World> find = new Finder<Long, World>(
+            Long.class, World.class
+    );
+
+    public static List<World> save(final List<World> worlds) throws Throwable {
+        Ebean.save(worlds);
+
+        return worlds;
+    }
+
+}

+ 21 - 0
frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/utils/Headers.java

@@ -0,0 +1,21 @@
+package utils;
+
+import org.joda.time.DateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+import play.libs.F;
+import play.mvc.Action;
+import play.mvc.Http;
+import play.mvc.Result;
+
+public class Headers extends Action.Simple {
+
+    private static final DateTimeFormatter RFC_1123_DATE_TIME = DateTimeFormat.forPattern("EEE, dd MMM yyyy HH:mm:ss 'GMT'").withZoneUTC();
+
+    @Override
+    public F.Promise<Result> call(Http.Context context) throws Throwable {
+        context.response().setHeader("Server", "play2-java-jpa");
+        context.response().setHeader("Date", RFC_1123_DATE_TIME.print(new DateTime()));
+        return delegate.call(context);
+    }
+}

+ 0 - 0
frameworks/Java/play2-java/play2-java-jpa/app/utils/Predicate.java → frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/utils/Predicate.java


+ 0 - 0
frameworks/Java/play2-java/play2-java-jpa/app/utils/Predicated.java → frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/utils/Predicated.java


+ 3 - 3
frameworks/Java/play2-java/play2-java-ebean/app/utils/PredicatedAction.java → frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/utils/PredicatedAction.java

@@ -8,16 +8,16 @@ package utils;
 import play.libs.F;
 import play.mvc.Action;
 import play.mvc.Http;
-import play.mvc.SimpleResult;
+import play.mvc.Result;
 
 public class PredicatedAction extends Action<Predicated> {
     @Override
-    public F.Promise<SimpleResult> call(final Http.Context ctx) throws Throwable {
+    public F.Promise<Result> call(final Http.Context ctx) throws Throwable {
         final Predicate p = configuration.predicate().newInstance();
         if (p.condition()) {
             return delegate.call(ctx);
         } else {
-            return F.Promise.<SimpleResult>pure(status(configuration.failed()));
+            return F.Promise.<Result>pure(status(configuration.failed()));
         }
     }
 }

+ 16 - 0
frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/views/fortunes.scala.html

@@ -0,0 +1,16 @@
+@(fortunes: List[Fortune])
+
+@main() {
+    <table>
+        <tr>
+            <th>id</th>
+            <th>message</th>
+        </tr>
+        @for(fortune <- fortunes) {
+        <tr>
+            <td>@fortune.id</td>
+            <td>@fortune.message</td>
+        </tr>
+        }
+    </table>
+}

+ 12 - 0
frameworks/Java/play2-java/play2-java-ebean-hikaricp/app/views/main.scala.html

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

+ 16 - 0
frameworks/Java/play2-java/play2-java-ebean-hikaricp/build.sbt

@@ -0,0 +1,16 @@
+name := "play2-java-ebean-hikaricp"
+
+version := "1.0-SNAPSHOT"
+
+lazy val root = (project in file(".")).enablePlugins(PlayJava)
+
+scalaVersion := "2.11.4"
+
+libraryDependencies ++= Seq(
+  javaJdbc,
+  javaEbean,
+  "mysql" % "mysql-connector-java" % "5.1.33",
+  "com.edulify" %% "play-hikaricp" % "1.5.0"
+)
+
+resolvers += Resolver.url("Edulify Repository", url("http://edulify.github.io/modules/releases/"))(Resolver.ivyStylePatterns)

+ 78 - 0
frameworks/Java/play2-java/play2-java-ebean-hikaricp/conf/application.conf

@@ -0,0 +1,78 @@
+# 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://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.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.
+
+# This value maps to the maximumPoolSize for HickariCP (db.default.partitionCount * db.default.maxConnectionsPerPartition)
+db.default.maxConnectionsPerPartition=64
+
+# The number of initial connections, per partition.
+#
+# This maps to the minimumIdle connections for HikariCP (db.default.partitionCount * db.default.minConnectionsPerPartition)
+db.default.minConnectionsPerPartition=64
+
+dbplugin=disabled
+
+# Evolutions
+# ~~~~~
+# You can disable evolutions if needed
+evolutionplugin=disabled
+
+# Ebean configuration
+# ~~~~~
+# You can declare as many Ebean servers as you want.
+# By convention, the default server is named `default`
+#
+ebean.default="models.*"
+
+# Logger
+# ~~~~~
+# You can also configure logback (http://logback.qos.ch/), by providing a logger.xml file in the conf directory .
+
+# Root logger:
+logger.root=INFO
+
+# Logger used by the framework:
+logger.play=ERROR
+
+# Logger provided to your application:
+logger.application=ERROR
+

+ 1 - 0
frameworks/Java/play2-java/play2-java-ebean-hikaricp/conf/play.plugins

@@ -0,0 +1 @@
+200:com.edulify.play.hikaricp.HikariCPPlugin

+ 13 - 0
frameworks/Java/play2-java/play2-java-ebean-hikaricp/conf/routes

@@ -0,0 +1,13 @@
+# Routes
+# This file defines all application routes (Higher priority routes first)
+# ~~~~
+
+# 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")
+
+
+# Map static resources from the /public folder to the /assets URL path
+GET        /assets/*file        controllers.Assets.at(path="/public", file)

+ 1 - 0
frameworks/Java/play2-java/play2-java-ebean-hikaricp/project/build.properties

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

+ 1 - 1
frameworks/Java/play2-java/play2-java-jpa/project/plugins.sbt → frameworks/Java/play2-java/play2-java-ebean-hikaricp/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.4")
+addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.6")

+ 2 - 0
frameworks/Java/play2-java/play2-java-jpa/source_code → frameworks/Java/play2-java/play2-java-ebean-hikaricp/source_code

@@ -2,8 +2,10 @@
 ./play-java-jpa/app/controllers
 ./play-java-jpa/app/controllers/Application.java
 ./play-java-jpa/app/utils
+./play-java-jpa/app/utils/Headers.java
 ./play-java-jpa/app/utils/Predicate.java
 ./play-java-jpa/app/utils/PredicatedAction.java
 ./play-java-jpa/app/utils/Predicated.java
 ./play-java-jpa/app/models
 ./play-java-jpa/app/models/World.java
+./play-java-jpa/app/models/Fortune.java

+ 0 - 20
frameworks/Java/play2-java/play2-java-ebean/README.md

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

+ 0 - 20
frameworks/Java/play2-java/play2-java-ebean/app/models/World.java

@@ -1,20 +0,0 @@
-package models;
-
-import javax.persistence.*;
-
-import play.db.ebean.*;
-
-@Entity
-public class World extends Model {
-
-    @Id
-    public Long id;
-
-    @Column(name = "randomNumber")
-    public Long randomNumber;
-
-    public static Finder<Long, World> find = new Finder<Long, World>(
-            Long.class, World.class
-    );
-
-}

+ 0 - 11
frameworks/Java/play2-java/play2-java-ebean/build.sbt

@@ -1,11 +0,0 @@
-name := "play2-java-ebean"
-
-version := "1.0-SNAPSHOT"
-
-libraryDependencies ++= Seq(
-  javaJdbc,
-  javaEbean,
-  "mysql" % "mysql-connector-java" % "5.1.32"
-  )
-
-playJavaSettings

+ 0 - 10
frameworks/Java/play2-java/play2-java-ebean/conf/routes

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

+ 0 - 1
frameworks/Java/play2-java/play2-java-ebean/project/build.properties

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

+ 0 - 9
frameworks/Java/play2-java/play2-java-ebean/source_code

@@ -1,9 +0,0 @@
-./play-java/app/
-./play-java/app/controllers
-./play-java/app/controllers/Application.java
-./play-java/app/utils
-./play-java/app/utils/Predicate.java
-./play-java/app/utils/PredicatedAction.java
-./play-java/app/utils/Predicated.java
-./play-java/app/models
-./play-java/app/models/World.java

+ 30 - 0
frameworks/Java/play2-java/play2-java-jpa-bonecp/.gitignore

@@ -0,0 +1,30 @@
+logs
+project/project
+project/target
+target
+tmp
+.history
+dist
+
+# 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
+

+ 35 - 0
frameworks/Java/play2-java/play2-java-jpa-bonecp/README.md

@@ -0,0 +1,35 @@
+#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 controller](app/controllers/Application.java)
+
+### Data-Store/Database Mapping Test
+
+* [Database test controller](app/controllers/Application.java)
+* [Database World test model](app/models/World.java)
+* [Database Fortune test model](app/models/Fortune.java)
+
+### Plain Text Test
+
+* [Plain text test controller](app/controllers/Application.java)
+
+## Infrastructure Software Versions
+The tests were run with:
+
+* [Java OpenJDK 1.7](http://openjdk.java.net/)
+* [Play 2.3.6](http://http://www.playframework.com/)
+
+## Test URLs
+### JSON Encoding Test
+
+* http://localhost/json
+
+### Data-Store/Database Mapping Test
+
+* http://localhost/db
+* http://localhost/queries?queries=10
+* http://localhost/fortunes
+* http://localhost/update?queries=10

+ 66 - 10
frameworks/Java/play2-java/play2-java-jpa/app/controllers/Application.java → frameworks/Java/play2-java/play2-java-jpa-bonecp/app/controllers/Application.java

@@ -3,6 +3,7 @@ package controllers;
 import akka.dispatch.ExecutionContexts;
 import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
+import models.Fortune;
 import models.World;
 import play.Play;
 import play.core.NamedThreadFactory;
@@ -10,21 +11,21 @@ import play.libs.F;
 import play.libs.Json;
 import play.mvc.Controller;
 import play.mvc.Result;
+import play.mvc.With;
 import scala.concurrent.ExecutionContext;
+import utils.Headers;
 import utils.Predicate;
 import utils.Predicated;
 
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Random;
+import java.util.*;
 import java.util.concurrent.LinkedBlockingQueue;
 import java.util.concurrent.ThreadLocalRandom;
 import java.util.concurrent.ThreadPoolExecutor;
 import java.util.concurrent.TimeUnit;
 
+@With(Headers.class)
 public class Application extends Controller {
 
-    private static final int MAX_QUERIES_PER_REQUEST = 20;
     private static final int TEST_DATABASE_ROWS = 10000;
     private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
 
@@ -60,6 +61,7 @@ public class Application extends Controller {
     @Predicated(predicate = IsDbAvailable.class, failed = SERVICE_UNAVAILABLE)
     public static F.Promise<Result> db() {
         return getRandomWorlds(1).map(new F.Function<List<World>, Result>() {
+
             @Override
             public Result apply(List<World> worlds) {
                 return ok(Json.toJson(worlds.get(0)));
@@ -69,6 +71,48 @@ public class Application extends Controller {
 
     @Predicated(predicate = IsDbAvailable.class, failed = SERVICE_UNAVAILABLE)
     public static F.Promise<Result> queries(final String queryCountString) {
+        return getRandomWorlds(queryCount(queryCountString)).map(new F.Function<List<World>, Result>() {
+
+            @Override
+            public Result apply(List<World> worlds) {
+                return ok(Json.toJson(worlds));
+            }
+        });
+    }
+
+    @Predicated(predicate = IsDbAvailable.class, failed = SERVICE_UNAVAILABLE)
+    public static F.Promise<Result> fortunes() {
+        return F.Promise.promise(new F.Function0<Result>() {
+
+            @Override
+            public Result apply() throws Throwable {
+                List<Fortune> fortunes = Fortune.findAll();
+                fortunes.add(new Fortune("Additional fortune added at request time."));
+                Collections.sort(fortunes, new Comparator<Fortune>() {
+
+                    @Override
+                    public int compare(Fortune f1, Fortune f2) {
+                        return f1.message.compareTo(f2.message);
+                    }
+                });
+
+                return ok(views.html.fortunes.render(fortunes));
+            }
+        }, dbEc);
+    }
+
+    @Predicated(predicate = IsDbAvailable.class, failed = SERVICE_UNAVAILABLE)
+    public static F.Promise<Result> update(final String queryCountString) {
+        return getRandomWorlds(queryCount(queryCountString)).flatMap(new F.Function<List<World>, F.Promise<Result>>() {
+
+            @Override
+            public F.Promise<Result> apply(List<World> worlds) throws Throwable {
+                return updateWorlds(worlds);
+            }
+        });
+    }
+
+    private static int queryCount(String queryCountString) {
         int queryCount;
         try {
             queryCount = Integer.parseInt(queryCountString, 10);
@@ -81,16 +125,12 @@ public class Application extends Controller {
             queryCount = 500;
         }
 
-        return getRandomWorlds(queryCount).map(new F.Function<List<World>, Result>() {
-            @Override
-            public Result apply(List<World> worlds) {
-                return ok(Json.toJson(worlds));
-            }
-        });
+        return queryCount;
     }
 
     private static F.Promise<List<World>> getRandomWorlds(final int n) {
         return F.Promise.promise(new F.Function0<List<World>>() {
+
             @Override
             public List<World> apply() throws Throwable {
                 Random random = ThreadLocalRandom.current();
@@ -105,4 +145,20 @@ public class Application extends Controller {
         }, dbEc);
     }
 
+    private static F.Promise<Result> updateWorlds(final List<World> worlds) {
+        return F.Promise.promise(new F.Function0<Result>() {
+
+            @Override
+            public Result apply() throws Throwable {
+                Random random = ThreadLocalRandom.current();
+                for (World world : worlds) {
+                    world.randomNumber = (long) (random.nextInt(10000) + 1);
+                }
+
+                List<World> updatedWorlds = World.save(worlds);
+                return ok(Json.toJson(updatedWorlds));
+            }
+        }, dbEc);
+    }
+
 }

+ 34 - 0
frameworks/Java/play2-java/play2-java-jpa-bonecp/app/models/Fortune.java

@@ -0,0 +1,34 @@
+package models;
+
+import play.db.jpa.JPA;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.criteria.CriteriaQuery;
+import java.util.List;
+
+@Entity
+public class Fortune {
+
+    @Id
+    public Long id = 0L;
+
+    public String message;
+
+    public Fortune() {
+    }
+
+    public Fortune(String message) {
+        this.message = message;
+    }
+
+    public static List<Fortune> findAll() throws Throwable {
+        return JPA.withTransaction("default", true, new play.libs.F.Function0<List<Fortune>>() {
+            public List<Fortune> apply() {
+                CriteriaQuery<Fortune> criteria = JPA.em().getCriteriaBuilder().createQuery(Fortune.class);
+                criteria.select(criteria.from(Fortune.class));
+                return JPA.em().createQuery(criteria).getResultList();
+            }
+        });
+    }
+}

+ 38 - 0
frameworks/Java/play2-java/play2-java-jpa-bonecp/app/models/World.java

@@ -0,0 +1,38 @@
+package models;
+
+import play.db.jpa.JPA;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import java.util.List;
+
+@Entity
+public class World {
+
+    @Id
+    public Long id;
+
+    @Column(name = "randomNumber")
+    public Long randomNumber;
+
+    public static World findById(final Long id) throws Throwable {
+        return JPA.withTransaction("default", true, new play.libs.F.Function0<World>() {
+            public World apply() {
+                return JPA.em().find(World.class, id);
+            }
+        });
+    }
+
+    public static List<World> save(final List<World> worlds) throws Throwable {
+        for (final World world : worlds) {
+            JPA.withTransaction("default", false, new play.libs.F.Function0<World>() {
+                public World apply() {
+                    return JPA.em().merge(world);
+                }
+            });
+        }
+
+        return worlds;
+    }
+}

+ 21 - 0
frameworks/Java/play2-java/play2-java-jpa-bonecp/app/utils/Headers.java

@@ -0,0 +1,21 @@
+package utils;
+
+import org.joda.time.DateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+import play.libs.F;
+import play.mvc.Action;
+import play.mvc.Http;
+import play.mvc.Result;
+
+public class Headers extends Action.Simple {
+
+    private static final DateTimeFormatter RFC_1123_DATE_TIME = DateTimeFormat.forPattern("EEE, dd MMM yyyy HH:mm:ss 'GMT'").withZoneUTC();
+
+    @Override
+    public F.Promise<Result> call(Http.Context context) throws Throwable {
+        context.response().setHeader("Server", "play2-java-jpa");
+        context.response().setHeader("Date", RFC_1123_DATE_TIME.print(new DateTime()));
+        return delegate.call(context);
+    }
+}

+ 8 - 0
frameworks/Java/play2-java/play2-java-jpa-bonecp/app/utils/Predicate.java

@@ -0,0 +1,8 @@
+package utils;
+
+/**
+ * Predicates for PredicatedActions.
+ */
+public interface Predicate {
+    boolean condition();
+}

+ 26 - 0
frameworks/Java/play2-java/play2-java-jpa-bonecp/app/utils/Predicated.java

@@ -0,0 +1,26 @@
+package utils;
+
+import play.mvc.With;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Declares a composing action that will check for a condition before deciding on whether to proceed with the request.
+ */
+@With(PredicatedAction.class)
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Predicated {
+    /**
+     * The condition.
+     */
+    Class<? extends Predicate> predicate();
+
+    /**
+     * The http status code to return if the condition fails.
+     */
+    int failed();
+}

+ 23 - 0
frameworks/Java/play2-java/play2-java-jpa-bonecp/app/utils/PredicatedAction.java

@@ -0,0 +1,23 @@
+package utils;
+
+/**
+ * A predicated action is one where a condition must be satisfied in order to proceed with the request. If the
+ * condition is not satisfied then a supplied status result is yielded.
+ */
+
+import play.libs.F;
+import play.mvc.Action;
+import play.mvc.Http;
+import play.mvc.Result;
+
+public class PredicatedAction extends Action<Predicated> {
+    @Override
+    public F.Promise<Result> call(final Http.Context ctx) throws Throwable {
+        final Predicate p = configuration.predicate().newInstance();
+        if (p.condition()) {
+            return delegate.call(ctx);
+        } else {
+            return F.Promise.<Result>pure(status(configuration.failed()));
+        }
+    }
+}

+ 16 - 0
frameworks/Java/play2-java/play2-java-jpa-bonecp/app/views/fortunes.scala.html

@@ -0,0 +1,16 @@
+@(fortunes: List[Fortune])
+
+@main() {
+    <table>
+        <tr>
+            <th>id</th>
+            <th>message</th>
+        </tr>
+        @for(fortune <- fortunes) {
+        <tr>
+            <td>@fortune.id</td>
+            <td>@fortune.message</td>
+        </tr>
+        }
+    </table>
+}

+ 12 - 0
frameworks/Java/play2-java/play2-java-jpa-bonecp/app/views/main.scala.html

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

+ 2 - 2
frameworks/Java/play2-java/play2-java-jpa/build.sbt → frameworks/Java/play2-java/play2-java-jpa-bonecp/build.sbt

@@ -2,14 +2,14 @@ name := "play2-java-jpa"
 
 version := "1.0-SNAPSHOT"
 
-scalaVersion := "2.11.2"
+scalaVersion := "2.11.4"
 
 lazy val root = (project in file(".")).enablePlugins(PlayJava)
 
 libraryDependencies ++= Seq(
   javaJdbc,
   javaJpa,
-  "mysql" % "mysql-connector-java" % "5.1.32",
+  "mysql" % "mysql-connector-java" % "5.1.33",
   "org.hibernate" % "hibernate-entitymanager" % "4.3.6.Final"
   )
 

+ 0 - 0
frameworks/Java/play2-java/play2-java-jpa/conf/META-INF/persistence.xml → frameworks/Java/play2-java/play2-java-jpa-bonecp/conf/META-INF/persistence.xml


+ 1 - 1
frameworks/Java/play2-java/play2-java-jpa/conf/application.conf → frameworks/Java/play2-java/play2-java-jpa-bonecp/conf/application.conf

@@ -65,7 +65,7 @@ db.default.minConnectionsPerPartition=64
 # 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.root=INFO
 
 # Logger used by the framework:
 logger.play=ERROR

+ 2 - 1
frameworks/Java/play2-java/play2-java-jpa/conf/routes → frameworks/Java/play2-java/play2-java-jpa-bonecp/conf/routes

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

+ 0 - 0
frameworks/Java/play2-java/play2-java-jpa/project/build.properties → frameworks/Java/play2-java/play2-java-jpa-bonecp/project/build.properties


+ 8 - 0
frameworks/Java/play2-java/play2-java-jpa-bonecp/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("com.typesafe.play" % "sbt-plugin" % "2.3.6")

+ 11 - 0
frameworks/Java/play2-java/play2-java-jpa-bonecp/source_code

@@ -0,0 +1,11 @@
+./play-java-jpa/app/
+./play-java-jpa/app/controllers
+./play-java-jpa/app/controllers/Application.java
+./play-java-jpa/app/utils
+./play-java-jpa/app/utils/Headers.java
+./play-java-jpa/app/utils/Predicate.java
+./play-java-jpa/app/utils/PredicatedAction.java
+./play-java-jpa/app/utils/Predicated.java
+./play-java-jpa/app/models
+./play-java-jpa/app/models/World.java
+./play-java-jpa/app/models/Fortune.java

+ 29 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/.gitignore

@@ -0,0 +1,29 @@
+logs
+project/project
+project/target
+target
+tmp
+.history
+dist
+
+# 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

+ 36 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/README.md

@@ -0,0 +1,36 @@
+#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 controller](app/controllers/Application.java)
+
+### Data-Store/Database Mapping Test
+
+* [Database test controller](app/controllers/Application.java)
+* [Database World test model](app/models/World.java)
+* [Database Fortune test model](app/models/Fortune.java)
+
+### Plain Text Test
+
+* [Plain text test controller](app/controllers/Application.java)
+
+## Infrastructure Software Versions
+The tests were run with:
+
+* [Java OpenJDK 1.7](http://openjdk.java.net/)
+* [Play 2.3.6](http://http://www.playframework.com/)
+
+## Test URLs
+### JSON Encoding Test
+
+* http://localhost/json
+
+### Data-Store/Database Mapping Test
+
+* http://localhost/db
+* http://localhost/queries?queries=10
+* http://localhost/fortunes
+* http://localhost/update?queries=10
+

+ 165 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/controllers/Application.java

@@ -0,0 +1,165 @@
+package controllers;
+
+import akka.dispatch.ExecutionContexts;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import models.Fortune;
+import models.World;
+import play.Play;
+import play.core.NamedThreadFactory;
+import play.libs.F;
+import play.libs.Json;
+import play.mvc.Controller;
+import play.mvc.Result;
+import play.mvc.With;
+import scala.concurrent.ExecutionContext;
+import utils.Headers;
+import utils.Predicate;
+import utils.Predicated;
+
+import java.util.*;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.ThreadLocalRandom;
+import java.util.concurrent.ThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+@With(Headers.class)
+public class Application extends Controller {
+
+    private static final int TEST_DATABASE_ROWS = 10000;
+    private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
+
+    private static final int partitionCount = Play.application().configuration().getInt("db.default.partitionCount");
+    private static final int maxConnections =
+            partitionCount * Play.application().configuration().getInt("db.default.maxConnectionsPerPartition");
+    private static final int minConnections =
+            partitionCount * Play.application().configuration().getInt("db.default.minConnectionsPerPartition");
+
+    private static final ThreadPoolExecutor tpe = new ThreadPoolExecutor(minConnections, maxConnections,
+            0L, TimeUnit.MILLISECONDS,
+            new LinkedBlockingQueue<Runnable>(),
+            new NamedThreadFactory("dbEc"));
+    private static final ExecutionContext dbEc = ExecutionContexts.fromExecutorService(tpe);
+
+    public static Result json() {
+        final ObjectNode result = OBJECT_MAPPER.createObjectNode();
+        result.put("message", "Hello World!");
+        return ok(result);
+    }
+
+    // If the thread-pool used by the database grows too large then our server
+    // is probably struggling, and we should start dropping requests. Set
+    // the max size of our queue something above the number of concurrent
+    // connections that we need to handle.
+    public static class IsDbAvailable implements Predicate {
+
+        @Override
+        public boolean condition() {
+            return tpe.getQueue().size() <= 1024;
+        }
+    }
+
+    @Predicated(predicate = IsDbAvailable.class, failed = SERVICE_UNAVAILABLE)
+    public static F.Promise<Result> db() {
+        return getRandomWorlds(1).map(new F.Function<List<World>, Result>() {
+
+            @Override
+            public Result apply(List<World> worlds) {
+                return ok(Json.toJson(worlds.get(0)));
+            }
+        });
+    }
+
+    @Predicated(predicate = IsDbAvailable.class, failed = SERVICE_UNAVAILABLE)
+    public static F.Promise<Result> queries(final String queryCountString) {
+        return getRandomWorlds(queryCount(queryCountString)).map(new F.Function<List<World>, Result>() {
+
+            @Override
+            public Result apply(List<World> worlds) {
+                return ok(Json.toJson(worlds));
+            }
+        });
+    }
+
+    @Predicated(predicate = IsDbAvailable.class, failed = SERVICE_UNAVAILABLE)
+    public static F.Promise<Result> fortunes() {
+        return F.Promise.promise(new F.Function0<Result>() {
+
+            @Override
+            public Result apply() throws Throwable {
+                List<Fortune> fortunes = Fortune.findAll();
+                fortunes.add(new Fortune("Additional fortune added at request time."));
+                Collections.sort(fortunes, new Comparator<Fortune>() {
+
+                    @Override
+                    public int compare(Fortune f1, Fortune f2) {
+                        return f1.message.compareTo(f2.message);
+                    }
+                });
+
+                return ok(views.html.fortunes.render(fortunes));
+            }
+        }, dbEc);
+    }
+
+    @Predicated(predicate = IsDbAvailable.class, failed = SERVICE_UNAVAILABLE)
+    public static F.Promise<Result> update(final String queryCountString) {
+        return getRandomWorlds(queryCount(queryCountString)).flatMap(new F.Function<List<World>, F.Promise<Result>>() {
+
+            @Override
+            public F.Promise<Result> apply(List<World> worlds) throws Throwable {
+                return updateWorlds(worlds);
+            }
+        });
+    }
+
+    private static int queryCount(String queryCountString) {
+        int queryCount;
+        try {
+            queryCount = Integer.parseInt(queryCountString, 10);
+        } catch (NumberFormatException e) {
+            queryCount = 1;
+        }
+        if (queryCount < 1) {
+            queryCount = 1;
+        } else if (queryCount > 500) {
+            queryCount = 500;
+        }
+
+        return queryCount;
+    }
+
+    private static F.Promise<List<World>> getRandomWorlds(final int n) {
+        return F.Promise.promise(new F.Function0<List<World>>() {
+
+            @Override
+            public List<World> apply() throws Throwable {
+                Random random = ThreadLocalRandom.current();
+                List<World> worlds = new ArrayList<World>(n);
+                for (int i = 0; i < n; ++i) {
+                    long randomId = random.nextInt(TEST_DATABASE_ROWS) + 1;
+                    World world = World.findById(randomId);
+                    worlds.add(world);
+                }
+                return worlds;
+            }
+        }, dbEc);
+    }
+
+    private static F.Promise<Result> updateWorlds(final List<World> worlds) {
+        return F.Promise.promise(new F.Function0<Result>() {
+
+            @Override
+            public Result apply() throws Throwable {
+                Random random = ThreadLocalRandom.current();
+                for (World world : worlds) {
+                    world.randomNumber = (long) (random.nextInt(10000) + 1);
+                }
+
+                List<World> updatedWorlds = World.save(worlds);
+                return ok(Json.toJson(updatedWorlds));
+            }
+        }, dbEc);
+    }
+
+}

+ 34 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/models/Fortune.java

@@ -0,0 +1,34 @@
+package models;
+
+import play.db.jpa.JPA;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.criteria.CriteriaQuery;
+import java.util.List;
+
+@Entity
+public class Fortune {
+
+    @Id
+    public Long id = 0L;
+
+    public String message;
+
+    public Fortune() {
+    }
+
+    public Fortune(String message) {
+        this.message = message;
+    }
+
+    public static List<Fortune> findAll() throws Throwable {
+        return JPA.withTransaction("default", true, new play.libs.F.Function0<List<Fortune>>() {
+            public List<Fortune> apply() {
+                CriteriaQuery<Fortune> criteria = JPA.em().getCriteriaBuilder().createQuery(Fortune.class);
+                criteria.select(criteria.from(Fortune.class));
+                return JPA.em().createQuery(criteria).getResultList();
+            }
+        });
+    }
+}

+ 38 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/models/World.java

@@ -0,0 +1,38 @@
+package models;
+
+import play.db.jpa.JPA;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import java.util.List;
+
+@Entity
+public class World {
+
+    @Id
+    public Long id;
+
+    @Column(name = "randomNumber")
+    public Long randomNumber;
+
+    public static World findById(final Long id) throws Throwable {
+        return JPA.withTransaction("default", true, new play.libs.F.Function0<World>() {
+            public World apply() {
+                return JPA.em().find(World.class, id);
+            }
+        });
+    }
+
+    public static List<World> save(final List<World> worlds) throws Throwable {
+        for (final World world : worlds) {
+            JPA.withTransaction("default", false, new play.libs.F.Function0<World>() {
+                public World apply() {
+                    return JPA.em().merge(world);
+                }
+            });
+        }
+
+        return worlds;
+    }
+}

+ 21 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/utils/Headers.java

@@ -0,0 +1,21 @@
+package utils;
+
+import org.joda.time.DateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+import play.libs.F;
+import play.mvc.Action;
+import play.mvc.Http;
+import play.mvc.Result;
+
+public class Headers extends Action.Simple {
+
+    private static final DateTimeFormatter RFC_1123_DATE_TIME = DateTimeFormat.forPattern("EEE, dd MMM yyyy HH:mm:ss 'GMT'").withZoneUTC();
+
+    @Override
+    public F.Promise<Result> call(Http.Context context) throws Throwable {
+        context.response().setHeader("Server", "play2-java-jpa");
+        context.response().setHeader("Date", RFC_1123_DATE_TIME.print(new DateTime()));
+        return delegate.call(context);
+    }
+}

+ 8 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/utils/Predicate.java

@@ -0,0 +1,8 @@
+package utils;
+
+/**
+ * Predicates for PredicatedActions.
+ */
+public interface Predicate {
+    boolean condition();
+}

+ 26 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/utils/Predicated.java

@@ -0,0 +1,26 @@
+package utils;
+
+import play.mvc.With;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Declares a composing action that will check for a condition before deciding on whether to proceed with the request.
+ */
+@With(PredicatedAction.class)
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Predicated {
+    /**
+     * The condition.
+     */
+    Class<? extends Predicate> predicate();
+
+    /**
+     * The http status code to return if the condition fails.
+     */
+    int failed();
+}

+ 23 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/utils/PredicatedAction.java

@@ -0,0 +1,23 @@
+package utils;
+
+/**
+ * A predicated action is one where a condition must be satisfied in order to proceed with the request. If the
+ * condition is not satisfied then a supplied status result is yielded.
+ */
+
+import play.libs.F;
+import play.mvc.Action;
+import play.mvc.Http;
+import play.mvc.Result;
+
+public class PredicatedAction extends Action<Predicated> {
+    @Override
+    public F.Promise<Result> call(final Http.Context ctx) throws Throwable {
+        final Predicate p = configuration.predicate().newInstance();
+        if (p.condition()) {
+            return delegate.call(ctx);
+        } else {
+            return F.Promise.<Result>pure(status(configuration.failed()));
+        }
+    }
+}

+ 16 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/views/fortunes.scala.html

@@ -0,0 +1,16 @@
+@(fortunes: List[Fortune])
+
+@main() {
+    <table>
+        <tr>
+            <th>id</th>
+            <th>message</th>
+        </tr>
+        @for(fortune <- fortunes) {
+        <tr>
+            <td>@fortune.id</td>
+            <td>@fortune.message</td>
+        </tr>
+        }
+    </table>
+}

+ 12 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/app/views/main.scala.html

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

+ 17 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/build.sbt

@@ -0,0 +1,17 @@
+name := """play2-java-jpa-hikaricp"""
+
+version := "1.0-SNAPSHOT"
+
+lazy val root = (project in file(".")).enablePlugins(PlayJava)
+
+scalaVersion := "2.11.4"
+
+libraryDependencies ++= Seq(
+  javaJdbc,
+  javaJpa,
+  "mysql" % "mysql-connector-java" % "5.1.33",
+  "org.hibernate" % "hibernate-entitymanager" % "4.3.6.Final",
+  "com.edulify" %% "play-hikaricp" % "1.5.0"
+)
+
+resolvers += Resolver.url("Edulify Repository", url("http://edulify.github.io/modules/releases/"))(Resolver.ivyStylePatterns)

+ 14 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/conf/META-INF/persistence.xml

@@ -0,0 +1,14 @@
+<persistence xmlns="http://java.sun.com/xml/ns/persistence"
+             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd"
+             version="2.0">
+             
+    <persistence-unit name="defaultPersistenceUnit" transaction-type="RESOURCE_LOCAL">
+        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
+        <non-jta-data-source>DefaultDS</non-jta-data-source>
+        <properties>
+            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
+        </properties>
+    </persistence-unit>
+    
+</persistence>

+ 95 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/conf/application.conf

@@ -0,0 +1,95 @@
+# This is the main configuration file for the application.
+# ~~~~~
+
+# Secret key
+# ~~~~~
+# The secret key is used to secure cryptographics functions.
+#
+# 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.
+application.secret="`o0VB@vXStsF:ffwM5ZZ_r:GYkm96QM[nRUJLpEbI8hwLUdV;N<:UyUWI;lf/XP`"
+
+# The application languages
+# ~~~~~
+application.langs="en"
+
+# Global object class
+# ~~~~~
+# Define the Global object class for this application.
+# Default to Global in the root package.
+# application.global=Global
+
+# Router
+# ~~~~~
+# 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 `conf/my.application.Router`,
+# you may need to define a router file `my.application.routes`.
+# Default to Routes in the root package (and `conf/routes`)
+# application.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
+
+db.default.driver= com.mysql.jdbc.Driver
+db.default.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.default.user=benchmarkdbuser
+db.default.password=benchmarkdbpass
+db.default.jndiName=DefaultDS
+jpa.default=defaultPersistenceUnit
+
+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..
+#
+# This value maps to the maximumPoolSize for HickariCP (db.default.partitionCount * db.default.maxConnectionsPerPartition)
+db.default.maxConnectionsPerPartition=64
+
+# The number of initial connections, per partition.
+#
+# This maps to the minimumIdle connections for HikariCP (db.default.partitionCount * db.default.minConnectionsPerPartition)
+db.default.minConnectionsPerPartition=64
+
+dbplugin=disabled
+
+# Evolutions
+# ~~~~~
+# You can disable evolutions if needed
+evolutionplugin=disabled
+
+# Ebean configuration
+# ~~~~~
+# You can declare as many Ebean servers as you want.
+# By convention, the default server is named `default`
+#
+# ebean.default="models.*"
+
+# Logger
+# ~~~~~
+# You can also configure logback (http://logback.qos.ch/),
+# by providing an application-logger.xml file in the conf directory.
+
+# Root logger:
+logger.root=INFO
+
+# Logger used by the framework:
+logger.play=ERROR
+
+# Logger provided to your application:
+logger.application=ERROR
+

+ 1 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/conf/play.plugins

@@ -0,0 +1 @@
+200:com.edulify.play.hikaricp.HikariCPPlugin

+ 12 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/conf/routes

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

+ 1 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/project/build.properties

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

+ 4 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/project/plugins.sbt

@@ -0,0 +1,4 @@
+resolvers += "Typesafe repository" at "http://repo.typesafe.com/typesafe/releases/"
+
+// The Play plugin
+addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.3.6")

+ 11 - 0
frameworks/Java/play2-java/play2-java-jpa-hikaricp/source_code

@@ -0,0 +1,11 @@
+./play-java-jpa/app/
+./play-java-jpa/app/controllers
+./play-java-jpa/app/controllers/Application.java
+./play-java-jpa/app/utils
+./play-java-jpa/app/utils/Headers.java
+./play-java-jpa/app/utils/Predicate.java
+./play-java-jpa/app/utils/PredicatedAction.java
+./play-java-jpa/app/utils/Predicated.java
+./play-java-jpa/app/models
+./play-java-jpa/app/models/World.java
+./play-java-jpa/app/models/Fortune.java

+ 0 - 28
frameworks/Java/play2-java/play2-java-jpa/README.md

@@ -1,28 +0,0 @@
-#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.java)
-
-### Data-Store/Database Mapping Test
-
-* [Database test controller](app/controllers/Application.java)
-* [Database test model](app/models/World.java)
-
-## Infrastructure Software Versions
-The tests were run with:
-
-* [Java OpenJDK 1.7](http://openjdk.java.net/)
-* [Play 2.3.3](http://http://www.playframework.com/)
-
-## Test URLs
-### JSON Encoding Test
-
-http://localhost/json
-
-### Data-Store/Database Mapping Test
-
-http://localhost/db
-http://localhost/queries?queries=10

+ 34 - 0
frameworks/Java/play2-java/play2-java-jpa/app/models/Fortune.java

@@ -0,0 +1,34 @@
+package models;
+
+import play.db.jpa.JPA;
+
+import javax.persistence.Entity;
+import javax.persistence.Id;
+import javax.persistence.criteria.CriteriaQuery;
+import java.util.List;
+
+@Entity
+public class Fortune {
+
+    @Id
+    public Long id = 0L;
+
+    public String message;
+
+    public Fortune() {
+    }
+
+    public Fortune(String message) {
+        this.message = message;
+    }
+
+    public static List<Fortune> findAll() throws Throwable {
+        return JPA.withTransaction("default", true, new play.libs.F.Function0<List<Fortune>>() {
+            public List<Fortune> apply() {
+                CriteriaQuery<Fortune> criteria = JPA.em().getCriteriaBuilder().createQuery(Fortune.class);
+                criteria.select(criteria.from(Fortune.class));
+                return JPA.em().createQuery(criteria).getResultList();
+            }
+        });
+    }
+}

+ 0 - 23
frameworks/Java/play2-java/play2-java-jpa/app/models/World.java

@@ -1,23 +0,0 @@
-package models;
-
-import javax.persistence.*;
-
-import play.db.jpa.JPA;
-
-@Entity
-public class World {
-
-    @Id
-    public Long id;
-
-    @Column(name = "randomNumber")
-    public Long randomNumber;
-
-    public static World findById(final Long id) throws Throwable {
-      return JPA.withTransaction("default", true, new play.libs.F.Function0<World>() {
-        public World apply() {
-            return JPA.em().find(World.class, id);
-        }
-      });
-    }
-}

+ 21 - 0
frameworks/Java/play2-java/play2-java-jpa/app/utils/Headers.java

@@ -0,0 +1,21 @@
+package utils;
+
+import org.joda.time.DateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+import play.libs.F;
+import play.mvc.Action;
+import play.mvc.Http;
+import play.mvc.Result;
+
+public class Headers extends Action.Simple {
+
+    private static final DateTimeFormatter RFC_1123_DATE_TIME = DateTimeFormat.forPattern("EEE, dd MMM yyyy HH:mm:ss 'GMT'").withZoneUTC();
+
+    @Override
+    public F.Promise<Result> call(Http.Context context) throws Throwable {
+        context.response().setHeader("Server", "play2-java-jpa");
+        context.response().setHeader("Date", RFC_1123_DATE_TIME.print(new DateTime()));
+        return delegate.call(context);
+    }
+}

+ 16 - 0
frameworks/Java/play2-java/play2-java-jpa/app/views/fortunes.scala.html

@@ -0,0 +1,16 @@
+@(fortunes: List[Fortune])
+
+@main() {
+    <table>
+        <tr>
+            <th>id</th>
+            <th>message</th>
+        </tr>
+        @for(fortune <- fortunes) {
+        <tr>
+            <td>@fortune.id</td>
+            <td>@fortune.message</td>
+        </tr>
+        }
+    </table>
+}

+ 12 - 0
frameworks/Java/play2-java/play2-java-jpa/app/views/main.scala.html

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

+ 5 - 1
frameworks/Java/play2-java/play2-java/README.md

@@ -15,4 +15,8 @@ The tests were run with:
 ## Test URLs
 ### JSON Encoding Test
 
-http://localhost/json
+* http://localhost/json
+
+### Plain Text Test
+
+* http://localhost/plaintext

+ 7 - 0
frameworks/Java/play2-java/play2-java/app/controllers/Application.java

@@ -4,7 +4,10 @@ import com.fasterxml.jackson.databind.ObjectMapper;
 import com.fasterxml.jackson.databind.node.ObjectNode;
 import play.mvc.Controller;
 import play.mvc.Result;
+import play.mvc.With;
+import utils.Headers;
 
+@With(Headers.class)
 public class Application extends Controller {
 
     //http://stackoverflow.com/questions/3907929/should-i-make-jacksons-objectmapper-as-static-final
@@ -16,4 +19,8 @@ public class Application extends Controller {
         return ok(result);
     }
 
+    public static Result plainText() {
+        return ok("Hello, World!");
+    }
+
 }

+ 21 - 0
frameworks/Java/play2-java/play2-java/app/utils/Headers.java

@@ -0,0 +1,21 @@
+package utils;
+
+import org.joda.time.DateTime;
+import org.joda.time.format.DateTimeFormat;
+import org.joda.time.format.DateTimeFormatter;
+import play.libs.F;
+import play.mvc.Action;
+import play.mvc.Http;
+import play.mvc.Result;
+
+public class Headers extends Action.Simple {
+
+    private static final DateTimeFormatter RFC_1123_DATE_TIME = DateTimeFormat.forPattern("EEE, dd MMM yyyy HH:mm:ss 'GMT'").withZoneUTC();
+
+    @Override
+    public F.Promise<Result> call(Http.Context context) throws Throwable {
+        context.response().setHeader("Server", "play2-java-jpa");
+        context.response().setHeader("Date", RFC_1123_DATE_TIME.print(new DateTime()));
+        return delegate.call(context);
+    }
+}

Some files were not shown because too many files changed in this diff